From 68b757f5de3be2bfe56fc75a9167cc91376ba881 Mon Sep 17 00:00:00 2001 From: wyun Date: Fri, 12 May 2017 18:51:21 -0400 Subject: drm/sde: add 4k hdmi support When HDMI resolution is bigger than 2560 pixel of width, driver needs to use two hardware pipes. Use virtual plane to support this feature. Change-Id: I19e3bb32aa2a16c83393b0e3c6bec3db03827eca Signed-off-by: wyun --- drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c | 7 +- drivers/gpu/drm/msm/hdmi/hdmi.xml.h | 18 +- drivers/gpu/drm/msm/sde/sde_crtc.c | 45 +- drivers/gpu/drm/msm/sde/sde_hw_catalog.c | 123 ++- drivers/gpu/drm/msm/sde/sde_hw_catalog.h | 32 + drivers/gpu/drm/msm/sde/sde_kms.c | 75 +- drivers/gpu/drm/msm/sde/sde_plane.c | 1257 ++++++++++++++++----------- drivers/gpu/drm/msm/sde/sde_plane.h | 6 +- drivers/gpu/drm/msm/sde/sde_rm.c | 42 +- drivers/gpu/drm/msm/sde/sde_rm.h | 14 +- 10 files changed, 1072 insertions(+), 547 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index c377f3759e67..020b1d84ae7a 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -48,6 +48,9 @@ static LIST_HEAD(sde_hdmi_list); #define HDMI_SCDC_ERR_DET_2_H 0x55 #define HDMI_SCDC_ERR_DET_CHECKSUM 0x56 +#define HDMI_DISPLAY_MAX_WIDTH 4096 +#define HDMI_DISPLAY_MAX_HEIGHT 2160 + static const struct of_device_id sde_hdmi_dt_match[] = { {.compatible = "qcom,hdmi-display"}, {} @@ -1428,8 +1431,8 @@ int sde_hdmi_get_info(struct msm_display_info *info, MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_VID_MODE; } info->is_connected = hdmi_display->connected; - info->max_width = 4096; - info->max_height = 2160; + info->max_width = HDMI_DISPLAY_MAX_WIDTH; + info->max_height = HDMI_DISPLAY_MAX_HEIGHT; info->compression = MSM_DISPLAY_COMPRESS_NONE; mutex_unlock(&hdmi_display->display_lock); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index 0956617442af..ea485a2ec2cd 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -466,13 +466,13 @@ static inline uint32_t HDMI_DDC_REF_REFTIMER(uint32_t val) #define REG_HDMI_CEC_RD_FILTER 0x000002b0 #define REG_HDMI_ACTIVE_HSYNC 0x000002b4 -#define HDMI_ACTIVE_HSYNC_START__MASK 0x00000fff +#define HDMI_ACTIVE_HSYNC_START__MASK 0x00001fff #define HDMI_ACTIVE_HSYNC_START__SHIFT 0 static inline uint32_t HDMI_ACTIVE_HSYNC_START(uint32_t val) { return ((val) << HDMI_ACTIVE_HSYNC_START__SHIFT) & HDMI_ACTIVE_HSYNC_START__MASK; } -#define HDMI_ACTIVE_HSYNC_END__MASK 0x0fff0000 +#define HDMI_ACTIVE_HSYNC_END__MASK 0x1fff0000 #define HDMI_ACTIVE_HSYNC_END__SHIFT 16 static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val) { @@ -480,13 +480,13 @@ static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val) } #define REG_HDMI_ACTIVE_VSYNC 0x000002b8 -#define HDMI_ACTIVE_VSYNC_START__MASK 0x00000fff +#define HDMI_ACTIVE_VSYNC_START__MASK 0x00001fff #define HDMI_ACTIVE_VSYNC_START__SHIFT 0 static inline uint32_t HDMI_ACTIVE_VSYNC_START(uint32_t val) { return ((val) << HDMI_ACTIVE_VSYNC_START__SHIFT) & HDMI_ACTIVE_VSYNC_START__MASK; } -#define HDMI_ACTIVE_VSYNC_END__MASK 0x0fff0000 +#define HDMI_ACTIVE_VSYNC_END__MASK 0x1fff0000 #define HDMI_ACTIVE_VSYNC_END__SHIFT 16 static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val) { @@ -494,13 +494,13 @@ static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val) } #define REG_HDMI_VSYNC_ACTIVE_F2 0x000002bc -#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00000fff +#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00001fff #define HDMI_VSYNC_ACTIVE_F2_START__SHIFT 0 static inline uint32_t HDMI_VSYNC_ACTIVE_F2_START(uint32_t val) { return ((val) << HDMI_VSYNC_ACTIVE_F2_START__SHIFT) & HDMI_VSYNC_ACTIVE_F2_START__MASK; } -#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x0fff0000 +#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x1fff0000 #define HDMI_VSYNC_ACTIVE_F2_END__SHIFT 16 static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val) { @@ -508,13 +508,13 @@ static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val) } #define REG_HDMI_TOTAL 0x000002c0 -#define HDMI_TOTAL_H_TOTAL__MASK 0x00000fff +#define HDMI_TOTAL_H_TOTAL__MASK 0x00001fff #define HDMI_TOTAL_H_TOTAL__SHIFT 0 static inline uint32_t HDMI_TOTAL_H_TOTAL(uint32_t val) { return ((val) << HDMI_TOTAL_H_TOTAL__SHIFT) & HDMI_TOTAL_H_TOTAL__MASK; } -#define HDMI_TOTAL_V_TOTAL__MASK 0x0fff0000 +#define HDMI_TOTAL_V_TOTAL__MASK 0x1fff0000 #define HDMI_TOTAL_V_TOTAL__SHIFT 16 static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val) { @@ -522,7 +522,7 @@ static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val) } #define REG_HDMI_VSYNC_TOTAL_F2 0x000002c4 -#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00000fff +#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00001fff #define HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT 0 static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val) { diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index adfe9e54e6f4..4fd20f5c2d94 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -57,6 +57,7 @@ static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) { struct msm_drm_private *priv = crtc->dev->dev_private; + return to_sde_kms(priv->kms); } @@ -183,9 +184,6 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, pstate = to_sde_plane_state(plane->state); - flush_mask = ctl->ops.get_bitmask_sspp(ctl, - sde_plane_pipe(plane)); - /* always stage plane on either left or right lm */ if (plane->state->crtc_x >= crtc_split_width) { lm_idx = RIGHT_MIXER; @@ -195,20 +193,36 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, idx = left_crtc_zpos_cnt[pstate->stage]++; } + /* + * program each mixer with two hw pipes in dual mixer mode, + */ + if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) { + stage_cfg->stage[LEFT_MIXER][pstate->stage][1] = + sde_plane_pipe(plane, 1); + + flush_mask = ctl->ops.get_bitmask_sspp(ctl, + sde_plane_pipe(plane, 1)); + } + + flush_mask |= ctl->ops.get_bitmask_sspp(ctl, + sde_plane_pipe(plane, lm_idx ? 1 : 0)); + /* stage plane on right LM if it crosses the boundary */ lm_right = (lm_idx == LEFT_MIXER) && (plane->state->crtc_x + plane->state->crtc_w > crtc_split_width); stage_cfg->stage[lm_idx][pstate->stage][idx] = - sde_plane_pipe(plane); + sde_plane_pipe(plane, lm_idx ? 1 : 0); + mixer[lm_idx].flush_mask |= flush_mask; SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n", crtc->base.id, pstate->stage, plane->base.id, - sde_plane_pipe(plane) - SSPP_VIG0, + sde_plane_pipe(plane, + lm_idx ? 1 : 0) - SSPP_VIG0, plane->state->fb ? plane->state->fb->base.id : -1); @@ -230,8 +244,19 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, if (lm_right) { idx = right_crtc_zpos_cnt[pstate->stage]++; - stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] = - sde_plane_pipe(plane); + + /* + * program each mixer with two hw pipes + in dual mixer mode, + */ + if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) { + stage_cfg->stage[RIGHT_MIXER][pstate->stage][1] + = sde_plane_pipe(plane, 0); + } + + stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] + = sde_plane_pipe(plane, 1); + mixer[RIGHT_MIXER].flush_mask |= flush_mask; /* blend config update */ @@ -1256,7 +1281,8 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en) return 0; } -void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file) +void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, + struct drm_file *file) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); @@ -1665,7 +1691,8 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc, #endif /* initialize crtc */ -struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) +struct drm_crtc *sde_crtc_init(struct drm_device *dev, + struct drm_plane *plane) { struct drm_crtc *crtc = NULL; struct sde_crtc *sde_crtc = NULL; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 519288f0dda2..17b678cfca46 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -14,6 +14,8 @@ #include #include +#include + #include "sde_hw_mdss.h" #include "sde_hw_catalog.h" #include "sde_hw_catalog_format.h" @@ -715,6 +717,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->pcc_blk.len = 0; set_bit(SDE_SSPP_PCC, &sspp->features); } + snprintf(sspp->name, sizeof(sspp->name), "vig%d", *vig_count-1); } static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, @@ -753,6 +756,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->pcc_blk.len = 0; set_bit(SDE_SSPP_PCC, &sspp->features); } + snprintf(sspp->name, sizeof(sspp->name), "rgb%d", *rgb_count-1); } static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, @@ -766,6 +770,7 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count; sblk->format_list = plane_formats; (*cursor_count)++; + snprintf(sspp->name, sizeof(sspp->name), "cursor%d", *cursor_count-1); } static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, @@ -779,6 +784,7 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, sblk->format_list = plane_formats; set_bit(SDE_SSPP_QOS, &sspp->features); (*dma_count)++; + snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1); } static int sde_sspp_parse_dt(struct device_node *np, @@ -1200,7 +1206,8 @@ end: return rc; } -static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) +static int sde_wb_parse_dt(struct device_node *np, + struct sde_mdss_cfg *sde_cfg) { int rc, prop_count[WB_PROP_MAX], i, j; struct sde_prop_value *prop_value = NULL; @@ -1686,7 +1693,8 @@ end: return rc; } -static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) +static int sde_pp_parse_dt(struct device_node *np, + struct sde_mdss_cfg *sde_cfg) { int rc, prop_count[PP_PROP_MAX], i; struct sde_prop_value *prop_value = NULL; @@ -1760,6 +1768,94 @@ end: return rc; } +static inline u32 _sde_parse_sspp_id(struct sde_mdss_cfg *cfg, + const char *name) +{ + int i; + + for (i = 0; i < cfg->sspp_count; i++) { + if (!strcmp(cfg->sspp[i].name, name)) + return cfg->sspp[i].id; + } + + return SSPP_NONE; +} + +static int _sde_vp_parse_dt(struct device_node *np, + struct sde_mdss_cfg *cfg) +{ + int rc = 0, i = 0; + struct device_node *node = NULL; + struct device_node *root_node = NULL; + struct sde_vp_cfg *vp; + struct sde_vp_sub_blks *vp_sub, *vp_sub_next; + struct property *prop; + const char *cname; + + root_node = of_get_child_by_name(np, "qcom,sde-plane-id-map"); + if (!root_node) { + root_node = of_parse_phandle(np, "qcom,sde-plane-id-map", 0); + if (!root_node) { + SDE_ERROR("No entry present for qcom,sde-plane-id-map"); + rc = -EINVAL; + goto end; + } + } + + for_each_child_of_node(root_node, node) { + if (i >= MAX_BLOCKS) { + SDE_ERROR("num of nodes(%d) is bigger than max(%d)\n", + i, MAX_BLOCKS); + rc = -EINVAL; + goto end; + } + cfg->vp_count++; + vp = &(cfg->vp[i]); + vp->id = i; + rc = of_property_read_string(node, "qcom,display-type", + &(vp->display_type)); + if (rc) { + SDE_ERROR("failed to read display-type, rc = %d\n", rc); + goto end; + } + + rc = of_property_read_string(node, "qcom,plane-type", + &(vp->plane_type)); + if (rc) { + SDE_ERROR("failed to read plane-type, rc = %d\n", rc); + goto end; + } + + INIT_LIST_HEAD(&vp->sub_blks); + of_property_for_each_string(node, "qcom,plane-name", + prop, cname) { + vp_sub = kzalloc(sizeof(*vp_sub), GFP_KERNEL); + if (!vp_sub) { + rc = -ENOMEM; + goto end; + } + vp_sub->sspp_id = _sde_parse_sspp_id(cfg, cname); + list_add_tail(&vp_sub->pipeid_list, &vp->sub_blks); + } + i++; + } + +end: + if (rc && cfg->vp_count) { + vp = &(cfg->vp[i]); + for (i = 0; i < cfg->vp_count; i++) { + list_for_each_entry_safe(vp_sub, vp_sub_next, + &vp->sub_blks, pipeid_list) { + list_del(&vp_sub->pipeid_list); + kfree(vp_sub); + } + } + memset(&(cfg->vp[0]), 0, sizeof(cfg->vp)); + cfg->vp_count = 0; + } + return rc; +} + static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) { int rc, len, prop_count[SDE_PROP_MAX]; @@ -1851,7 +1947,8 @@ end: return rc; } -static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) +static int sde_perf_parse_dt(struct device_node *np, + struct sde_mdss_cfg *cfg) { int rc, len, prop_count[PERF_PROP_MAX]; struct sde_prop_value *prop_value = NULL; @@ -1891,7 +1988,8 @@ end: return rc; } -static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) +static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, + uint32_t hw_rev) { switch (hw_rev) { case SDE_HW_VER_170: @@ -1909,6 +2007,7 @@ static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) { int i; + struct sde_vp_sub_blks *vp_sub, *vp_sub_next; if (!sde_cfg) return; @@ -1932,13 +2031,23 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) kfree(sde_cfg->vbif[i].dynamic_ot_rd_tbl.cfg); kfree(sde_cfg->vbif[i].dynamic_ot_wr_tbl.cfg); } + + for (i = 0; i < sde_cfg->vp_count; i++) { + list_for_each_entry_safe(vp_sub, vp_sub_next, + &sde_cfg->vp[i].sub_blks, pipeid_list) { + list_del(&vp_sub->pipeid_list); + kfree(vp_sub); + } + } + kfree(sde_cfg); } /************************************************************* * hardware catalog init *************************************************************/ -struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev) +struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, + u32 hw_rev) { int rc; struct sde_mdss_cfg *sde_cfg; @@ -1996,6 +2105,10 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev) if (rc) goto end; + rc = _sde_vp_parse_dt(np, sde_cfg); + if (rc) + SDE_DEBUG("virtual plane is not supported.\n"); + sde_hardware_caps(sde_cfg, hw_rev); return sde_cfg; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index a8f9169aaf35..bca221d2a959 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -57,6 +57,8 @@ #define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16) #define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF) +#define SSPP_NAME_SIZE 12 + /** * MDP TOP BLOCK features * @SDE_MDP_PANIC_PER_PIPE Panic configuration needs to be be done per pipe @@ -455,12 +457,14 @@ struct sde_ctl_cfg { * @sblk: SSPP sub-blocks information * @xin_id: bus client identifier * @clk_ctrl clock control identifier + *@name source pipe name */ struct sde_sspp_cfg { SDE_HW_BLK_INFO; const struct sde_sspp_sub_blks *sblk; u32 xin_id; enum sde_clk_ctrl_type clk_ctrl; + char name[SSPP_NAME_SIZE]; }; /** @@ -607,6 +611,31 @@ struct sde_perf_cfg { u32 max_bw_high; }; +/** +* struct sde_vp_sub_blks - Virtual Plane sub-blocks +* @pipeid_list list for hw pipe id +* @sspp_id SSPP ID, refer to enum sde_sspp. +*/ +struct sde_vp_sub_blks { + struct list_head pipeid_list; + u32 sspp_id; +}; + +/** +* struct sde_vp_cfg - information of Virtual Plane SW blocks +* @id enum identifying this block +* @sub_blks list head for virtual plane sub blocks +* @plane_type plane type, such as primary, overlay or cursor +* @display_type which display the plane bound to, such as primary, +* secondary or tertiary +*/ +struct sde_vp_cfg { + u32 id; + struct list_head sub_blks; + const char *plane_type; + const char *display_type; +}; + /** * struct sde_mdss_cfg - information of MDSS HW * This is the main catalog data structure representing @@ -672,6 +701,9 @@ struct sde_mdss_cfg { /* Add additional block data structures here */ struct sde_perf_cfg perf; + + u32 vp_count; + struct sde_vp_cfg vp[MAX_BLOCKS]; }; struct sde_mdss_hw_cfg_handler { diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 709c9970b357..45a87456e5ec 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -797,6 +797,16 @@ static void _sde_kms_drm_obj_destroy(struct sde_kms *sde_kms) _sde_kms_release_displays(sde_kms); } +static inline int sde_get_crtc_id(const char *display_type) +{ + if (!strcmp(display_type, "primary")) + return 0; + else if (!strcmp(display_type, "secondary")) + return 1; + else + return 2; +} + static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) { struct drm_device *dev; @@ -829,28 +839,57 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) (void)_sde_kms_setup_displays(dev, priv, sde_kms); max_crtc_count = min(catalog->mixer_count, priv->num_encoders); - max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES); /* Create the planes */ primary_planes_idx = 0; - for (i = 0; i < max_plane_count; i++) { - bool primary = true; - - if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR) - || primary_planes_idx >= max_crtc_count) - primary = false; - - plane = sde_plane_init(dev, catalog->sspp[i].id, primary, - (1UL << max_crtc_count) - 1); - if (IS_ERR(plane)) { - SDE_ERROR("sde_plane_init failed\n"); - ret = PTR_ERR(plane); - goto fail; + if (catalog->vp_count) { + max_plane_count = min_t(u32, catalog->vp_count, MAX_PLANES); + + for (i = 0; i < max_plane_count; i++) { + bool primary = true; + int crtc_id = + sde_get_crtc_id(catalog->vp[i].display_type); + + if (strcmp(catalog->vp[i].plane_type, "primary")) + primary = false; + + plane = sde_plane_init(dev, catalog->vp[i].id, + primary, 1UL << crtc_id, true); + if (IS_ERR(plane)) { + SDE_ERROR("sde_plane_init failed\n"); + ret = PTR_ERR(plane); + goto fail; + } + priv->planes[priv->num_planes++] = plane; + + if (primary) { + primary_planes[crtc_id] = plane; + primary_planes_idx++; + } + } + } else { + max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES); + + for (i = 0; i < max_plane_count; i++) { + bool primary = true; + + if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR) + || primary_planes_idx >= max_crtc_count) + primary = false; + + plane = sde_plane_init(dev, catalog->sspp[i].id, + primary, (1UL << max_crtc_count) - 1, + false); + if (IS_ERR(plane)) { + SDE_ERROR("sde_plane_init failed\n"); + ret = PTR_ERR(plane); + goto fail; + } + priv->planes[priv->num_planes++] = plane; + + if (primary) + primary_planes[primary_planes_idx++] = plane; } - priv->planes[priv->num_planes++] = plane; - - if (primary) - primary_planes[primary_planes_idx++] = plane; } max_crtc_count = min(max_crtc_count, primary_planes_idx); diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c index 114acfd7a173..5c84025243a3 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.c +++ b/drivers/gpu/drm/msm/sde/sde_plane.c @@ -78,20 +78,22 @@ enum sde_plane_qos { }; /* - * struct sde_plane - local sde plane structure + * struct sde_phy_plane - physical plane structure + * @sde_plane: Points to virtual plane + * @phy_plane_list: list of hw pipe(physical plane) + * @index: index of physical plane (starts from 0, order from left to right) + * @features: capabilities from catalog * @csc_cfg: Decoded user configuration for csc * @csc_usr_ptr: Points to csc_cfg if valid user config available * @csc_ptr: Points to sde_csc_cfg structure to use for current */ -struct sde_plane { - struct drm_plane base; - - struct msm_gem_address_space *aspace; - - struct mutex lock; - +struct sde_phy_plane { + struct sde_plane *sde_plane; + struct list_head phy_plane_list; enum sde_sspp pipe; - uint32_t features; /* capabilities from catalog */ + uint32_t index; + + uint32_t features; uint32_t nformats; uint32_t formats[64]; @@ -101,7 +103,6 @@ struct sde_plane { struct sde_hw_scaler3_cfg *scaler3_cfg; struct sde_hw_pipe_qos_cfg pipe_qos_cfg; uint32_t color_fill; - bool is_error; bool is_rt_pipe; struct sde_hw_pixel_ext pixel_ext; @@ -112,9 +113,22 @@ struct sde_plane { struct sde_csc_cfg *csc_ptr; const struct sde_sspp_sub_blks *pipe_sblk; +}; +/* + * struct sde_plane - local sde plane structure + */ +struct sde_plane { + struct drm_plane base; + + struct msm_gem_address_space *aspace; + struct mutex lock; + bool is_error; char pipe_name[SDE_NAME_SIZE]; + struct list_head phy_plane_head; + u32 num_of_phy_planes; + struct msm_property_info property_info; struct msm_property_data property_data[PLANE_PROP_COUNT]; struct drm_property_blob *blob_info; @@ -140,20 +154,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state) * @src_wdith: width of source buffer * Return: fill level corresponding to the source buffer/format or 0 if error */ -static inline int _sde_plane_calc_fill_level(struct drm_plane *plane, +static inline int _sde_plane_calc_fill_level(struct sde_phy_plane *pp, const struct sde_format *fmt, u32 src_width) { struct sde_plane *psde; u32 fixed_buff_size; u32 total_fl; - if (!plane || !fmt) { + if (!pp || !fmt) { SDE_ERROR("invalid arguments\n"); return 0; } - psde = to_sde_plane(plane); - fixed_buff_size = psde->pipe_sblk->pixel_ram_size; + psde = pp->sde_plane; + fixed_buff_size = pp->pipe_sblk->pixel_ram_size; if (fmt->fetch_planes == SDE_PLANE_PSEUDO_PLANAR) { if (fmt->chroma_sample == SDE_CHROMA_420) { @@ -171,7 +185,7 @@ static inline int _sde_plane_calc_fill_level(struct drm_plane *plane, } SDE_DEBUG("plane%u: pnum:%d fmt:%x w:%u fl:%u\n", - plane->base.id, psde->pipe - SSPP_VIG0, + psde->base.base.id, pp->pipe - SSPP_VIG0, fmt->base.pixel_format, src_width, total_fl); return total_fl; @@ -236,7 +250,7 @@ static inline u32 _sde_plane_get_qos_lut_macrotile(u32 total_fl) * @plane: Pointer to drm plane * @fb: Pointer to framebuffer associated with the given plane */ -static void _sde_plane_set_qos_lut(struct drm_plane *plane, +static void _sde_plane_set_qos_lut(struct sde_phy_plane *pp, struct drm_framebuffer *fb) { struct sde_plane *psde; @@ -244,30 +258,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, u32 qos_lut; u32 total_fl = 0; - if (!plane || !fb) { - SDE_ERROR("invalid arguments plane %d fb %d\n", - plane != 0, fb != 0); + if (!pp || !fb) { + SDE_ERROR("invalid arguments phy_plane %d fb %d\n", + pp != NULL, fb != NULL); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_creq_lut) { + } else if (!pp->pipe_hw->ops.setup_creq_lut) { return; } - if (!psde->is_rt_pipe) { - qos_lut = psde->pipe_sblk->creq_lut_nrt; + if (!pp->is_rt_pipe) { + qos_lut = pp->pipe_sblk->creq_lut_nrt; } else { fmt = sde_get_sde_format_ext( fb->pixel_format, fb->modifier, drm_format_num_planes(fb->pixel_format)); - total_fl = _sde_plane_calc_fill_level(plane, fmt, - psde->pipe_cfg.src_rect.w); + total_fl = _sde_plane_calc_fill_level(pp, fmt, + pp->pipe_cfg.src_rect.w); if (SDE_FORMAT_IS_LINEAR(fmt)) qos_lut = _sde_plane_get_qos_lut_linear(total_fl); @@ -275,20 +289,20 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, qos_lut = _sde_plane_get_qos_lut_macrotile(total_fl); } - psde->pipe_qos_cfg.creq_lut = qos_lut; + pp->pipe_qos_cfg.creq_lut = qos_lut; - trace_sde_perf_set_qos_luts(psde->pipe - SSPP_VIG0, + trace_sde_perf_set_qos_luts(pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, - psde->is_rt_pipe, total_fl, qos_lut, + pp->is_rt_pipe, total_fl, qos_lut, (fmt) ? SDE_FORMAT_IS_LINEAR(fmt) : 0); SDE_DEBUG("plane%u: pnum:%d fmt:%x rt:%d fl:%u lut:0x%x\n", - plane->base.id, - psde->pipe - SSPP_VIG0, + psde->base.base.id, + pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, - psde->is_rt_pipe, total_fl, qos_lut); + pp->is_rt_pipe, total_fl, qos_lut); - psde->pipe_hw->ops.setup_creq_lut(psde->pipe_hw, &psde->pipe_qos_cfg); + pp->pipe_hw->ops.setup_creq_lut(pp->pipe_hw, &pp->pipe_qos_cfg); } /** @@ -296,30 +310,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, * @plane: Pointer to drm plane * @fb: Pointer to framebuffer associated with the given plane */ -static void _sde_plane_set_danger_lut(struct drm_plane *plane, +static void _sde_plane_set_danger_lut(struct sde_phy_plane *pp, struct drm_framebuffer *fb) { struct sde_plane *psde; const struct sde_format *fmt = NULL; u32 danger_lut, safe_lut; - if (!plane || !fb) { + if (!pp || !fb) { SDE_ERROR("invalid arguments\n"); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_danger_safe_lut) { + } else if (!pp->pipe_hw->ops.setup_danger_safe_lut) { return; } - if (!psde->is_rt_pipe) { - danger_lut = psde->pipe_sblk->danger_lut_nrt; - safe_lut = psde->pipe_sblk->safe_lut_nrt; + if (!pp->is_rt_pipe) { + danger_lut = pp->pipe_sblk->danger_lut_nrt; + safe_lut = pp->pipe_sblk->safe_lut_nrt; } else { fmt = sde_get_sde_format_ext( fb->pixel_format, @@ -327,33 +341,33 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane, drm_format_num_planes(fb->pixel_format)); if (SDE_FORMAT_IS_LINEAR(fmt)) { - danger_lut = psde->pipe_sblk->danger_lut_linear; - safe_lut = psde->pipe_sblk->safe_lut_linear; + danger_lut = pp->pipe_sblk->danger_lut_linear; + safe_lut = pp->pipe_sblk->safe_lut_linear; } else { - danger_lut = psde->pipe_sblk->danger_lut_tile; - safe_lut = psde->pipe_sblk->safe_lut_tile; + danger_lut = pp->pipe_sblk->danger_lut_tile; + safe_lut = pp->pipe_sblk->safe_lut_tile; } } - psde->pipe_qos_cfg.danger_lut = danger_lut; - psde->pipe_qos_cfg.safe_lut = safe_lut; + pp->pipe_qos_cfg.danger_lut = danger_lut; + pp->pipe_qos_cfg.safe_lut = safe_lut; - trace_sde_perf_set_danger_luts(psde->pipe - SSPP_VIG0, + trace_sde_perf_set_danger_luts(pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, (fmt) ? fmt->fetch_mode : 0, - psde->pipe_qos_cfg.danger_lut, - psde->pipe_qos_cfg.safe_lut); + pp->pipe_qos_cfg.danger_lut, + pp->pipe_qos_cfg.safe_lut); SDE_DEBUG("plane%u: pnum:%d fmt:%x mode:%d luts[0x%x, 0x%x]\n", - plane->base.id, - psde->pipe - SSPP_VIG0, + psde->base.base.id, + pp->pipe - SSPP_VIG0, fmt ? fmt->base.pixel_format : 0, fmt ? fmt->fetch_mode : -1, - psde->pipe_qos_cfg.danger_lut, - psde->pipe_qos_cfg.safe_lut); + pp->pipe_qos_cfg.danger_lut, + pp->pipe_qos_cfg.safe_lut); - psde->pipe_hw->ops.setup_danger_safe_lut(psde->pipe_hw, - &psde->pipe_qos_cfg); + pp->pipe_hw->ops.setup_danger_safe_lut(pp->pipe_hw, + &pp->pipe_qos_cfg); } /** @@ -362,85 +376,90 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane, * @enable: true to enable QoS control * @flags: QoS control mode (enum sde_plane_qos) */ -static void _sde_plane_set_qos_ctrl(struct drm_plane *plane, +static void _sde_plane_set_qos_ctrl(struct sde_phy_plane *pp, bool enable, u32 flags) { struct sde_plane *psde; - if (!plane) { + if (!pp) { SDE_ERROR("invalid arguments\n"); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_qos_ctrl) { + } else if (!pp->pipe_hw->ops.setup_qos_ctrl) { return; } if (flags & SDE_PLANE_QOS_VBLANK_CTRL) { - psde->pipe_qos_cfg.creq_vblank = psde->pipe_sblk->creq_vblank; - psde->pipe_qos_cfg.danger_vblank = - psde->pipe_sblk->danger_vblank; - psde->pipe_qos_cfg.vblank_en = enable; + pp->pipe_qos_cfg.creq_vblank = pp->pipe_sblk->creq_vblank; + pp->pipe_qos_cfg.danger_vblank = + pp->pipe_sblk->danger_vblank; + pp->pipe_qos_cfg.vblank_en = enable; } if (flags & SDE_PLANE_QOS_VBLANK_AMORTIZE) { /* this feature overrules previous VBLANK_CTRL */ - psde->pipe_qos_cfg.vblank_en = false; - psde->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */ + pp->pipe_qos_cfg.vblank_en = false; + pp->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */ } if (flags & SDE_PLANE_QOS_PANIC_CTRL) - psde->pipe_qos_cfg.danger_safe_en = enable; + pp->pipe_qos_cfg.danger_safe_en = enable; - if (!psde->is_rt_pipe) { - psde->pipe_qos_cfg.vblank_en = false; - psde->pipe_qos_cfg.danger_safe_en = false; + if (!pp->is_rt_pipe) { + pp->pipe_qos_cfg.vblank_en = false; + pp->pipe_qos_cfg.danger_safe_en = false; } SDE_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n", - plane->base.id, - psde->pipe - SSPP_VIG0, - psde->pipe_qos_cfg.danger_safe_en, - psde->pipe_qos_cfg.vblank_en, - psde->pipe_qos_cfg.creq_vblank, - psde->pipe_qos_cfg.danger_vblank, - psde->is_rt_pipe); - - psde->pipe_hw->ops.setup_qos_ctrl(psde->pipe_hw, - &psde->pipe_qos_cfg); + psde->base.base.id, + pp->pipe - SSPP_VIG0, + pp->pipe_qos_cfg.danger_safe_en, + pp->pipe_qos_cfg.vblank_en, + pp->pipe_qos_cfg.creq_vblank, + pp->pipe_qos_cfg.danger_vblank, + pp->is_rt_pipe); + + pp->pipe_hw->ops.setup_qos_ctrl(pp->pipe_hw, + &pp->pipe_qos_cfg); } -int sde_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable) +static int sde_plane_danger_signal_ctrl(struct sde_phy_plane *pp, bool enable) { struct sde_plane *psde; struct msm_drm_private *priv; struct sde_kms *sde_kms; - if (!plane || !plane->dev) { + if (!pp) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + psde = pp->sde_plane; + + if (!psde->base.dev) { SDE_ERROR("invalid arguments\n"); return -EINVAL; } - priv = plane->dev->dev_private; + priv = psde->base.dev->dev_private; if (!priv || !priv->kms) { SDE_ERROR("invalid KMS reference\n"); return -EINVAL; } sde_kms = to_sde_kms(priv->kms); - psde = to_sde_plane(plane); - if (!psde->is_rt_pipe) + if (!pp->is_rt_pipe) goto end; sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); - _sde_plane_set_qos_ctrl(plane, enable, SDE_PLANE_QOS_PANIC_CTRL); + _sde_plane_set_qos_ctrl(pp, enable, SDE_PLANE_QOS_PANIC_CTRL); sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); @@ -453,7 +472,7 @@ end: * @plane: Pointer to drm plane * @crtc: Pointer to drm crtc */ -static void _sde_plane_set_ot_limit(struct drm_plane *plane, +static void _sde_plane_set_ot_limit(struct sde_phy_plane *pp, struct drm_crtc *crtc) { struct sde_plane *psde; @@ -461,34 +480,38 @@ static void _sde_plane_set_ot_limit(struct drm_plane *plane, struct msm_drm_private *priv; struct sde_kms *sde_kms; - if (!plane || !plane->dev || !crtc) { - SDE_ERROR("invalid arguments plane %d crtc %d\n", - plane != 0, crtc != 0); + if (!pp || !crtc) { + SDE_ERROR("invalid arguments phy_plane %d crtc %d\n", + pp != NULL, crtc != NULL); + return; + } + psde = pp->sde_plane; + if (!psde->base.dev) { + SDE_ERROR("invalid DRM device\n"); return; } - priv = plane->dev->dev_private; + priv = psde->base.dev->dev_private; if (!priv || !priv->kms) { SDE_ERROR("invalid KMS reference\n"); return; } sde_kms = to_sde_kms(priv->kms); - psde = to_sde_plane(plane); - if (!psde->pipe_hw) { + if (!pp->pipe_hw) { SDE_ERROR("invalid pipe reference\n"); return; } memset(&ot_params, 0, sizeof(ot_params)); - ot_params.xin_id = psde->pipe_hw->cap->xin_id; - ot_params.num = psde->pipe_hw->idx - SSPP_NONE; - ot_params.width = psde->pipe_cfg.src_rect.w; - ot_params.height = psde->pipe_cfg.src_rect.h; - ot_params.is_wfd = !psde->is_rt_pipe; + ot_params.xin_id = pp->pipe_hw->cap->xin_id; + ot_params.num = pp->pipe_hw->idx - SSPP_NONE; + ot_params.width = pp->pipe_cfg.src_rect.w; + ot_params.height = pp->pipe_cfg.src_rect.h; + ot_params.is_wfd = !pp->is_rt_pipe; ot_params.frame_rate = crtc->mode.vrefresh; ot_params.vbif_idx = VBIF_RT; - ot_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl; + ot_params.clk_ctrl = pp->pipe_hw->cap->clk_ctrl; ot_params.rd = true; sde_vbif_set_ot_limit(sde_kms, &ot_params); @@ -559,7 +582,7 @@ int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms) return ret; } -static inline void _sde_plane_set_scanout(struct drm_plane *plane, +static inline void _sde_plane_set_scanout(struct sde_phy_plane *pp, struct sde_plane_state *pstate, struct sde_hw_pipe_cfg *pipe_cfg, struct drm_framebuffer *fb) @@ -567,15 +590,15 @@ static inline void _sde_plane_set_scanout(struct drm_plane *plane, struct sde_plane *psde; int ret; - if (!plane || !pstate || !pipe_cfg || !fb) { + if (!pp || !pstate || !pipe_cfg || !fb) { SDE_ERROR( - "invalid arg(s), plane %d state %d cfg %d fb %d\n", - plane != 0, pstate != 0, pipe_cfg != 0, fb != 0); + "invalid arg(s), phy_plane %d state %d cfg %d fb %d\n", + pp != 0, pstate != 0, pipe_cfg != 0, fb != 0); return; } - psde = to_sde_plane(plane); - if (!psde->pipe_hw) { + psde = pp->sde_plane; + if (!pp->pipe_hw) { SDE_ERROR_PLANE(psde, "invalid pipe_hw\n"); return; } @@ -585,14 +608,15 @@ static inline void _sde_plane_set_scanout(struct drm_plane *plane, SDE_DEBUG_PLANE(psde, "not updating same src addrs\n"); else if (ret) SDE_ERROR_PLANE(psde, "failed to get format layout, %d\n", ret); - else if (psde->pipe_hw->ops.setup_sourceaddress) - psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg); + else if (pp->pipe_hw && pp->pipe_hw->ops.setup_sourceaddress) + pp->pipe_hw->ops.setup_sourceaddress(pp->pipe_hw, pipe_cfg); } -static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde, +static int _sde_plane_setup_scaler3_lut(struct sde_phy_plane *pp, struct sde_plane_state *pstate) { - struct sde_hw_scaler3_cfg *cfg = psde->scaler3_cfg; + struct sde_plane *psde = pp->sde_plane; + struct sde_hw_scaler3_cfg *cfg = pp->scaler3_cfg; int ret = 0; cfg->dir_lut = msm_property_get_blob( @@ -612,7 +636,7 @@ static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde, return ret; } -static void _sde_plane_setup_scaler3(struct sde_plane *psde, +static void _sde_plane_setup_scaler3(struct sde_phy_plane *pp, uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h, struct sde_hw_scaler3_cfg *scale_cfg, const struct sde_format *fmt, @@ -620,10 +644,10 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, { uint32_t decimated, i; - if (!psde || !scale_cfg || !fmt || !chroma_subsmpl_h || + if (!pp || !scale_cfg || !fmt || !chroma_subsmpl_h || !chroma_subsmpl_v) { SDE_ERROR("psde %pK scale_cfg %pK fmt %pK smp_h %d smp_v %d\n" - , psde, scale_cfg, fmt, chroma_subsmpl_h, + , pp, scale_cfg, fmt, chroma_subsmpl_h, chroma_subsmpl_v); return; } @@ -631,11 +655,11 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, memset(scale_cfg, 0, sizeof(*scale_cfg)); decimated = DECIMATED_DIMENSION(src_w, - psde->pipe_cfg.horz_decimation); + pp->pipe_cfg.horz_decimation); scale_cfg->phase_step_x[SDE_SSPP_COMP_0] = mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_w); decimated = DECIMATED_DIMENSION(src_h, - psde->pipe_cfg.vert_decimation); + pp->pipe_cfg.vert_decimation); scale_cfg->phase_step_y[SDE_SSPP_COMP_0] = mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_h); @@ -657,9 +681,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, for (i = 0; i < SDE_MAX_PLANES; i++) { scale_cfg->src_width[i] = DECIMATED_DIMENSION(src_w, - psde->pipe_cfg.horz_decimation); + pp->pipe_cfg.horz_decimation); scale_cfg->src_height[i] = DECIMATED_DIMENSION(src_h, - psde->pipe_cfg.vert_decimation); + pp->pipe_cfg.vert_decimation); if (SDE_FORMAT_IS_YUV(fmt)) scale_cfg->src_width[i] &= ~0x1; if (i == SDE_SSPP_COMP_1_2 || i == SDE_SSPP_COMP_2) { @@ -668,9 +692,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, } scale_cfg->preload_x[i] = SDE_QSEED3_DEFAULT_PRELOAD_H; scale_cfg->preload_y[i] = SDE_QSEED3_DEFAULT_PRELOAD_V; - psde->pixel_ext.num_ext_pxls_top[i] = + pp->pixel_ext.num_ext_pxls_top[i] = scale_cfg->src_height[i]; - psde->pixel_ext.num_ext_pxls_left[i] = + pp->pixel_ext.num_ext_pxls_left[i] = scale_cfg->src_width[i]; } if (!(SDE_FORMAT_IS_YUV(fmt)) && (src_h == dst_h) @@ -835,7 +859,7 @@ static void _sde_plane_setup_pixel_ext(struct sde_plane *psde, } } -static inline void _sde_plane_setup_csc(struct sde_plane *psde) +static inline void _sde_plane_setup_csc(struct sde_phy_plane *pp) { static const struct sde_csc_cfg sde_csc_YUV2RGB_601L = { { @@ -866,26 +890,30 @@ static inline void _sde_plane_setup_csc(struct sde_plane *psde) { 0x00, 0x3ff, 0x00, 0x3ff, 0x00, 0x3ff,}, }; - if (!psde) { + struct sde_plane *psde; + + if (!pp) { SDE_ERROR("invalid plane\n"); return; } + psde = pp->sde_plane; /* revert to kernel default if override not available */ - if (psde->csc_usr_ptr) - psde->csc_ptr = psde->csc_usr_ptr; - else if (BIT(SDE_SSPP_CSC_10BIT) & psde->features) - psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L; + if (pp->csc_usr_ptr) + pp->csc_ptr = pp->csc_usr_ptr; + else if (BIT(SDE_SSPP_CSC_10BIT) & pp->features) + pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L; else - psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L; + pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L; SDE_DEBUG_PLANE(psde, "using 0x%X 0x%X 0x%X...\n", - psde->csc_ptr->csc_mv[0], - psde->csc_ptr->csc_mv[1], - psde->csc_ptr->csc_mv[2]); + pp->csc_ptr->csc_mv[0], + pp->csc_ptr->csc_mv[1], + pp->csc_ptr->csc_mv[2]); } -static void sde_color_process_plane_setup(struct drm_plane *plane) +static void sde_color_process_plane_setup(struct drm_plane *plane, + struct sde_phy_plane *pp) { struct sde_plane *psde; struct sde_plane_state *pstate; @@ -893,32 +921,32 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) struct drm_msm_memcol *memcol = NULL; size_t memcol_sz = 0; - psde = to_sde_plane(plane); + psde = pp->sde_plane; pstate = to_sde_plane_state(plane->state); hue = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_HUE_ADJUST); - if (psde->pipe_hw->ops.setup_pa_hue) - psde->pipe_hw->ops.setup_pa_hue(psde->pipe_hw, &hue); + if (pp->pipe_hw->ops.setup_pa_hue) + pp->pipe_hw->ops.setup_pa_hue(pp->pipe_hw, &hue); saturation = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_SATURATION_ADJUST); - if (psde->pipe_hw->ops.setup_pa_sat) - psde->pipe_hw->ops.setup_pa_sat(psde->pipe_hw, &saturation); + if (pp->pipe_hw->ops.setup_pa_sat) + pp->pipe_hw->ops.setup_pa_sat(pp->pipe_hw, &saturation); value = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_VALUE_ADJUST); - if (psde->pipe_hw->ops.setup_pa_val) - psde->pipe_hw->ops.setup_pa_val(psde->pipe_hw, &value); + if (pp->pipe_hw->ops.setup_pa_val) + pp->pipe_hw->ops.setup_pa_val(pp->pipe_hw, &value); contrast = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_CONTRAST_ADJUST); - if (psde->pipe_hw->ops.setup_pa_cont) - psde->pipe_hw->ops.setup_pa_cont(psde->pipe_hw, &contrast); + if (pp->pipe_hw->ops.setup_pa_cont) + pp->pipe_hw->ops.setup_pa_cont(pp->pipe_hw, &contrast); - if (psde->pipe_hw->ops.setup_pa_memcolor) { + if (pp->pipe_hw->ops.setup_pa_memcolor) { /* Skin memory color setup */ memcol = msm_property_get_blob(&psde->property_info, pstate->property_blobs, &memcol_sz, PLANE_PROP_SKIN_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_SKIN, memcol); /* Sky memory color setup */ @@ -926,7 +954,7 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) pstate->property_blobs, &memcol_sz, PLANE_PROP_SKY_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_SKY, memcol); /* Foliage memory color setup */ @@ -934,87 +962,89 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) pstate->property_blobs, &memcol_sz, PLANE_PROP_FOLIAGE_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_FOLIAGE, memcol); } } -static void _sde_plane_setup_scaler(struct sde_plane *psde, +static void _sde_plane_setup_scaler(struct sde_phy_plane *pp, const struct sde_format *fmt, struct sde_plane_state *pstate) { struct sde_hw_pixel_ext *pe; uint32_t chroma_subsmpl_h, chroma_subsmpl_v; + struct sde_plane *psde; - if (!psde || !fmt) { - SDE_ERROR("invalid arg(s), plane %d fmt %d state %d\n", - psde != 0, fmt != 0, pstate != 0); + if (!pp || !fmt || !pstate || !pp->sde_plane) { + SDE_ERROR("invalid arg(s), phy_plane %d fmt %d\n", + pp != NULL, fmt != NULL); return; } + psde = pp->sde_plane; - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); - psde->pipe_cfg.horz_decimation = + pp->pipe_cfg.horz_decimation = sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE); - psde->pipe_cfg.vert_decimation = + pp->pipe_cfg.vert_decimation = sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE); /* don't chroma subsample if decimating */ - chroma_subsmpl_h = psde->pipe_cfg.horz_decimation ? 1 : + chroma_subsmpl_h = pp->pipe_cfg.horz_decimation ? 1 : drm_format_horz_chroma_subsampling(fmt->base.pixel_format); - chroma_subsmpl_v = psde->pipe_cfg.vert_decimation ? 1 : + chroma_subsmpl_v = pp->pipe_cfg.vert_decimation ? 1 : drm_format_vert_chroma_subsampling(fmt->base.pixel_format); /* update scaler */ - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) { int error; - error = _sde_plane_setup_scaler3_lut(psde, pstate); - if (error || !psde->pixel_ext_usr) { + error = _sde_plane_setup_scaler3_lut(pp, pstate); + if (error || !pp->pixel_ext_usr) { memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); /* calculate default config for QSEED3 */ - _sde_plane_setup_scaler3(psde, - psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.dst_rect.w, - psde->pipe_cfg.dst_rect.h, - psde->scaler3_cfg, fmt, + _sde_plane_setup_scaler3(pp, + pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.dst_rect.w, + pp->pipe_cfg.dst_rect.h, + pp->scaler3_cfg, fmt, chroma_subsmpl_h, chroma_subsmpl_v); } - } else if (!psde->pixel_ext_usr) { + } else if (!pp->pixel_ext_usr) { uint32_t deci_dim, i; /* calculate default configuration for QSEED2 */ memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); SDE_DEBUG_PLANE(psde, "default config\n"); - deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.horz_decimation); + deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.horz_decimation); _sde_plane_setup_scaler2(psde, deci_dim, - psde->pipe_cfg.dst_rect.w, + pp->pipe_cfg.dst_rect.w, pe->phase_step_x, pe->horz_filter, fmt, chroma_subsmpl_h); if (SDE_FORMAT_IS_YUV(fmt)) deci_dim &= ~0x1; - _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.dst_rect.w, deci_dim, + _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.dst_rect.w, deci_dim, pe->phase_step_x, pe->roi_w, pe->num_ext_pxls_left, pe->num_ext_pxls_right, pe->horz_filter, fmt, chroma_subsmpl_h, 0); - deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.vert_decimation); + deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.vert_decimation); _sde_plane_setup_scaler2(psde, deci_dim, - psde->pipe_cfg.dst_rect.h, + pp->pipe_cfg.dst_rect.h, pe->phase_step_y, pe->vert_filter, fmt, chroma_subsmpl_v); - _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.dst_rect.h, deci_dim, + _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.dst_rect.h, deci_dim, pe->phase_step_y, pe->roi_h, pe->num_ext_pxls_top, @@ -1052,22 +1082,22 @@ static void _sde_plane_setup_scaler(struct sde_plane *psde, * @alpha: 8-bit fill alpha value, 255 selects 100% alpha * Returns: 0 on success */ -static int _sde_plane_color_fill(struct sde_plane *psde, +static int _sde_plane_color_fill(struct sde_phy_plane *pp, uint32_t color, uint32_t alpha) { const struct sde_format *fmt; - if (!psde) { + if (!pp) { SDE_ERROR("invalid plane\n"); return -EINVAL; } - if (!psde->pipe_hw) { - SDE_ERROR_PLANE(psde, "invalid plane h/w pointer\n"); + if (!pp->pipe_hw) { + SDE_ERROR_PLANE(pp->sde_plane, "invalid plane h/w pointer\n"); return -EINVAL; } - SDE_DEBUG_PLANE(psde, "\n"); + SDE_DEBUG_PLANE(pp->sde_plane, "\n"); /* * select fill format to match user property expectation, @@ -1076,26 +1106,26 @@ static int _sde_plane_color_fill(struct sde_plane *psde, fmt = sde_get_sde_format(DRM_FORMAT_ABGR8888); /* update sspp */ - if (fmt && psde->pipe_hw->ops.setup_solidfill) { - psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw, + if (fmt && pp->pipe_hw->ops.setup_solidfill) { + pp->pipe_hw->ops.setup_solidfill(pp->pipe_hw, (color & 0xFFFFFF) | ((alpha & 0xFF) << 24)); /* override scaler/decimation if solid fill */ - psde->pipe_cfg.src_rect.x = 0; - psde->pipe_cfg.src_rect.y = 0; - psde->pipe_cfg.src_rect.w = psde->pipe_cfg.dst_rect.w; - psde->pipe_cfg.src_rect.h = psde->pipe_cfg.dst_rect.h; + pp->pipe_cfg.src_rect.x = 0; + pp->pipe_cfg.src_rect.y = 0; + pp->pipe_cfg.src_rect.w = pp->pipe_cfg.dst_rect.w; + pp->pipe_cfg.src_rect.h = pp->pipe_cfg.dst_rect.h; - _sde_plane_setup_scaler(psde, fmt, 0); + _sde_plane_setup_scaler(pp, fmt, NULL); - if (psde->pipe_hw->ops.setup_format) - psde->pipe_hw->ops.setup_format(psde->pipe_hw, + if (pp->pipe_hw->ops.setup_format) + pp->pipe_hw->ops.setup_format(pp->pipe_hw, fmt, SDE_SSPP_SOLID_FILL); - if (psde->pipe_hw->ops.setup_rects) - psde->pipe_hw->ops.setup_rects(psde->pipe_hw, - &psde->pipe_cfg, &psde->pixel_ext, - psde->scaler3_cfg); + if (pp->pipe_hw->ops.setup_rects) + pp->pipe_hw->ops.setup_rects(pp->pipe_hw, + &pp->pipe_cfg, &pp->pixel_ext, + pp->scaler3_cfg); } return 0; @@ -1113,6 +1143,8 @@ static int _sde_plane_mode_set(struct drm_plane *plane, struct sde_rect src, dst; bool q16_data = true; int idx; + struct sde_phy_plane *pp; + uint32_t num_of_phy_planes = 0, maxlinewidth = 0xFFFF; if (!plane) { SDE_ERROR("invalid plane\n"); @@ -1170,19 +1202,24 @@ static int _sde_plane_mode_set(struct drm_plane *plane, } } - if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) - memset(&(psde->pipe_cfg), 0, sizeof(struct sde_hw_pipe_cfg)); + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) + memset(&(pp->pipe_cfg), 0, + sizeof(struct sde_hw_pipe_cfg)); + + _sde_plane_set_scanout(pp, pstate, &pp->pipe_cfg, fb); - _sde_plane_set_scanout(plane, pstate, &psde->pipe_cfg, fb); + pstate->pending = true; + + pp->is_rt_pipe = sde_crtc_is_rt(crtc); + _sde_plane_set_qos_ctrl(pp, false, SDE_PLANE_QOS_PANIC_CTRL); + } /* early out if nothing dirty */ if (!pstate->dirty) return 0; - pstate->pending = true; - - psde->is_rt_pipe = sde_crtc_is_rt(crtc); - _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL); + memset(&src, 0, sizeof(struct sde_rect)); /* update roi config */ if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) { POPULATE_RECT(&src, state->src_x, state->src_y, @@ -1201,72 +1238,99 @@ static int _sde_plane_mode_set(struct drm_plane *plane, BIT(SDE_DRM_DEINTERLACE)) { SDE_DEBUG_PLANE(psde, "deinterlace\n"); for (idx = 0; idx < SDE_MAX_PLANES; ++idx) - psde->pipe_cfg.layout.plane_pitch[idx] <<= 1; + pp->pipe_cfg.layout.plane_pitch[idx] <<= 1; src.h /= 2; src.y = DIV_ROUND_UP(src.y, 2); src.y &= ~0x1; } + } + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (maxlinewidth > pp->pipe_sblk->maxlinewidth) + maxlinewidth = pp->pipe_sblk->maxlinewidth; + num_of_phy_planes++; + } + + /* + * Only need to use one physical plane if plane width is still within + * the limitation. + */ + if (maxlinewidth >= (src.x + src.w)) + num_of_phy_planes = 1; + + if (num_of_phy_planes > 1) { + /* Adjust width for multi-pipe */ + src.w /= num_of_phy_planes; + dst.w /= num_of_phy_planes; + } - psde->pipe_cfg.src_rect = src; - psde->pipe_cfg.dst_rect = dst; + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + /* Adjust offset for multi-pipe */ + src.x += src.w * pp->index; + dst.x += dst.w * pp->index; + + pp->pipe_cfg.src_rect = src; + pp->pipe_cfg.dst_rect = dst; /* check for color fill */ - psde->color_fill = (uint32_t)sde_plane_get_property(pstate, + pp->color_fill = (uint32_t)sde_plane_get_property(pstate, PLANE_PROP_COLOR_FILL); - if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG) { + if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) { /* skip remaining processing on color fill */ pstate->dirty = 0x0; - } else if (psde->pipe_hw->ops.setup_rects) { - _sde_plane_setup_scaler(psde, fmt, pstate); + } else if (pp->pipe_hw->ops.setup_rects) { + _sde_plane_setup_scaler(pp, fmt, pstate); - psde->pipe_hw->ops.setup_rects(psde->pipe_hw, - &psde->pipe_cfg, &psde->pixel_ext, - psde->scaler3_cfg); + pp->pipe_hw->ops.setup_rects(pp->pipe_hw, + &pp->pipe_cfg, &pp->pixel_ext, + pp->scaler3_cfg); } - } - if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) && - psde->pipe_hw->ops.setup_format) { - src_flags = 0x0; - SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n", + if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) && + pp->pipe_hw->ops.setup_format) { + src_flags = 0x0; + SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n", sde_plane_get_property(pstate, PLANE_PROP_ROTATION)); - if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) & - BIT(DRM_REFLECT_X)) - src_flags |= SDE_SSPP_FLIP_LR; - if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) & - BIT(DRM_REFLECT_Y)) - src_flags |= SDE_SSPP_FLIP_UD; - - /* update format */ - psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags); - - /* update csc */ - if (SDE_FORMAT_IS_YUV(fmt)) - _sde_plane_setup_csc(psde); - else - psde->csc_ptr = 0; - } + if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) + & BIT(DRM_REFLECT_X)) + src_flags |= SDE_SSPP_FLIP_LR; + if (sde_plane_get_property(pstate, + PLANE_PROP_ROTATION) & BIT(DRM_REFLECT_Y)) + src_flags |= SDE_SSPP_FLIP_UD; + + /* update format */ + pp->pipe_hw->ops.setup_format(pp->pipe_hw, + fmt, src_flags); + + /* update csc */ + if (SDE_FORMAT_IS_YUV(fmt)) + _sde_plane_setup_csc(pp); + else + pp->csc_ptr = NULL; + } - sde_color_process_plane_setup(plane); + sde_color_process_plane_setup(plane, pp); - /* update sharpening */ - if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) && - psde->pipe_hw->ops.setup_sharpening) { - psde->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT; - psde->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT; - psde->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT; - psde->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT; + /* update sharpening */ + if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) && + pp->pipe_hw->ops.setup_sharpening) { + pp->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT; + pp->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT; + pp->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT; + pp->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT; - psde->pipe_hw->ops.setup_sharpening(psde->pipe_hw, - &psde->sharp_cfg); - } + pp->pipe_hw->ops.setup_sharpening(pp->pipe_hw, + &pp->sharp_cfg); + } - _sde_plane_set_qos_lut(plane, fb); - _sde_plane_set_danger_lut(plane, fb); + _sde_plane_set_qos_lut(pp, fb); + _sde_plane_set_danger_lut(pp, fb); - if (plane->type != DRM_PLANE_TYPE_CURSOR) { - _sde_plane_set_qos_ctrl(plane, true, SDE_PLANE_QOS_PANIC_CTRL); - _sde_plane_set_ot_limit(plane, crtc); + if (plane->type != DRM_PLANE_TYPE_CURSOR) { + _sde_plane_set_qos_ctrl(pp, true, + SDE_PLANE_QOS_PANIC_CTRL); + _sde_plane_set_ot_limit(pp, crtc); + } } /* clear dirty */ @@ -1393,10 +1457,12 @@ static int sde_plane_atomic_check(struct drm_plane *plane, uint32_t deci_w, deci_h, src_deci_w, src_deci_h; uint32_t max_upscale, max_downscale, min_src_size, max_linewidth; bool q16_data = true; + struct sde_phy_plane *pp; + uint32_t num_of_phy_planes = 0; if (!plane || !state) { - SDE_ERROR("invalid arg(s), plane %d state %d\n", - plane != 0, state != 0); + SDE_ERROR("invalid arg(s), plane %d state %d.\n", + plane != NULL, state != NULL); ret = -EINVAL; goto exit; } @@ -1404,11 +1470,8 @@ static int sde_plane_atomic_check(struct drm_plane *plane, psde = to_sde_plane(plane); pstate = to_sde_plane_state(state); - if (!psde->pipe_sblk) { - SDE_ERROR_PLANE(psde, "invalid catalog\n"); - ret = -EINVAL; - goto exit; - } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) + num_of_phy_planes++; deci_w = sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE); deci_h = sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE); @@ -1422,10 +1485,6 @@ static int sde_plane_atomic_check(struct drm_plane *plane, src_deci_w = DECIMATED_DIMENSION(src.w, deci_w); src_deci_h = DECIMATED_DIMENSION(src.h, deci_h); - max_upscale = psde->pipe_sblk->maxupscale; - max_downscale = psde->pipe_sblk->maxdwnscale; - max_linewidth = psde->pipe_sblk->maxlinewidth; - SDE_DEBUG_PLANE(psde, "check %d -> %d\n", sde_plane_enabled(plane->state), sde_plane_enabled(state)); @@ -1436,73 +1495,87 @@ static int sde_plane_atomic_check(struct drm_plane *plane, min_src_size = SDE_FORMAT_IS_YUV(fmt) ? 2 : 1; - if (SDE_FORMAT_IS_YUV(fmt) && - (!(psde->features & SDE_SSPP_SCALER) || - !(psde->features & (BIT(SDE_SSPP_CSC) - | BIT(SDE_SSPP_CSC_10BIT))))) { - SDE_ERROR_PLANE(psde, - "plane doesn't have scaler/csc for yuv\n"); - ret = -EINVAL; - - /* check src bounds */ - } else if (state->fb->width > MAX_IMG_WIDTH || - state->fb->height > MAX_IMG_HEIGHT || - src.w < min_src_size || src.h < min_src_size || - CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) || - CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) { - SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n", - src.x, src.y, src.w, src.h); - ret = -E2BIG; - - /* valid yuv image */ - } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1) || (src.y & 0x1) || - (src.w & 0x1) || (src.h & 0x1))) { - SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u, %ux%u\n", - src.x, src.y, src.w, src.h); - ret = -EINVAL; + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (!pp->pipe_sblk) { + SDE_ERROR("invalid plane catalog\n"); + ret = -EINVAL; + goto exit; + } - /* min dst support */ - } else if (dst.w < 0x1 || dst.h < 0x1) { - SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u, %ux%u\n", - dst.x, dst.y, dst.w, dst.h); - ret = -EINVAL; + max_upscale = pp->pipe_sblk->maxupscale; + max_downscale = pp->pipe_sblk->maxdwnscale; + max_linewidth = pp->pipe_sblk->maxlinewidth; - /* decimation validation */ - } else if (deci_w || deci_h) { - if ((deci_w > psde->pipe_sblk->maxhdeciexp) || - (deci_h > psde->pipe_sblk->maxvdeciexp)) { + if (SDE_FORMAT_IS_YUV(fmt) && + (!(pp->features & SDE_SSPP_SCALER) || + !(pp->features & (BIT(SDE_SSPP_CSC) + | BIT(SDE_SSPP_CSC_10BIT))))) { SDE_ERROR_PLANE(psde, - "too much decimation requested\n"); + "plane doesn't have scaler/csc for yuv\n"); ret = -EINVAL; - } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) { - SDE_ERROR_PLANE(psde, - "decimation requires linear fetch\n"); + + /* check src bounds */ + } else if (state->fb->width > MAX_IMG_WIDTH || + state->fb->height > MAX_IMG_HEIGHT || + src.w < min_src_size || src.h < min_src_size || + CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) || + CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) { + SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n", + src.x, src.y, src.w, src.h); + ret = -E2BIG; + + /* valid yuv image */ + } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1) + || (src.y & 0x1) || (src.w & 0x1) + || (src.h & 0x1))) { + SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u,\"\ + %ux%u\n", src.x, src.y, src.w, src.h); ret = -EINVAL; - } - } else if (!(psde->features & SDE_SSPP_SCALER) && - ((src.w != dst.w) || (src.h != dst.h))) { - SDE_ERROR_PLANE(psde, - "pipe doesn't support scaling %ux%u->%ux%u\n", - src.w, src.h, dst.w, dst.h); - ret = -EINVAL; + /* min dst support */ + } else if (dst.w < 0x1 || dst.h < 0x1) { + SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u,\"\ + %ux%u\n", dst.x, dst.y, dst.w, dst.h); + ret = -EINVAL; - /* check decimated source width */ - } else if (src_deci_w > max_linewidth) { - SDE_ERROR_PLANE(psde, - "invalid src w:%u, deci w:%u, line w:%u\n", - src.w, src_deci_w, max_linewidth); - ret = -E2BIG; + /* decimation validation */ + } else if (deci_w || deci_h) { + if ((deci_w > pp->pipe_sblk->maxhdeciexp) || + (deci_h > pp->pipe_sblk->maxvdeciexp)) { + SDE_ERROR_PLANE(psde, + "too much decimation requested\n"); + ret = -EINVAL; + } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) { + SDE_ERROR_PLANE(psde, + "decimation requires linear fetch\n"); + ret = -EINVAL; + } - /* check max scaler capability */ - } else if (((src_deci_w * max_upscale) < dst.w) || - ((src_deci_h * max_upscale) < dst.h) || - ((dst.w * max_downscale) < src_deci_w) || - ((dst.h * max_downscale) < src_deci_h)) { - SDE_ERROR_PLANE(psde, - "too much scaling requested %ux%u->%ux%u\n", - src_deci_w, src_deci_h, dst.w, dst.h); - ret = -E2BIG; + } else if (!(pp->features & SDE_SSPP_SCALER) && + ((src.w != dst.w) || (src.h != dst.h))) { + SDE_ERROR_PLANE(psde, + "pipe doesn't support scaling %ux%u->%ux%u\n", + src.w, src.h, dst.w, dst.h); + ret = -EINVAL; + + /* check decimated source width */ + } else if (src_deci_w > max_linewidth * num_of_phy_planes) { + SDE_ERROR_PLANE(psde, + "invalid src w:%u, deci w:%u, line w:%u, num_phy_planes:%u\n", + src.w, src_deci_w, max_linewidth, + num_of_phy_planes); + ret = -E2BIG; + + /* check max scaler capability */ + } else if (((src_deci_w * max_upscale) < dst.w) || + ((src_deci_h * max_upscale) < dst.h) || + ((dst.w * max_downscale) < src_deci_w) || + ((dst.h * max_downscale) < src_deci_h)) { + SDE_ERROR_PLANE(psde, + "too much scaling requested %ux%u->%ux%u\n", + src_deci_w, src_deci_h, dst.w, dst.h); + ret = -E2BIG; + } } modeset_update: @@ -1519,6 +1592,7 @@ exit: void sde_plane_flush(struct drm_plane *plane) { struct sde_plane *psde; + struct sde_phy_plane *pp; if (!plane) { SDE_ERROR("invalid plane\n"); @@ -1531,14 +1605,17 @@ void sde_plane_flush(struct drm_plane *plane) * These updates have to be done immediately before the plane flush * timing, and may not be moved to the atomic_update/mode_set functions. */ - if (psde->is_error) + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (psde->is_error) /* force white frame with 0% alpha pipe output on error */ - _sde_plane_color_fill(psde, 0xFFFFFF, 0x0); - else if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG) - /* force 100% alpha */ - _sde_plane_color_fill(psde, psde->color_fill, 0xFF); - else if (psde->pipe_hw && psde->csc_ptr && psde->pipe_hw->ops.setup_csc) - psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr); + _sde_plane_color_fill(pp, 0xFFFFFF, 0x0); + else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) + /* force 100% alpha */ + _sde_plane_color_fill(pp, pp->color_fill, 0xFF); + else if (pp->pipe_hw && pp->csc_ptr && + pp->pipe_hw->ops.setup_csc) + pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr); + } /* flag h/w flush complete */ if (plane->state) @@ -1598,19 +1675,48 @@ static void _sde_plane_install_properties(struct drm_plane *plane, int zpos_max = 255; int zpos_def = 0; char feature_name[256]; + struct sde_phy_plane *pp; + uint32_t features = 0xFFFFFFFF, nformats = 64; + u32 maxlinewidth = -1, maxupscale = -1, maxdwnscale = -1; + u32 maxhdeciexp = -1, maxvdeciexp = -1; if (!plane || !psde) { SDE_ERROR("invalid plane\n"); return; - } else if (!psde->pipe_hw || !psde->pipe_sblk) { - SDE_ERROR("invalid plane, pipe_hw %d pipe_sblk %d\n", - psde->pipe_hw != 0, psde->pipe_sblk != 0); - return; - } else if (!catalog) { + } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (!pp->pipe_hw || !pp->pipe_sblk) { + SDE_ERROR("invalid phy_plane, pipe_hw %d\"\ + pipe_sblk %d\n", pp->pipe_hw != NULL, + pp->pipe_sblk != NULL); + return; + } + } + if (!catalog) { SDE_ERROR("invalid catalog\n"); return; } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + /* Get common features for all pipes */ + features &= pp->features; + if (nformats > pp->nformats) { + nformats = pp->nformats; + format_list = pp->pipe_sblk->format_list; + } + if (maxlinewidth < pp->pipe_sblk->maxlinewidth) + maxlinewidth = pp->pipe_sblk->maxlinewidth; + if (maxupscale < pp->pipe_sblk->maxupscale) + maxupscale = pp->pipe_sblk->maxupscale; + if (maxdwnscale < pp->pipe_sblk->maxdwnscale) + maxdwnscale = pp->pipe_sblk->maxdwnscale; + if (maxhdeciexp < pp->pipe_sblk->maxhdeciexp) + maxhdeciexp = pp->pipe_sblk->maxhdeciexp; + if (maxvdeciexp < pp->pipe_sblk->maxvdeciexp) + maxvdeciexp = pp->pipe_sblk->maxvdeciexp; + break; + } + if (sde_is_custom_client()) { if (catalog->mixer_count && catalog->mixer && catalog->mixer[0].sblk->maxblendstages) { @@ -1633,19 +1739,24 @@ static void _sde_plane_install_properties(struct drm_plane *plane, msm_property_install_range(&psde->property_info, "input_fence", 0x0, 0, INR_OPEN_MAX, 0, PLANE_PROP_INPUT_FENCE); - if (psde->pipe_sblk->maxhdeciexp) { - msm_property_install_range(&psde->property_info, "h_decimate", - 0x0, 0, psde->pipe_sblk->maxhdeciexp, 0, - PLANE_PROP_H_DECIMATE); - } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_sblk->maxhdeciexp) { + msm_property_install_range(&psde->property_info, + "h_decimate", 0x0, 0, + pp->pipe_sblk->maxhdeciexp, 0, + PLANE_PROP_H_DECIMATE); + } - if (psde->pipe_sblk->maxvdeciexp) { - msm_property_install_range(&psde->property_info, "v_decimate", - 0x0, 0, psde->pipe_sblk->maxvdeciexp, 0, + if (pp->pipe_sblk->maxvdeciexp) { + msm_property_install_range(&psde->property_info, + "v_decimate", 0x0, 0, + pp->pipe_sblk->maxvdeciexp, 0, PLANE_PROP_V_DECIMATE); + } + break; } - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + if (features & BIT(SDE_SSPP_SCALER_QSEED3)) { msm_property_install_volatile_range(&psde->property_info, "scaler_v2", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2); msm_property_install_blob(&psde->property_info, "lut_ed", 0, @@ -1654,38 +1765,38 @@ static void _sde_plane_install_properties(struct drm_plane *plane, PLANE_PROP_SCALER_LUT_CIR); msm_property_install_blob(&psde->property_info, "lut_sep", 0, PLANE_PROP_SCALER_LUT_SEP); - } else if (psde->features & SDE_SSPP_SCALER) { + } else if (features & SDE_SSPP_SCALER) { msm_property_install_volatile_range(&psde->property_info, "scaler_v1", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V1); } - if (psde->features & BIT(SDE_SSPP_CSC)) { + if (features & BIT(SDE_SSPP_CSC)) { msm_property_install_volatile_range(&psde->property_info, "csc_v1", 0x0, 0, ~0, 0, PLANE_PROP_CSC_V1); } - if (psde->features & BIT(SDE_SSPP_HSIC)) { + if (features & BIT(SDE_SSPP_HSIC)) { snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_HUE_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_HUE_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SATURATION_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_SATURATION_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_VALUE_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_VALUE_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_CONTRAST_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_CONTRAST_ADJUST); @@ -1701,9 +1812,13 @@ static void _sde_plane_install_properties(struct drm_plane *plane, msm_property_install_enum(&psde->property_info, "src_config", 0x0, 1, e_src_config, ARRAY_SIZE(e_src_config), PLANE_PROP_SRC_CONFIG); - if (psde->pipe_hw->ops.setup_solidfill) - msm_property_install_range(&psde->property_info, "color_fill", - 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_COLOR_FILL); + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw->ops.setup_solidfill) + msm_property_install_range(&psde->property_info, + "color_fill", 0, 0, 0xFFFFFFFF, 0, + PLANE_PROP_COLOR_FILL); + break; + } info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL); if (!info) { @@ -1715,7 +1830,6 @@ static void _sde_plane_install_properties(struct drm_plane *plane, DRM_MODE_PROP_IMMUTABLE, PLANE_PROP_INFO); sde_kms_info_reset(info); - format_list = psde->pipe_sblk->format_list; if (format_list) { sde_kms_info_start(info, "pixel_formats"); while (format_list->fourcc_format) { @@ -1727,51 +1841,49 @@ static void _sde_plane_install_properties(struct drm_plane *plane, sde_kms_info_stop(info); } - sde_kms_info_add_keyint(info, "max_linewidth", - psde->pipe_sblk->maxlinewidth); - sde_kms_info_add_keyint(info, "max_upscale", - psde->pipe_sblk->maxupscale); - sde_kms_info_add_keyint(info, "max_downscale", - psde->pipe_sblk->maxdwnscale); - sde_kms_info_add_keyint(info, "max_horizontal_deci", - psde->pipe_sblk->maxhdeciexp); - sde_kms_info_add_keyint(info, "max_vertical_deci", - psde->pipe_sblk->maxvdeciexp); + sde_kms_info_add_keyint(info, "max_linewidth", maxlinewidth); + sde_kms_info_add_keyint(info, "max_upscale", maxupscale); + sde_kms_info_add_keyint(info, "max_downscale", maxdwnscale); + sde_kms_info_add_keyint(info, "max_horizontal_deci", maxhdeciexp); + sde_kms_info_add_keyint(info, "max_vertical_deci", maxvdeciexp); msm_property_set_blob(&psde->property_info, &psde->blob_info, info->data, info->len, PLANE_PROP_INFO); kfree(info); - if (psde->features & BIT(SDE_SSPP_MEMCOLOR)) { + if (features & BIT(SDE_SSPP_MEMCOLOR)) { snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SKIN_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_SKIN_COLOR); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SKY_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_SKY_COLOR); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_FOLIAGE_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_FOLIAGE_COLOR); } } -static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) +static inline void _sde_plane_set_csc_v1(struct sde_phy_plane *pp, + void *usr_ptr) { struct sde_drm_csc_v1 csc_v1; + struct sde_plane *psde; int i; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - psde->csc_usr_ptr = NULL; + pp->csc_usr_ptr = NULL; if (!usr_ptr) { SDE_DEBUG_PLANE(psde, "csc data removed\n"); return; @@ -1784,30 +1896,33 @@ static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) /* populate from user space */ for (i = 0; i < SDE_CSC_MATRIX_COEFF_SIZE; ++i) - psde->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16; + pp->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16; for (i = 0; i < SDE_CSC_BIAS_SIZE; ++i) { - psde->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i]; - psde->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i]; + pp->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i]; + pp->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i]; } for (i = 0; i < SDE_CSC_CLAMP_SIZE; ++i) { - psde->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i]; - psde->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i]; + pp->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i]; + pp->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i]; } - psde->csc_usr_ptr = &psde->csc_cfg; + pp->csc_usr_ptr = &pp->csc_cfg; } -static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) +static inline void _sde_plane_set_scaler_v1(struct sde_phy_plane *pp, + void *usr) { struct sde_drm_scaler_v1 scale_v1; struct sde_hw_pixel_ext *pe; + struct sde_plane *psde; int i; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - psde->pixel_ext_usr = false; + pp->pixel_ext_usr = false; if (!usr) { SDE_DEBUG_PLANE(psde, "scale data removed\n"); return; @@ -1819,7 +1934,7 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) } /* populate from user space */ - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); for (i = 0; i < SDE_MAX_PLANES; i++) { pe->init_phase_x[i] = scale_v1.init_phase_x[i]; @@ -1844,26 +1959,28 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) pe->roi_h[i] = scale_v1.pe.num_ext_pxls_tb[i]; } - psde->pixel_ext_usr = true; + pp->pixel_ext_usr = true; SDE_DEBUG_PLANE(psde, "user property data copied\n"); } -static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, +static inline void _sde_plane_set_scaler_v2(struct sde_phy_plane *pp, struct sde_plane_state *pstate, void *usr) { struct sde_drm_scaler_v2 scale_v2; struct sde_hw_pixel_ext *pe; int i; struct sde_hw_scaler3_cfg *cfg; + struct sde_plane *psde; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - cfg = psde->scaler3_cfg; - psde->pixel_ext_usr = false; + cfg = pp->scaler3_cfg; + pp->pixel_ext_usr = false; if (!usr) { SDE_DEBUG_PLANE(psde, "scale data removed\n"); return; @@ -1875,7 +1992,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, } /* populate from user space */ - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); cfg->enable = scale_v2.enable; cfg->dir_en = scale_v2.dir_en; @@ -1933,7 +2050,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, pe->btm_rpt[i] = scale_v2.pe.btm_rpt[i]; pe->roi_h[i] = scale_v2.pe.num_ext_pxls_tb[i]; } - psde->pixel_ext_usr = true; + pp->pixel_ext_usr = true; SDE_DEBUG_PLANE(psde, "user property data copied\n"); } @@ -1945,6 +2062,7 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL; struct sde_plane_state *pstate; int idx, ret = -EINVAL; + struct sde_phy_plane *pp; SDE_DEBUG_PLANE(psde, "\n"); @@ -1965,14 +2083,24 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, _sde_plane_set_input_fence(psde, pstate, val); break; case PLANE_PROP_CSC_V1: - _sde_plane_set_csc_v1(psde, (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_csc_v1(pp, (void *)val); + } break; case PLANE_PROP_SCALER_V1: - _sde_plane_set_scaler_v1(psde, (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_scaler_v1(pp, + (void *)val); + } break; case PLANE_PROP_SCALER_V2: - _sde_plane_set_scaler_v2(psde, pstate, - (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_scaler_v2(pp, pstate, + (void *)val); + } break; default: /* nothing to do */ @@ -2019,12 +2147,15 @@ static int sde_plane_atomic_get_property(struct drm_plane *plane, static void sde_plane_destroy(struct drm_plane *plane) { struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL; + struct sde_phy_plane *pp, *n; SDE_DEBUG_PLANE(psde, "\n"); if (psde) { - _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL); - + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + _sde_plane_set_qos_ctrl(pp, + false, SDE_PLANE_QOS_PANIC_CTRL); + } debugfs_remove_recursive(psde->debugfs_root); if (psde->blob_info) @@ -2037,8 +2168,13 @@ static void sde_plane_destroy(struct drm_plane *plane) /* this will destroy the states as well */ drm_plane_cleanup(plane); - if (psde->pipe_hw) - sde_hw_sspp_destroy(psde->pipe_hw); + list_for_each_entry_safe(pp, n, + &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw) + sde_hw_sspp_destroy(pp->pipe_hw); + list_del(&pp->phy_plane_list); + kfree(pp); + } kfree(psde); } @@ -2174,9 +2310,22 @@ static const struct drm_plane_helper_funcs sde_plane_helper_funcs = { .atomic_update = sde_plane_atomic_update, }; -enum sde_sspp sde_plane_pipe(struct drm_plane *plane) +enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index) { - return plane ? to_sde_plane(plane)->pipe : SSPP_NONE; + struct sde_plane *sde_plane = to_sde_plane(plane); + struct sde_phy_plane *pp; + int i = 0; + enum sde_sspp default_sspp = SSPP_NONE; + + list_for_each_entry(pp, &sde_plane->phy_plane_head, phy_plane_list) { + if (i == 0) + default_sspp = pp->pipe; + if (i == index) + return pp->pipe; + i++; + } + + return default_sspp; } static ssize_t _sde_plane_danger_read(struct file *file, @@ -2208,10 +2357,16 @@ static ssize_t _sde_plane_danger_read(struct file *file, static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable) { struct drm_plane *plane; + struct sde_plane *psde; + struct sde_phy_plane *pp; drm_for_each_plane(plane, kms->dev) { if (plane->fb && plane->state) { - sde_plane_danger_signal_ctrl(plane, enable); + psde = to_sde_plane(plane); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + sde_plane_danger_signal_ctrl(pp, enable); + } SDE_DEBUG("plane:%d img:%dx%d ", plane->base.id, plane->fb->width, plane->fb->height); @@ -2229,7 +2384,7 @@ static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable) } static ssize_t _sde_plane_danger_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) + const char __user *user_buf, size_t count, loff_t *ppos) { struct sde_kms *kms = file->private_data; struct sde_mdss_cfg *cfg = kms->catalog; @@ -2271,85 +2426,166 @@ static const struct file_operations sde_plane_danger_enable = { .write = _sde_plane_danger_write, }; -static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms) +static void _sde_plane_init_debugfs(struct sde_plane *psde, + struct sde_kms *kms) { const struct sde_sspp_sub_blks *sblk = 0; const struct sde_sspp_cfg *cfg = 0; + struct sde_phy_plane *pp; + + if (!psde || !kms) { + SDE_ERROR("invalid arg(s), psde %d kms %d\n", + psde != NULL, kms != NULL); + return; + } - if (psde && psde->pipe_hw) - cfg = psde->pipe_hw->cap; - if (cfg) + /* create overall sub-directory for the pipe */ + psde->debugfs_root = debugfs_create_dir(psde->pipe_name, + sde_debugfs_get_root(kms)); + if (!psde->debugfs_root) + return; + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + debugfs_create_u32("pipe", S_IRUGO | S_IWUSR, + psde->debugfs_root, &pp->pipe); + + if (!pp->pipe_hw || !pp->pipe_hw->cap || + !pp->pipe_hw->cap->sblk) + continue; + cfg = pp->pipe_hw->cap; sblk = cfg->sblk; - if (kms && sblk) { - /* create overall sub-directory for the pipe */ - psde->debugfs_root = - debugfs_create_dir(psde->pipe_name, - sde_debugfs_get_root(kms)); - if (psde->debugfs_root) { - /* don't error check these */ - debugfs_create_x32("features", S_IRUGO | S_IWUSR, - psde->debugfs_root, &psde->features); - - /* add register dump support */ - sde_debugfs_setup_regset32(&psde->debugfs_src, - sblk->src_blk.base + cfg->base, - sblk->src_blk.len, - kms); - sde_debugfs_create_regset32("src_blk", S_IRUGO, - psde->debugfs_root, &psde->debugfs_src); - - sde_debugfs_setup_regset32(&psde->debugfs_scaler, - sblk->scaler_blk.base + cfg->base, - sblk->scaler_blk.len, - kms); - sde_debugfs_create_regset32("scaler_blk", S_IRUGO, - psde->debugfs_root, - &psde->debugfs_scaler); - - sde_debugfs_setup_regset32(&psde->debugfs_csc, - sblk->csc_blk.base + cfg->base, - sblk->csc_blk.len, - kms); - sde_debugfs_create_regset32("csc_blk", S_IRUGO, - psde->debugfs_root, &psde->debugfs_csc); - - debugfs_create_u32("xin_id", - S_IRUGO, - psde->debugfs_root, - (u32 *) &cfg->xin_id); - debugfs_create_u32("clk_ctrl", - S_IRUGO, - psde->debugfs_root, - (u32 *) &cfg->clk_ctrl); - debugfs_create_x32("creq_vblank", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - (u32 *) &sblk->creq_vblank); - debugfs_create_x32("danger_vblank", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - (u32 *) &sblk->danger_vblank); - - debugfs_create_file("disable_danger", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - kms, &sde_plane_danger_enable); + /* don't error check these */ + debugfs_create_x32("features", S_IRUGO | S_IWUSR, + psde->debugfs_root, &pp->features); + + /* add register dump support */ + sde_debugfs_setup_regset32(&psde->debugfs_src, + sblk->src_blk.base + cfg->base, + sblk->src_blk.len, + kms); + sde_debugfs_create_regset32("src_blk", S_IRUGO, + psde->debugfs_root, &psde->debugfs_src); + + sde_debugfs_setup_regset32(&psde->debugfs_scaler, + sblk->scaler_blk.base + cfg->base, + sblk->scaler_blk.len, + kms); + sde_debugfs_create_regset32("scaler_blk", S_IRUGO, + psde->debugfs_root, + &psde->debugfs_scaler); + + sde_debugfs_setup_regset32(&psde->debugfs_csc, + sblk->csc_blk.base + cfg->base, + sblk->csc_blk.len, + kms); + sde_debugfs_create_regset32("csc_blk", S_IRUGO, + psde->debugfs_root, &psde->debugfs_csc); + + debugfs_create_u32("xin_id", + S_IRUGO, + psde->debugfs_root, + (u32 *) &cfg->xin_id); + debugfs_create_u32("clk_ctrl", + S_IRUGO, + psde->debugfs_root, + (u32 *) &cfg->clk_ctrl); + debugfs_create_x32("creq_vblank", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + (u32 *) &sblk->creq_vblank); + debugfs_create_x32("danger_vblank", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + (u32 *) &sblk->danger_vblank); + + debugfs_create_file("disable_danger", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + kms, &sde_plane_danger_enable); + + break; + } +} + +static int _sde_init_phy_plane(struct sde_kms *sde_kms, + struct sde_plane *psde, uint32_t pipe, uint32_t index, + struct sde_phy_plane *pp) +{ + int rc = 0; + + pp->pipe_hw = sde_rm_get_hw_by_id(&sde_kms->rm, + SDE_HW_BLK_SSPP, pipe); + if (!pp->pipe_hw) { + SDE_ERROR("Not found resource for id=%d\n", pipe); + rc = -EINVAL; + goto end; + } else if (!pp->pipe_hw->cap || !pp->pipe_hw->cap->sblk) { + SDE_ERROR("[%u]SSPP returned invalid cfg\n", pipe); + rc = -EINVAL; + goto end; + } + + /* cache features mask for later */ + pp->features = pp->pipe_hw->cap->features; + pp->pipe_sblk = pp->pipe_hw->cap->sblk; + if (!pp->pipe_sblk) { + SDE_ERROR("invalid sblk on pipe %d\n", pipe); + rc = -EINVAL; + goto end; + } + + if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + pp->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg), + GFP_KERNEL); + if (!pp->scaler3_cfg) { + SDE_ERROR("[%u]failed to allocate scale struct\n", + pipe); + rc = -ENOMEM; + goto end; } } + + /* add plane to DRM framework */ + pp->nformats = sde_populate_formats( + pp->pipe_sblk->format_list, + pp->formats, + NULL, + ARRAY_SIZE(pp->formats)); + + if (!pp->nformats) { + SDE_ERROR("[%u]no valid formats for plane\n", pipe); + if (pp->scaler3_cfg) + kzfree(pp->scaler3_cfg); + + rc = -EINVAL; + goto end; + } + + pp->sde_plane = psde; + pp->pipe = pipe; + pp->index = index; + +end: + return rc; } /* initialize plane */ struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe, bool primary_plane, - unsigned long possible_crtcs) + unsigned long possible_crtcs, bool vp_enabled) { struct drm_plane *plane = NULL; struct sde_plane *psde; + struct sde_phy_plane *pp, *n; struct msm_drm_private *priv; struct sde_kms *kms; enum drm_plane_type type; int ret = -EINVAL; + struct sde_vp_cfg *vp; + struct sde_vp_sub_blks *vp_sub; + uint32_t features = 0xFFFFFFFF, nformats = 64, formats[64]; + uint32_t index = 0; if (!dev) { SDE_ERROR("[%u]device is NULL\n", pipe); @@ -2383,60 +2619,77 @@ struct drm_plane *sde_plane_init(struct drm_device *dev, /* cache local stuff for later */ plane = &psde->base; - psde->pipe = pipe; psde->aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE]; - /* initialize underlying h/w driver */ - psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, kms->catalog); - if (IS_ERR(psde->pipe_hw)) { - SDE_ERROR("[%u]SSPP init failed\n", pipe); - ret = PTR_ERR(psde->pipe_hw); - goto clean_plane; - } else if (!psde->pipe_hw->cap || !psde->pipe_hw->cap->sblk) { - SDE_ERROR("[%u]SSPP init returned invalid cfg\n", pipe); - goto clean_sspp; - } + INIT_LIST_HEAD(&psde->phy_plane_head); - /* cache features mask for later */ - psde->features = psde->pipe_hw->cap->features; - psde->pipe_sblk = psde->pipe_hw->cap->sblk; - if (!psde->pipe_sblk) { - SDE_ERROR("[%u]invalid sblk\n", pipe); - goto clean_sspp; - } + /* initialize underlying h/w driver */ + if (vp_enabled) { + vp = &(kms->catalog->vp[pipe]); + list_for_each_entry(vp_sub, &vp->sub_blks, pipeid_list) { + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + SDE_ERROR("out of memory\n"); + ret = -ENOMEM; + goto clean_plane; + } - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { - psde->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg), - GFP_KERNEL); - if (!psde->scaler3_cfg) { - SDE_ERROR("[%u]failed to allocate scale struct\n", - pipe); + ret = _sde_init_phy_plane(kms, psde, vp_sub->sspp_id, + index, pp); + if (ret) { + SDE_ERROR("_sde_init_phy_plane error vp=%d\n", + pipe); + kfree(pp); + ret = -EINVAL; + goto clean_plane; + } + /* Get common features for all pipes */ + features &= pp->features; + if (nformats > pp->nformats) { + nformats = pp->nformats; + memcpy(formats, pp->formats, + sizeof(formats)); + } + list_add_tail(&pp->phy_plane_list, + &psde->phy_plane_head); + index++; + psde->num_of_phy_planes++; + } + } else { + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + SDE_ERROR("out of memory\n"); ret = -ENOMEM; - goto clean_sspp; + goto clean_plane; } - } - - /* add plane to DRM framework */ - psde->nformats = sde_populate_formats(psde->pipe_sblk->format_list, - psde->formats, - 0, - ARRAY_SIZE(psde->formats)); - if (!psde->nformats) { - SDE_ERROR("[%u]no valid formats for plane\n", pipe); - goto clean_sspp; + ret = _sde_init_phy_plane(kms, psde, pipe, index, pp); + if (ret) { + SDE_ERROR("_sde_init_phy_plane error id=%d\n", + pipe); + kfree(pp); + ret = -EINVAL; + goto clean_plane; + } + features = pp->features; + nformats = pp->nformats; + memcpy(formats, pp->formats, + sizeof(uint32_t) * 64); + list_add_tail(&pp->phy_plane_list, + &psde->phy_plane_head); + psde->num_of_phy_planes++; } - if (psde->features & BIT(SDE_SSPP_CURSOR)) + if (features & BIT(SDE_SSPP_CURSOR)) type = DRM_PLANE_TYPE_CURSOR; else if (primary_plane) type = DRM_PLANE_TYPE_PRIMARY; else type = DRM_PLANE_TYPE_OVERLAY; ret = drm_universal_plane_init(dev, plane, possible_crtcs, - &sde_plane_funcs, psde->formats, psde->nformats, type); + &sde_plane_funcs, formats, nformats, type); if (ret) - goto clean_sspp; + goto clean_plane; /* success! finalize initialization */ drm_plane_helper_add(plane, &sde_plane_helper_funcs); @@ -2458,14 +2711,20 @@ struct drm_plane *sde_plane_init(struct drm_device *dev, DRM_INFO("%s created for pipe %u\n", psde->pipe_name, pipe); return plane; -clean_sspp: - if (psde && psde->pipe_hw) - sde_hw_sspp_destroy(psde->pipe_hw); - - if (psde && psde->scaler3_cfg) - kfree(psde->scaler3_cfg); clean_plane: - kfree(psde); + if (psde) { + list_for_each_entry_safe(pp, n, + &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw) + sde_hw_sspp_destroy(pp->pipe_hw); + + kfree(pp->scaler3_cfg); + list_del(&pp->phy_plane_list); + kfree(pp); + } + kfree(psde); + } + exit: return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h index 1514f633c61e..7b91822d4cde 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.h +++ b/drivers/gpu/drm/msm/sde/sde_plane.h @@ -59,9 +59,10 @@ struct sde_plane_state { /** * sde_plane_pipe - return sspp identifier for the given plane * @plane: Pointer to DRM plane object + * @index: Plane index * Returns: sspp identifier of the given plane */ -enum sde_sspp sde_plane_pipe(struct drm_plane *plane); +enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index); /** * sde_plane_flush - final plane operations before commit flush @@ -75,10 +76,11 @@ void sde_plane_flush(struct drm_plane *plane); * @pipe: sde hardware pipe identifier * @primary_plane: true if this pipe is primary plane for crtc * @possible_crtcs: bitmask of crtc that can be attached to the given pipe + * @vp_enabled: Flag indicating if virtual planes enabled */ struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe, bool primary_plane, - unsigned long possible_crtcs); + unsigned long possible_crtcs, bool vp_enabled); /** * sde_plane_wait_input_fence - wait for input fence object diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index fca0768e2734..fe4b73b4ffea 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -23,6 +23,7 @@ #include "sde_hw_wb.h" #include "sde_encoder.h" #include "sde_connector.h" +#include "sde_hw_sspp.h" #define RESERVED_BY_OTHER(h, r) \ ((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) @@ -197,6 +198,33 @@ bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *i) return false; } +void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id) +{ + struct list_head *blk_list; + struct sde_rm_hw_blk *blk; + void *hw = NULL; + + if (!rm || type >= SDE_HW_BLK_MAX) { + SDE_ERROR("invalid rm\n"); + return hw; + } + + blk_list = &rm->hw_blks[type]; + + list_for_each_entry(blk, blk_list, list) { + if (blk->id == id) { + hw = blk->hw; + SDE_DEBUG("found type %d %s id %d\n", + type, blk->type_name, blk->id); + return hw; + } + } + + SDE_DEBUG("no match, type %d id=%d\n", type, id); + + return hw; +} + static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw) { switch (type) { @@ -222,7 +250,8 @@ static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw) sde_hw_wb_destroy(hw); break; case SDE_HW_BLK_SSPP: - /* SSPPs are not managed by the resource manager */ + sde_hw_sspp_destroy(hw); + break; case SDE_HW_BLK_TOP: /* Top is a singleton, not managed in hw_blks list */ case SDE_HW_BLK_MAX: @@ -310,7 +339,9 @@ static int _sde_rm_hw_blk_create( name = "wb"; break; case SDE_HW_BLK_SSPP: - /* SSPPs are not managed by the resource manager */ + hw = sde_hw_sspp_init(id, (void __iomem *)mmio, cat); + name = "sspp"; + break; case SDE_HW_BLK_TOP: /* Top is a singleton, not managed in hw_blks list */ case SDE_HW_BLK_MAX: @@ -369,6 +400,13 @@ int sde_rm_init(struct sde_rm *rm, goto fail; } + for (i = 0; i < cat->sspp_count; i++) { + rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_SSPP, + cat->sspp[i].id, &cat->sspp[i]); + if (rc) + goto fail; + } + /* Interrogate HW catalog and create tracking items for hw blocks */ for (i = 0; i < cat->mixer_count; i++) { struct sde_lm_cfg *lm = &cat->mixer[i]; diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h index 855b12ce8150..1cc22c5fbbf4 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.h +++ b/drivers/gpu/drm/msm/sde/sde_rm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -184,6 +184,18 @@ void sde_rm_init_hw_iter( */ bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *iter); +/** + * sde_rm_get_hw_by_id - retrieve hw object given hw type and hw id + * Meant to do a single pass through the hardware list to iteratively + * retrieve hardware blocks of a given type and id. + * Function returns the hw resource pointer. + * @rm: SDE Resource Manager handle + * @type: hw type + * @id: hw id + * @Return: hw resource pointer on match found, NULL on no match found + */ +void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id); + /** * sde_rm_check_property_topctl - validate property bitmask before it is set * @val: user's proposed topology control bitmask -- cgit v1.2.3