From ed868466f59c0387282c2110ae804fadadbefb6c Mon Sep 17 00:00:00 2001 From: Amine Najahi Date: Sun, 7 Jun 2020 19:51:11 -0400 Subject: [PATCH] disp: msm: dp: Extend mode filtering to support 8K Currently DP driver determines if a mode is DSC capable based on a DTSI entry and the required number of DSC to support it. This approach does not scale when there is an overlap in DSC requirement between DSI displays and external DP display, thus causing one of the display to report modes that cannot be supported. This change compares the resources reserved for DP driver calculated at initialization time and the currently available ones to determine the correct number of resources that DP driver can use. It also adds DSC and topology filtering logic and moves DSC hardware specific from DP driver to SDE driver. Change-Id: I8e601de33422b7c6d786826f7bfe152c4af8a6b5 Signed-off-by: Amine Najahi --- msm/dp/dp_display.c | 153 ++++++++++++++++++++++++++++++++++----- msm/dp/dp_display.h | 3 + msm/dp/dp_drm.c | 21 +++++- msm/dp/dp_parser.c | 18 +---- msm/dp/dp_parser.h | 4 - msm/msm_drv.c | 26 +++++++ msm/msm_drv.h | 3 + msm/msm_kms.h | 5 +- msm/sde/sde_hw_catalog.c | 14 ++-- msm/sde/sde_hw_catalog.h | 5 +- msm/sde/sde_kms.c | 29 ++++++++ 11 files changed, 231 insertions(+), 50 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 7877058537..bc21379de2 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -2012,15 +2012,18 @@ static int dp_display_set_stream_info(struct dp_display *dp_display, static void dp_display_update_dsc_resources(struct dp_display_private *dp, struct dp_panel *panel, bool enable) { + int rc; u32 dsc_blk_cnt = 0; + struct msm_drm_private *priv = dp->priv; if (panel->pinfo.comp_info.comp_type == MSM_DISPLAY_COMPRESSION_DSC && - (panel->pinfo.comp_info.comp_ratio > 1)) { - dsc_blk_cnt = panel->pinfo.h_active / - dp->parser->max_dp_dsc_input_width_pixs; - if (panel->pinfo.h_active % - dp->parser->max_dp_dsc_input_width_pixs) - dsc_blk_cnt++; + (panel->pinfo.comp_info.comp_ratio > 1)) { + rc = msm_get_dsc_count(priv, panel->pinfo.h_active, + &dsc_blk_cnt); + if (rc) { + DP_ERR("error getting dsc count. rc:%d\n", rc); + return; + } } if (enable) { @@ -2431,10 +2434,74 @@ static int dp_display_validate_mixers(struct msm_drm_private *priv, return rc; } - if (num_lm > avail_res->num_lm || - (num_lm == 2 && !avail_res->num_3dmux)) { - DP_DEBUG("num_lm:%d, req lm:%d 3dmux:%d\n", num_lm, - avail_res->num_lm, avail_res->num_3dmux); + if (num_lm > avail_res->num_lm) { + DP_DEBUG("num lm:%d > available lm:%d\n", num_lm, + avail_res->num_lm); + return -EPERM; + } + + return 0; +} + +static int dp_display_validate_dscs(struct msm_drm_private *priv, + struct dp_panel *dp_panel, struct drm_display_mode *mode, + struct dp_display_mode *dp_mode, + const struct msm_resource_caps_info *avail_res) +{ + int rc; + u32 num_dsc = 0; + bool dsc_capable = dp_mode->capabilities & DP_PANEL_CAPS_DSC; + + if (!dp_panel->dsc_en || !dsc_capable) + return 0; + + rc = msm_get_dsc_count(priv, mode->hdisplay, &num_dsc); + if (rc) { + DP_ERR("error getting dsc count. rc:%d\n", rc); + return rc; + } + + if (num_dsc > avail_res->num_dsc) { + DP_DEBUG("num dsc:%d > available dsc:%d\n", num_dsc, + avail_res->num_dsc); + return -EPERM; + } + + return 0; +} + +static int dp_display_validate_topology(struct dp_display_private *dp, + struct dp_panel *dp_panel, struct drm_display_mode *mode, + struct dp_display_mode *dp_mode, + const struct msm_resource_caps_info *avail_res) +{ + int rc; + struct msm_drm_private *priv = dp->priv; + const u32 dual_lm = 2, quad_lm = 4; + u32 num_lm = 0, num_dsc = 0, num_3dmux = 0; + bool dsc_capable = dp_mode->capabilities & DP_PANEL_CAPS_DSC; + + rc = msm_get_mixer_count(priv, mode, avail_res, &num_lm); + if (rc) { + DP_ERR("error getting mixer count. rc:%d\n", rc); + return rc; + } + + num_3dmux = avail_res->num_3dmux; + + if (dp_panel->dsc_en && dsc_capable) { + rc = msm_get_dsc_count(priv, mode->hdisplay, &num_dsc); + if (rc) { + DP_ERR("error getting dsc count. rc:%d\n", rc); + return rc; + } + } + + /* filter out unsupported DP topologies */ + if ((num_lm == dual_lm && (!num_3dmux && !num_dsc)) || + (num_lm == quad_lm && (num_dsc != 4))) { + DP_DEBUG("invalid topology lm:%d dsc:%d 3dmux:%d intf:1\n", + num_lm, num_dsc, num_3dmux); return -EPERM; } @@ -2554,6 +2621,16 @@ static enum drm_mode_status dp_display_validate_mode( if (rc) goto end; + rc = dp_display_validate_dscs(dp->priv, panel, mode, &dp_mode, + avail_res); + if (rc) + goto end; + + rc = dp_display_validate_topology(dp, dp_panel, mode, + &dp_mode, avail_res); + if (rc) + goto end; + dp_display_validate_mst_connectors(debug, dp_panel, mode, &mode_status, &use_default); if (!use_default) @@ -2568,9 +2645,40 @@ static enum drm_mode_status dp_display_validate_mode( mode_status = MODE_OK; end: mutex_unlock(&dp->session_lock); + DP_DEBUG("[%s] mode is %s\n", mode->name, + (mode_status == MODE_OK) ? "valid" : "invalid"); + return mode_status; } +static int dp_display_get_available_dp_resources(struct dp_display *dp_display, + const struct msm_resource_caps_info *avail_res, + struct msm_resource_caps_info *max_dp_avail_res) +{ + if (!dp_display || !avail_res || !max_dp_avail_res) { + DP_ERR("invalid arguments\n"); + return -EINVAL; + } + + memcpy(max_dp_avail_res, avail_res, + sizeof(struct msm_resource_caps_info)); + + max_dp_avail_res->num_lm = min(avail_res->num_lm, + dp_display->max_mixer_count); + max_dp_avail_res->num_dsc = min(avail_res->num_dsc, + dp_display->max_dsc_count); + + DP_DEBUG("max_lm:%d, avail_lm:%d, dp_avail_lm:%d\n", + dp_display->max_mixer_count, avail_res->num_lm, + max_dp_avail_res->num_lm); + + DP_DEBUG("max_dsc:%d, avail_dsc:%d, dp_avail_dsc:%d\n", + dp_display->max_dsc_count, avail_res->num_dsc, + max_dp_avail_res->num_dsc); + + return 0; +} + static int dp_display_get_modes(struct dp_display *dp, void *panel, struct dp_display_mode *dp_mode) { @@ -2602,6 +2710,7 @@ static void dp_display_convert_to_dp_mode(struct dp_display *dp_display, const struct drm_display_mode *drm_mode, struct dp_display_mode *dp_mode) { + int rc; struct dp_display_private *dp; struct dp_panel *dp_panel; u32 free_dsc_blks = 0, required_dsc_blks = 0; @@ -2616,22 +2725,26 @@ static void dp_display_convert_to_dp_mode(struct dp_display *dp_display, memset(dp_mode, 0, sizeof(*dp_mode)); - free_dsc_blks = dp->parser->max_dp_dsc_blks - + free_dsc_blks = dp_display->max_dsc_count - dp->tot_dsc_blks_in_use + dp_panel->tot_dsc_blks_in_use; - required_dsc_blks = drm_mode->hdisplay / - dp->parser->max_dp_dsc_input_width_pixs; - if (drm_mode->hdisplay % dp->parser->max_dp_dsc_input_width_pixs) - required_dsc_blks++; + + rc = msm_get_dsc_count(dp->priv, drm_mode->hdisplay, + &required_dsc_blks); + if (rc) { + DP_ERR("error getting dsc count. rc:%d\n", rc); + return; + } if (free_dsc_blks >= required_dsc_blks) dp_mode->capabilities |= DP_PANEL_CAPS_DSC; if (dp_mode->capabilities & DP_PANEL_CAPS_DSC) - DP_DEBUG("in_use:%d, max:%d, free:%d, req:%d, caps:0x%x, width:%d\n", - dp->tot_dsc_blks_in_use, dp->parser->max_dp_dsc_blks, - free_dsc_blks, required_dsc_blks, dp_mode->capabilities, - dp->parser->max_dp_dsc_input_width_pixs); + DP_DEBUG("in_use:%d, max:%d, free:%d, req:%d, caps:0x%x\n", + dp->tot_dsc_blks_in_use, + dp_display->max_dsc_count, + free_dsc_blks, required_dsc_blks, + dp_mode->capabilities); dp_panel->convert_to_dp_mode(dp_panel, drm_mode, dp_mode); } @@ -3231,6 +3344,8 @@ static int dp_display_probe(struct platform_device *pdev) g_dp_display->wakeup_phy_layer = dp_display_wakeup_phy_layer; g_dp_display->set_colorspace = dp_display_setup_colospace; + g_dp_display->get_available_dp_resources = + dp_display_get_available_dp_resources; rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index 5bb2a74ad4..0ea71cc80f 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -127,6 +127,9 @@ struct dp_display { struct drm_connector *connector, char *pps_cmd); void (*wakeup_phy_layer)(struct dp_display *dp_display, bool wakeup); + int (*get_available_dp_resources)(struct dp_display *dp_display, + const struct msm_resource_caps_info *avail_res, + struct msm_resource_caps_info *max_dp_avail_res); }; #if IS_ENABLED(CONFIG_DRM_MSM_DP) diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index dd9b80fd76..43bd72ad68 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -383,6 +383,7 @@ int dp_connector_get_mode_info(struct drm_connector *connector, struct dp_display_mode dp_mode; struct dp_display *dp_disp = display; struct msm_drm_private *priv; + struct msm_resource_caps_info avail_dp_res; int rc = 0; if (!drm_mode || !mode_info || !avail_res || @@ -400,7 +401,14 @@ int dp_connector_get_mode_info(struct drm_connector *connector, topology = &mode_info->topology; - rc = msm_get_mixer_count(priv, drm_mode, avail_res, + rc = dp_disp->get_available_dp_resources(dp_disp, avail_res, + &avail_dp_res); + if (rc) { + DP_ERR("error getting max dp resources. rc:%d\n", rc); + return rc; + } + + rc = msm_get_mixer_count(priv, drm_mode, &avail_dp_res, &topology->num_lm); if (rc) { DP_ERR("error getting mixer count. rc:%d\n", rc); @@ -644,8 +652,10 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode, void *display, const struct msm_resource_caps_info *avail_res) { + int rc = 0; struct dp_display *dp_disp; struct sde_connector *sde_conn; + struct msm_resource_caps_info avail_dp_res; if (!mode || !display || !connector) { DP_ERR("invalid params\n"); @@ -661,8 +671,15 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, dp_disp = display; mode->vrefresh = drm_mode_vrefresh(mode); + rc = dp_disp->get_available_dp_resources(dp_disp, avail_res, + &avail_dp_res); + if (rc) { + DP_ERR("error getting max dp resources. rc:%d\n", rc); + return MODE_ERROR; + } + return dp_disp->validate_mode(dp_disp, sde_conn->drv_panel, - mode, avail_res); + mode, &avail_dp_res); } int dp_connector_update_pps(struct drm_connector *connector, diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index 21ac2562b9..dbe11c37a0 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -722,27 +722,13 @@ static int dp_parser_mst(struct dp_parser *parser) static void dp_parser_dsc(struct dp_parser *parser) { - int rc; struct device *dev = &parser->pdev->dev; parser->dsc_feature_enable = of_property_read_bool(dev->of_node, "qcom,dsc-feature-enable"); - rc = of_property_read_u32(dev->of_node, - "qcom,max-dp-dsc-blks", &parser->max_dp_dsc_blks); - if (rc || !parser->max_dp_dsc_blks) - parser->dsc_feature_enable = false; - - rc = of_property_read_u32(dev->of_node, - "qcom,max-dp-dsc-input-width-pixs", - &parser->max_dp_dsc_input_width_pixs); - if (rc || !parser->max_dp_dsc_input_width_pixs) - parser->dsc_feature_enable = false; - - DP_DEBUG("dsc parsing successful. dsc:%d, blks:%d, width:%d\n", - parser->dsc_feature_enable, - parser->max_dp_dsc_blks, - parser->max_dp_dsc_input_width_pixs); + DP_DEBUG("dsc parsing successful. dsc:%d\n", + parser->dsc_feature_enable); } static void dp_parser_fec(struct dp_parser *parser) diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index c5d0b186a5..8ba2e5dc7e 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -195,8 +195,6 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) * @gpio_aux_switch: presence GPIO AUX switch status * @dsc_feature_enable: DSC feature enable status * @fec_feature_enable: FEC feature enable status - * @max_dp_dsc_blks: maximum DSC blks for DP interface - * @max_dp_dsc_input_width_pixs: Maximum input width for DSC block * @has_widebus: widebus (2PPC) feature eanble status *@mst_fixed_port: mst port_num reserved for fixed topology * @parse: function to be called by client to parse device tree. @@ -225,8 +223,6 @@ struct dp_parser { bool fec_feature_enable; bool has_widebus; bool gpio_aux_switch; - u32 max_dp_dsc_blks; - u32 max_dp_dsc_input_width_pixs; bool lphw_hpd; u32 mst_fixed_port[MAX_DP_MST_STREAMS]; diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 5e725f481f..3382a4f112 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -1938,6 +1938,32 @@ int msm_get_mixer_count(struct msm_drm_private *priv, return funcs->get_mixer_count(priv->kms, mode, res, num_lm); } +int msm_get_dsc_count(struct msm_drm_private *priv, + u32 hdisplay, u32 *num_dsc) +{ + struct msm_kms *kms; + const struct msm_kms_funcs *funcs; + + if (!priv) { + DRM_ERROR("invalid drm private struct\n"); + return -EINVAL; + } + + kms = priv->kms; + if (!kms) { + DRM_ERROR("invalid msm kms struct\n"); + return -EINVAL; + } + + funcs = kms->funcs; + if (!funcs || !funcs->get_dsc_count) { + DRM_ERROR("invalid function pointers\n"); + return -EINVAL; + } + + return funcs->get_dsc_count(priv->kms, hdisplay, num_dsc); +} + static int msm_drm_bind(struct device *dev) { return msm_drm_component_init(dev); diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 88990df459..5dd3a2db95 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -1351,6 +1351,9 @@ int msm_get_mixer_count(struct msm_drm_private *priv, const struct drm_display_mode *mode, const struct msm_resource_caps_info *res, u32 *num_lm); +int msm_get_dsc_count(struct msm_drm_private *priv, + u32 hdisplay, u32 *num_dsc); + int msm_get_src_bpc(int chroma_format, int bpc); #endif /* __MSM_DRV_H__ */ diff --git a/msm/msm_kms.h b/msm/msm_kms.h index e05349dcb6..b4b8a0d2c5 100644 --- a/msm/msm_kms.h +++ b/msm/msm_kms.h @@ -120,10 +120,13 @@ struct msm_kms_funcs { int (*cont_splash_config)(struct msm_kms *kms); /* check for continuous splash status */ bool (*check_for_splash)(struct msm_kms *kms); - /* topology information */ + /* topology lm information */ int (*get_mixer_count)(const struct msm_kms *kms, const struct drm_display_mode *mode, const struct msm_resource_caps_info *res, u32 *num_lm); + /* topology dsc information */ + int (*get_dsc_count)(const struct msm_kms *kms, + u32 hdisplay, u32 *num_dsc); }; struct msm_kms { diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 6fbebd0051..3ff265298f 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -35,7 +35,7 @@ /* max table size for dts property lists, increase if tables grow larger */ #define MAX_SDE_DT_TABLE_SIZE 64 -/* default line width for sspp, mixer, ds (input), wb */ +/* default line width for sspp, mixer, ds (input), dsc, wb */ #define DEFAULT_SDE_LINE_WIDTH 2048 /* default output line width for ds */ @@ -101,7 +101,6 @@ #define MAX_DISPLAY_HEIGHT 5760 #define MIN_DISPLAY_HEIGHT 0 #define MIN_DISPLAY_WIDTH 0 -#define MAX_LM_PER_DISPLAY 2 /* maximum XIN halt timeout in usec */ #define VBIF_XIN_HALT_TIMEOUT 0x4000 @@ -324,6 +323,7 @@ enum { DSC_CTL, DSC_CTL_LEN, DSC_422, + DSC_LINEWIDTH, DSC_PROP_MAX, }; @@ -790,7 +790,8 @@ static struct sde_prop_type dsc_prop[] = { {DSC_ENC_LEN, "qcom,sde-dsc-enc-size", false, PROP_TYPE_U32}, {DSC_CTL, "qcom,sde-dsc-ctl", false, PROP_TYPE_U32_ARRAY}, {DSC_CTL_LEN, "qcom,sde-dsc-ctl-size", false, PROP_TYPE_U32}, - {DSC_422, "qcom,sde-dsc-native422-supp", false, PROP_TYPE_U32_ARRAY} + {DSC_422, "qcom,sde-dsc-native422-supp", false, PROP_TYPE_U32_ARRAY}, + {DSC_LINEWIDTH, "qcom,sde-dsc-linewidth", false, PROP_TYPE_U32}, }; static struct sde_prop_type vdc_prop[] = { @@ -2932,6 +2933,10 @@ static int sde_dsc_parse_dt(struct device_node *np, if (rc) goto end; + sde_cfg->max_dsc_width = prop_exists[DSC_LINEWIDTH] ? + PROP_VALUE_ACCESS(prop_value, DSC_LINEWIDTH, 0) : + DEFAULT_SDE_LINE_WIDTH; + for (i = 0; i < off_count; i++) { dsc = sde_cfg->dsc + i; @@ -4734,9 +4739,6 @@ static int _sde_hardware_post_caps(struct sde_mdss_cfg *sde_cfg, &sde_cfg->sspp[i].features); } - /* this should be updated based on HW rev in future */ - sde_cfg->max_lm_per_display = MAX_LM_PER_DISPLAY; - if (max_horz_deci) sde_cfg->max_display_width = sde_cfg->max_sspp_linewidth * max_horz_deci; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 33f2e49d90..063e1d262f 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -1362,13 +1362,14 @@ struct sde_perf_cfg { * @vig_sspp_linewidth max vig source pipe line width support. * @scaling_linewidth max vig source pipe linewidth for scaling usecases * @max_mixer_width max layer mixer line width support. + * @max_dsc_width max dsc line width support. * @max_mixer_blendstages max layer mixer blend stages or * supported z order * @max_wb_linewidth max writeback line width support. * @max_wb_linewidth_linear max writeback line width for linear formats. * @max_display_width maximum display width support. * @max_display_height maximum display height support. - * @max_lm_per_display maximum layer mixer per display + * @min_display_width minimum display width support. * @min_display_height minimum display height support. * @qseed_type qseed2 or qseed3 support. @@ -1433,6 +1434,7 @@ struct sde_mdss_cfg { u32 vig_sspp_linewidth; u32 scaling_linewidth; u32 max_mixer_width; + u32 max_dsc_width; u32 max_mixer_blendstages; u32 max_wb_linewidth; u32 max_wb_linewidth_linear; @@ -1441,7 +1443,6 @@ struct sde_mdss_cfg { u32 max_display_height; u32 min_display_width; u32 min_display_height; - u32 max_lm_per_display; u32 qseed_type; u32 csc_type; diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index c1602ab3f7..e12e2449ac 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2533,6 +2533,34 @@ error: return rc; } +static int sde_kms_get_dsc_count(const struct msm_kms *kms, + u32 hdisplay, u32 *num_dsc) +{ + struct sde_kms *sde_kms; + uint32_t max_dsc_width; + + if (!num_dsc) { + SDE_ERROR("invalid num_dsc pointer\n"); + return -EINVAL; + } + + *num_dsc = 0; + if (!kms || !hdisplay) { + SDE_ERROR("invalid input args\n"); + return -EINVAL; + } + + sde_kms = to_sde_kms(kms); + max_dsc_width = sde_kms->catalog->max_dsc_width; + *num_dsc = DIV_ROUND_UP(hdisplay, max_dsc_width); + + SDE_DEBUG("h=%d, max_dsc_width=%d, num_dsc=%d\n", + hdisplay, max_dsc_width, + *num_dsc); + + return 0; +} + static void _sde_kms_null_commit(struct drm_device *dev, struct drm_encoder *enc) { @@ -2885,6 +2913,7 @@ static const struct msm_kms_funcs kms_funcs = { .postopen = _sde_kms_post_open, .check_for_splash = sde_kms_check_for_splash, .get_mixer_count = sde_kms_get_mixer_count, + .get_dsc_count = sde_kms_get_dsc_count, }; static int _sde_kms_mmu_destroy(struct sde_kms *sde_kms)