From dd9f329b96537657fd05129080e5c7636b0b4fc4 Mon Sep 17 00:00:00 2001 From: Steve Cohen Date: Fri, 7 Feb 2020 14:43:35 -0500 Subject: [PATCH 1/5] disp: msm: sde: set prop_count for U32 and BOOL type device node props Set the prop_count corresponding to unsigned 32-bit integers and boolean types to 1. With this change prop_count should have a positive value for all properties which exist under the device node. Change-Id: I601870dc25ab347b742fcc4aa2f6bea2397c6caf Signed-off-by: Steve Cohen --- msm/sde/sde_hw_catalog.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index ed6126d3409c..a7189e6bafc7 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -912,6 +912,8 @@ static int _validate_dt_entry(struct device_node *np, case PROP_TYPE_U32: rc = of_property_read_u32(np, sde_prop[i].prop_name, &val); + if (!rc) + prop_count[i] = 1; break; case PROP_TYPE_U32_ARRAY: prop_count[i] = of_property_count_u32_elems(np, @@ -935,6 +937,14 @@ static int _validate_dt_entry(struct device_node *np, if (!snp) rc = -EINVAL; break; + case PROP_TYPE_BOOL: + /** + * No special handling for bool properties here. + * They will always exist, with value indicating + * if the given key is present or not. + */ + prop_count[i] = 1; + break; default: SDE_DEBUG("invalid property type:%d\n", sde_prop[i].type); From e1e4771b775b5f8cf1612c1965d8b0278889e226 Mon Sep 17 00:00:00 2001 From: Steve Cohen Date: Wed, 5 Feb 2020 17:08:19 -0500 Subject: [PATCH 2/5] disp: msm: sde: create helper for getting CTL & mixer dts properties Simplify device node parsing by using a new structure for temporarily holding the read properties and create helpers for getting/freeing all the properties in a sde_prop_type table. This patch uses these helpers for CTL and mixer parsing, other blocks will be refactored as well. Change-Id: I546f32baea8fc09c36a7f24b042f1f1615770b72 Signed-off-by: Steve Cohen --- msm/sde/sde_hw_catalog.c | 199 +++++++++++++++++++++------------------ 1 file changed, 106 insertions(+), 93 deletions(-) diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index a7189e6bafc7..241d0bde5ae1 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -32,6 +32,9 @@ /* each entry will have register address and bit offset in that register */ #define MAX_BIT_OFFSET 2 +/* 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 */ #define DEFAULT_SDE_LINE_WIDTH 2048 @@ -475,6 +478,20 @@ struct sde_prop_value { u32 bit_value[MAX_SDE_HW_BLK][MAX_BIT_OFFSET]; }; +/** + * struct sde_dt_props - stores dts properties read from a sde_prop_type table + * @exists: Array of bools indicating if the given prop name was present + * @counts: Count of the number of valid values for the property + * @values: Array storing the count[i] property values + * + * Must use the sde_[get|put]_dt_props APIs to allocate/free this object. + */ +struct sde_dt_props { + bool exists[MAX_SDE_DT_TABLE_SIZE]; + int counts[MAX_SDE_DT_TABLE_SIZE]; + struct sde_prop_value *values; +}; + /************************************************************* * dts property list *************************************************************/ @@ -1101,6 +1118,50 @@ end: return rc; } +static struct sde_dt_props *sde_get_dt_props(struct device_node *np, + size_t prop_max, struct sde_prop_type *sde_prop, + u32 prop_size, u32 *off_count) +{ + struct sde_dt_props *props; + int rc = -ENOMEM; + + props = kzalloc(sizeof(*props), GFP_KERNEL); + if (!props) + return ERR_PTR(rc); + + props->values = kcalloc(prop_max, sizeof(*props->values), + GFP_KERNEL); + if (!props->values) + goto free_props; + + rc = _validate_dt_entry(np, sde_prop, prop_size, props->counts, + off_count); + if (rc) + goto free_vals; + + rc = _read_dt_entry(np, sde_prop, prop_size, props->counts, + props->exists, props->values); + if (rc) + goto free_vals; + + return props; + +free_vals: + kfree(props->values); +free_props: + kfree(props); + return ERR_PTR(rc); +} + +static void sde_put_dt_props(struct sde_dt_props *props) +{ + if (!props) + return; + + kfree(props->values); + kfree(props); +} + static int _add_to_irq_offset_list(struct sde_mdss_cfg *sde_cfg, enum sde_intr_hwblk_type blk_type, u32 instance, u32 offset) { @@ -1719,43 +1780,29 @@ end: static int sde_ctl_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) { - int rc, prop_count[HW_PROP_MAX], i; - bool prop_exists[HW_PROP_MAX]; - struct sde_prop_value *prop_value = NULL; + int i; + struct sde_dt_props *props; struct sde_ctl_cfg *ctl; u32 off_count; if (!sde_cfg) { SDE_ERROR("invalid argument input param\n"); - rc = -EINVAL; - goto end; + return -EINVAL; } - prop_value = kzalloc(HW_PROP_MAX * - sizeof(struct sde_prop_value), GFP_KERNEL); - if (!prop_value) { - rc = -ENOMEM; - goto end; - } - - rc = _validate_dt_entry(np, ctl_prop, ARRAY_SIZE(ctl_prop), prop_count, - &off_count); - if (rc) - goto end; + props = sde_get_dt_props(np, HW_PROP_MAX, ctl_prop, + ARRAY_SIZE(ctl_prop), &off_count); + if (IS_ERR_OR_NULL(props)) + return PTR_ERR(props); sde_cfg->ctl_count = off_count; - rc = _read_dt_entry(np, ctl_prop, ARRAY_SIZE(ctl_prop), prop_count, - prop_exists, prop_value); - if (rc) - goto end; - for (i = 0; i < off_count; i++) { const char *disp_pref = NULL; ctl = sde_cfg->ctl + i; - ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i); - ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0); + ctl->base = PROP_VALUE_ACCESS(props->values, HW_OFF, i); + ctl->len = PROP_VALUE_ACCESS(props->values, HW_LEN, 0); ctl->id = CTL_0 + i; snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u", ctl->id - CTL_0); @@ -1776,9 +1823,9 @@ static int sde_ctl_parse_dt(struct device_node *np, SDE_HW_MAJOR(SDE_HW_VER_700)) set_bit(SDE_CTL_UNIFIED_DSPP_FLUSH, &ctl->features); } -end: - kfree(prop_value); - return rc; + + sde_put_dt_props(props); + return 0; } void sde_hw_mixer_set_preference(struct sde_mdss_cfg *sde_cfg, u32 num_lm, @@ -1850,14 +1897,7 @@ void sde_hw_mixer_set_preference(struct sde_mdss_cfg *sde_cfg, u32 num_lm, static int sde_mixer_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) { - int rc, prop_count[MIXER_PROP_MAX], i, j; - int blocks_prop_count[MIXER_BLOCKS_PROP_MAX]; - int blend_prop_count[MIXER_BLEND_PROP_MAX]; - bool prop_exists[MIXER_PROP_MAX]; - bool blocks_prop_exists[MIXER_BLOCKS_PROP_MAX]; - bool blend_prop_exists[MIXER_BLEND_PROP_MAX]; - struct sde_prop_value *prop_value = NULL, *blocks_prop_value = NULL; - struct sde_prop_value *blend_prop_value = NULL; + int rc = 0, i, j; u32 off_count, blend_off_count, max_blendstages, lm_pair_mask; struct sde_lm_cfg *mixer; struct sde_lm_sub_blks *sblk; @@ -1865,30 +1905,18 @@ static int sde_mixer_parse_dt(struct device_node *np, u32 pp_idx, dspp_idx, ds_idx; u32 mixer_base; struct device_node *snp = NULL; + struct sde_dt_props *props, *blend_props, *blocks_props = NULL; if (!sde_cfg) { SDE_ERROR("invalid argument input param\n"); - rc = -EINVAL; - goto end; + return -EINVAL; } max_blendstages = sde_cfg->max_mixer_blendstages; - prop_value = kcalloc(MIXER_PROP_MAX, - sizeof(struct sde_prop_value), GFP_KERNEL); - if (!prop_value) { - rc = -ENOMEM; - goto end; - } - - rc = _validate_dt_entry(np, mixer_prop, ARRAY_SIZE(mixer_prop), - prop_count, &off_count); - if (rc) - goto end; - - rc = _read_dt_entry(np, mixer_prop, ARRAY_SIZE(mixer_prop), prop_count, - prop_exists, prop_value); - if (rc) - goto end; + props = sde_get_dt_props(np, MIXER_PROP_MAX, mixer_prop, + ARRAY_SIZE(mixer_prop), &off_count); + if (IS_ERR_OR_NULL(props)) + return PTR_ERR(props); pp_count = sde_cfg->pingpong_count; dspp_count = sde_cfg->dspp_count; @@ -1897,47 +1925,30 @@ static int sde_mixer_parse_dt(struct device_node *np, /* get mixer feature dt properties if they exist */ snp = of_get_child_by_name(np, mixer_prop[MIXER_BLOCKS].prop_name); if (snp) { - blocks_prop_value = kzalloc(MIXER_BLOCKS_PROP_MAX * - MAX_SDE_HW_BLK * sizeof(struct sde_prop_value), - GFP_KERNEL); - if (!blocks_prop_value) { - rc = -ENOMEM; - goto end; + blocks_props = sde_get_dt_props(snp, MIXER_PROP_MAX, + mixer_blocks_prop, + ARRAY_SIZE(mixer_blocks_prop), NULL); + if (IS_ERR_OR_NULL(blocks_props)) { + rc = PTR_ERR(blocks_props); + goto put_props; } - rc = _validate_dt_entry(snp, mixer_blocks_prop, - ARRAY_SIZE(mixer_blocks_prop), blocks_prop_count, NULL); - if (rc) - goto end; - rc = _read_dt_entry(snp, mixer_blocks_prop, - ARRAY_SIZE(mixer_blocks_prop), - blocks_prop_count, blocks_prop_exists, - blocks_prop_value); } /* get the blend_op register offsets */ - blend_prop_value = kzalloc(MIXER_BLEND_PROP_MAX * - sizeof(struct sde_prop_value), GFP_KERNEL); - if (!blend_prop_value) { - rc = -ENOMEM; - goto end; + blend_props = sde_get_dt_props(np, MIXER_BLEND_PROP_MAX, + mixer_blend_prop, ARRAY_SIZE(mixer_blend_prop), + &blend_off_count); + if (IS_ERR_OR_NULL(blend_props)) { + rc = PTR_ERR(blend_props); + goto put_blocks; } - rc = _validate_dt_entry(np, mixer_blend_prop, - ARRAY_SIZE(mixer_blend_prop), blend_prop_count, - &blend_off_count); - if (rc) - goto end; - - rc = _read_dt_entry(np, mixer_blend_prop, ARRAY_SIZE(mixer_blend_prop), - blend_prop_count, blend_prop_exists, blend_prop_value); - if (rc) - goto end; for (i = 0, mixer_count = 0, pp_idx = 0, dspp_idx = 0, ds_idx = 0; i < off_count; i++) { const char *disp_pref = NULL; const char *cwb_pref = NULL; - mixer_base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i); + mixer_base = PROP_VALUE_ACCESS(props->values, MIXER_OFF, i); if (!mixer_base) continue; @@ -1952,15 +1963,15 @@ static int sde_mixer_parse_dt(struct device_node *np, mixer->sblk = sblk; mixer->base = mixer_base; - mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0); + mixer->len = PROP_VALUE_ACCESS(props->values, MIXER_LEN, 0); mixer->id = LM_0 + i; snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u", mixer->id - LM_0); - if (!prop_exists[MIXER_LEN]) + if (!props->exists[MIXER_LEN]) mixer->len = DEFAULT_SDE_HW_BLOCK_LEN; - lm_pair_mask = PROP_VALUE_ACCESS(prop_value, + lm_pair_mask = PROP_VALUE_ACCESS(props->values, MIXER_PAIR_MASK, i); if (lm_pair_mask) mixer->lm_pair_mask = 1 << lm_pair_mask; @@ -1970,7 +1981,7 @@ static int sde_mixer_parse_dt(struct device_node *np, for (j = 0; j < blend_off_count; j++) sblk->blendstage_base[j] = - PROP_VALUE_ACCESS(blend_prop_value, + PROP_VALUE_ACCESS(blend_props->values, MIXER_BLEND_OP_OFF, j); if (sde_cfg->has_src_split) @@ -2005,11 +2016,11 @@ static int sde_mixer_parse_dt(struct device_node *np, mixer_count++; sblk->gc.id = SDE_MIXER_GC; - if (blocks_prop_value && blocks_prop_exists[MIXER_GC_PROP]) { - sblk->gc.base = PROP_VALUE_ACCESS(blocks_prop_value, + if (blocks_props && blocks_props->exists[MIXER_GC_PROP]) { + sblk->gc.base = PROP_VALUE_ACCESS(blocks_props->values, MIXER_GC_PROP, 0); - sblk->gc.version = PROP_VALUE_ACCESS(blocks_prop_value, - MIXER_GC_PROP, 1); + sblk->gc.version = PROP_VALUE_ACCESS( + blocks_props->values, MIXER_GC_PROP, 1); sblk->gc.len = 0; set_bit(SDE_MIXER_GC, &mixer->features); } @@ -2017,9 +2028,11 @@ static int sde_mixer_parse_dt(struct device_node *np, sde_cfg->mixer_count = mixer_count; end: - kfree(prop_value); - kfree(blocks_prop_value); - kfree(blend_prop_value); + sde_put_dt_props(blend_props); +put_blocks: + sde_put_dt_props(blocks_props); +put_props: + sde_put_dt_props(props); return rc; } From 818651c2f165965bdfbf93f407e01fb9a9e4b4e8 Mon Sep 17 00:00:00 2001 From: Steve Cohen Date: Thu, 6 Feb 2020 16:02:25 -0500 Subject: [PATCH 3/5] disp: msm: sde: use sde_dt_props object to parse dspp dts Use the new helpers for parsing DSPP device nodes entries. Change-Id: I620ba80cac90f34faa1f853f00cf856370ae387c Signed-off-by: Steve Cohen --- msm/sde/sde_hw_catalog.c | 129 +++++++++++++-------------------------- 1 file changed, 44 insertions(+), 85 deletions(-) diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 241d0bde5ae1..b47c39d54cfc 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -2470,102 +2470,58 @@ end: static int sde_dspp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) { - int rc, prop_count[DSPP_PROP_MAX], i; - int ad_prop_count[AD_PROP_MAX]; - int ltm_prop_count[LTM_PROP_MAX]; - bool prop_exists[DSPP_PROP_MAX], ad_prop_exists[AD_PROP_MAX]; - bool ltm_prop_exists[LTM_PROP_MAX]; - bool blocks_prop_exists[DSPP_BLOCKS_PROP_MAX]; - struct sde_prop_value *ad_prop_value = NULL, *ltm_prop_value = NULL; - int blocks_prop_count[DSPP_BLOCKS_PROP_MAX]; - struct sde_prop_value *prop_value = NULL, *blocks_prop_value = NULL; + int rc = 0, i; u32 off_count, ad_off_count, ltm_off_count; + struct sde_dt_props *props, *ad_props, *ltm_props; + struct sde_dt_props *blocks_props = NULL; struct sde_dspp_cfg *dspp; struct sde_dspp_sub_blks *sblk; struct device_node *snp = NULL; if (!sde_cfg) { SDE_ERROR("invalid argument\n"); - rc = -EINVAL; - goto end; + return -EINVAL; } - prop_value = kzalloc(DSPP_PROP_MAX * - sizeof(struct sde_prop_value), GFP_KERNEL); - if (!prop_value) { - rc = -ENOMEM; - goto end; - } - - rc = _validate_dt_entry(np, dspp_prop, ARRAY_SIZE(dspp_prop), - prop_count, &off_count); - if (rc) - goto end; + props = sde_get_dt_props(np, DSPP_PROP_MAX, dspp_prop, + ARRAY_SIZE(dspp_prop), &off_count); + if (IS_ERR_OR_NULL(props)) + return PTR_ERR(props); sde_cfg->dspp_count = off_count; - rc = _read_dt_entry(np, dspp_prop, ARRAY_SIZE(dspp_prop), prop_count, - prop_exists, prop_value); - if (rc) - goto end; - /* Parse AD dtsi entries */ - ad_prop_value = kcalloc(AD_PROP_MAX, - sizeof(struct sde_prop_value), GFP_KERNEL); - if (!ad_prop_value) { - rc = -ENOMEM; - goto end; + ad_props = sde_get_dt_props(np, AD_PROP_MAX, ad_prop, + ARRAY_SIZE(ad_prop), &ad_off_count); + if (IS_ERR_OR_NULL(ad_props)) { + rc = PTR_ERR(ad_props); + goto put_props; } - rc = _validate_dt_entry(np, ad_prop, ARRAY_SIZE(ad_prop), - ad_prop_count, &ad_off_count); - if (rc) - goto end; - rc = _read_dt_entry(np, ad_prop, ARRAY_SIZE(ad_prop), ad_prop_count, - ad_prop_exists, ad_prop_value); - if (rc) - goto end; /* Parse LTM dtsi entries */ - ltm_prop_value = kcalloc(LTM_PROP_MAX, - sizeof(struct sde_prop_value), GFP_KERNEL); - if (!ltm_prop_value) { - rc = -ENOMEM; - goto end; + ltm_props = sde_get_dt_props(np, LTM_PROP_MAX, ltm_prop, + ARRAY_SIZE(ltm_prop), <m_off_count); + if (IS_ERR_OR_NULL(ltm_props)) { + rc = PTR_ERR(ltm_props); + goto put_ad_props; } - rc = _validate_dt_entry(np, ltm_prop, ARRAY_SIZE(ltm_prop), - ltm_prop_count, <m_off_count); - if (rc) - goto end; - rc = _read_dt_entry(np, ltm_prop, ARRAY_SIZE(ltm_prop), ltm_prop_count, - ltm_prop_exists, ltm_prop_value); - if (rc) - goto end; /* get DSPP feature dt properties if they exist */ snp = of_get_child_by_name(np, dspp_prop[DSPP_BLOCKS].prop_name); if (snp) { - blocks_prop_value = kzalloc(DSPP_BLOCKS_PROP_MAX * - MAX_SDE_HW_BLK * sizeof(struct sde_prop_value), - GFP_KERNEL); - if (!blocks_prop_value) { - rc = -ENOMEM; - goto end; + blocks_props = sde_get_dt_props(snp, DSPP_BLOCKS_PROP_MAX, + dspp_blocks_prop, ARRAY_SIZE(dspp_blocks_prop), + NULL); + if (IS_ERR_OR_NULL(blocks_props)) { + rc = PTR_ERR(blocks_props); + goto put_ltm_props; } - rc = _validate_dt_entry(snp, dspp_blocks_prop, - ARRAY_SIZE(dspp_blocks_prop), blocks_prop_count, NULL); - if (rc) - goto end; - rc = _read_dt_entry(snp, dspp_blocks_prop, - ARRAY_SIZE(dspp_blocks_prop), blocks_prop_count, - blocks_prop_exists, blocks_prop_value); - if (rc) - goto end; } for (i = 0; i < off_count; i++) { dspp = sde_cfg->dspp + i; - dspp->base = PROP_VALUE_ACCESS(prop_value, DSPP_OFF, i); - dspp->len = PROP_VALUE_ACCESS(prop_value, DSPP_SIZE, 0); + dspp->base = PROP_VALUE_ACCESS(props->values, DSPP_OFF, i); + dspp->len = PROP_VALUE_ACCESS(props->values, DSPP_SIZE, 0); dspp->id = DSPP_0 + i; snprintf(dspp->name, SDE_HW_BLK_NAME_LEN, "dspp_%u", dspp->id - DSPP_0); @@ -2578,17 +2534,18 @@ static int sde_dspp_parse_dt(struct device_node *np, } dspp->sblk = sblk; - if (blocks_prop_value) + if (blocks_props) _sde_dspp_setup_blocks(sde_cfg, dspp, sblk, - blocks_prop_exists, blocks_prop_value); + blocks_props->exists, + blocks_props->values); sblk->ad.id = SDE_DSPP_AD; sde_cfg->ad_count = ad_off_count; - if (ad_prop_value && (i < ad_off_count) && - ad_prop_exists[AD_OFF]) { - sblk->ad.base = PROP_VALUE_ACCESS(ad_prop_value, + if (ad_props && (i < ad_off_count) && + ad_props->exists[AD_OFF]) { + sblk->ad.base = PROP_VALUE_ACCESS(ad_props->values, AD_OFF, i); - sblk->ad.version = PROP_VALUE_ACCESS(ad_prop_value, + sblk->ad.version = PROP_VALUE_ACCESS(ad_props->values, AD_VERSION, 0); set_bit(SDE_DSPP_AD, &dspp->features); rc = _add_to_irq_offset_list(sde_cfg, @@ -2600,11 +2557,11 @@ static int sde_dspp_parse_dt(struct device_node *np, sblk->ltm.id = SDE_DSPP_LTM; sde_cfg->ltm_count = ltm_off_count; - if (ltm_prop_value && (i < ltm_off_count) && - ltm_prop_exists[LTM_OFF]) { - sblk->ltm.base = PROP_VALUE_ACCESS(ltm_prop_value, + if (ltm_props && (i < ltm_off_count) && + ltm_props->exists[LTM_OFF]) { + sblk->ltm.base = PROP_VALUE_ACCESS(ltm_props->values, LTM_OFF, i); - sblk->ltm.version = PROP_VALUE_ACCESS(ltm_prop_value, + sblk->ltm.version = PROP_VALUE_ACCESS(ltm_props->values, LTM_VERSION, 0); set_bit(SDE_DSPP_LTM, &dspp->features); rc = _add_to_irq_offset_list(sde_cfg, @@ -2617,10 +2574,13 @@ static int sde_dspp_parse_dt(struct device_node *np, } end: - kfree(prop_value); - kfree(ad_prop_value); - kfree(ltm_prop_value); - kfree(blocks_prop_value); + sde_put_dt_props(blocks_props); +put_ltm_props: + sde_put_dt_props(ltm_props); +put_ad_props: + sde_put_dt_props(ad_props); +put_props: + sde_put_dt_props(props); return rc; } @@ -2728,7 +2688,6 @@ static int sde_ds_parse_dt(struct device_node *np, set_bit(SDE_SSPP_SCALER_QSEED3, &ds->features); else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3LITE) set_bit(SDE_SSPP_SCALER_QSEED3LITE, &ds->features); - } end: From af07b8a5d44cdad59e6ab1b7e4605a57f2eb17a3 Mon Sep 17 00:00:00 2001 From: Amine Najahi Date: Thu, 10 Oct 2019 11:19:15 -0400 Subject: [PATCH 4/5] disp: msm: sde: add support for hardware based rounded corner Add support for hardware based rounded corner part of color processing framework. Change-Id: I3e5f4dac6ffc759bb940215b7621ac716f255169 Signed-off-by: Amine Najahi Signed-off-by: Steve Cohen --- msm/Makefile | 1 + msm/sde/sde_color_processing.c | 166 ++++- msm/sde/sde_crtc.c | 8 + msm/sde/sde_hw_catalog.c | 47 +- msm/sde/sde_hw_catalog.h | 18 + msm/sde/sde_hw_dspp.c | 42 ++ msm/sde/sde_hw_dspp.h | 40 + msm/sde/sde_hw_mdss.h | 7 + msm/sde/sde_hw_rc.c | 974 +++++++++++++++++++++++++ msm/sde/sde_hw_rc.h | 71 ++ msm/sde/sde_hw_reg_dma_v1.c | 2 + msm/sde/sde_hw_reg_dma_v1_color_proc.c | 89 +++ msm/sde/sde_hw_reg_dma_v1_color_proc.h | 9 + msm/sde/sde_reg_dma.h | 3 + 14 files changed, 1470 insertions(+), 7 deletions(-) create mode 100644 msm/sde/sde_hw_rc.c create mode 100644 msm/sde/sde_hw_rc.h diff --git a/msm/Makefile b/msm/Makefile index 36c65e913409..e2f9f645942b 100644 --- a/msm/Makefile +++ b/msm/Makefile @@ -76,6 +76,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \ sde/sde_hw_qdss.o \ sde_dsc_helper.o \ sde_vdc_helper.o \ + sde/sde_hw_rc.o \ msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \ sde/sde_encoder_phys_wb.o \ diff --git a/msm/sde/sde_color_processing.c b/msm/sde/sde_color_processing.c index 7f07222cd342..35bf9aca8018 100644 --- a/msm/sde/sde_color_processing.c +++ b/msm/sde/sde_color_processing.c @@ -56,6 +56,8 @@ static void dspp_ad_install_property(struct drm_crtc *crtc); static void dspp_ltm_install_property(struct drm_crtc *crtc); +static void dspp_rc_install_property(struct drm_crtc *crtc); + static void dspp_vlut_install_property(struct drm_crtc *crtc); static void dspp_gamut_install_property(struct drm_crtc *crtc); @@ -112,6 +114,7 @@ do { \ func[SDE_DSPP_IGC] = dspp_igc_install_property; \ func[SDE_DSPP_HIST] = dspp_hist_install_property; \ func[SDE_DSPP_DITHER] = dspp_dither_install_property; \ + func[SDE_DSPP_RC] = dspp_rc_install_property; \ } while (0) typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc); @@ -160,6 +163,7 @@ enum sde_cp_crtc_features { SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3, SDE_CP_CRTC_DSPP_LTM_VLUT, SDE_CP_CRTC_DSPP_SB, + SDE_CP_CRTC_DSPP_RC_MASK, SDE_CP_CRTC_DSPP_MAX, /* DSPP features end */ @@ -172,11 +176,15 @@ enum sde_cp_crtc_features { }; enum sde_cp_crtc_pu_features { + SDE_CP_CRTC_DSPP_RC_PU, SDE_CP_CRTC_MAX_PU_FEATURES, }; static enum sde_cp_crtc_pu_features - sde_cp_crtc_pu_to_feature[SDE_CP_CRTC_MAX_PU_FEATURES]; + sde_cp_crtc_pu_to_feature[SDE_CP_CRTC_MAX_PU_FEATURES] = { + [SDE_CP_CRTC_DSPP_RC_PU] = + (enum sde_cp_crtc_pu_features) SDE_CP_CRTC_DSPP_RC_MASK, +}; static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc); @@ -672,10 +680,122 @@ static int set_ltm_hist_crtl_feature(struct sde_hw_dspp *hw_dspp, return ret; } +static int check_rc_mask_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; + + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments"); + return -EINVAL; + } + + if (!hw_dspp->ops.validate_rc_mask) { + DRM_ERROR("invalid rc ops"); + return -EINVAL; + } + + ret = hw_dspp->ops.validate_rc_mask(hw_dspp, hw_cfg); + if (ret) + DRM_ERROR("failed to validate rc mask %d", ret); + + return ret; +} + +static int set_rc_mask_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; + + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (!hw_dspp->ops.setup_rc_mask || !hw_dspp->ops.setup_rc_data) { + DRM_ERROR("invalid rc ops\n"); + return -EINVAL; + } + + DRM_DEBUG_DRIVER("dspp %d setup mask for rc instance %u\n", + hw_dspp->idx, hw_dspp->cap->sblk->rc.idx); + + ret = hw_dspp->ops.setup_rc_mask(hw_dspp, hw_cfg); + if (ret) { + DRM_ERROR("failed to setup rc mask, ret %d\n", ret); + goto exit; + } + + /* rc data should be programmed once if dspp are in multi-pipe mode */ + if (hw_dspp->cap->sblk->rc.idx % hw_cfg->num_of_mixers == 0) { + ret = hw_dspp->ops.setup_rc_data(hw_dspp, hw_cfg); + if (ret) { + DRM_ERROR("failed to setup rc data, ret %d\n", ret); + goto exit; + } + } + +exit: + return ret; +} + +static int set_rc_pu_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; + + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (!hw_dspp->ops.setup_rc_pu_roi) { + DRM_ERROR("invalid rc ops\n"); + return -EINVAL; + } + + DRM_DEBUG_DRIVER("dspp %d setup pu roi for rc instance %u\n", + hw_dspp->idx, hw_dspp->cap->sblk->rc.idx); + + ret = hw_dspp->ops.setup_rc_pu_roi(hw_dspp, hw_cfg); + if (ret < 0) + DRM_ERROR("failed to setup rc pu roi, ret %d\n", ret); + + return ret; +} + +static int check_rc_pu_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; + + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (!hw_dspp->ops.validate_rc_pu_roi) { + SDE_ERROR("invalid rc ops"); + return -EINVAL; + } + + ret = hw_dspp->ops.validate_rc_pu_roi(hw_dspp, hw_cfg); + if (ret) + SDE_ERROR("failed to validate rc pu roi, ret %d", ret); + + return ret; +} feature_wrapper check_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES]; #define setup_check_crtc_feature_wrappers(wrappers) \ -memset(wrappers, 0, sizeof(wrappers)) +do { \ + memset(wrappers, 0, sizeof(wrappers)); \ + wrappers[SDE_CP_CRTC_DSPP_RC_MASK] = check_rc_mask_feature; \ +} while (0) feature_wrapper set_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES]; #define setup_set_crtc_feature_wrappers(wrappers) \ @@ -718,15 +838,22 @@ do { \ wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF2] = set_ltm_queue_buf_feature; \ wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = set_ltm_queue_buf_feature; \ wrappers[SDE_CP_CRTC_DSPP_LTM_HIST_CTL] = set_ltm_hist_crtl_feature; \ + wrappers[SDE_CP_CRTC_DSPP_RC_MASK] = set_rc_mask_feature; \ } while (0) feature_wrapper set_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES]; #define setup_set_crtc_pu_feature_wrappers(wrappers) \ -memset(wrappers, 0, sizeof(wrappers)) +do { \ + memset(wrappers, 0, sizeof(wrappers)); \ + wrappers[SDE_CP_CRTC_DSPP_RC_PU] = set_rc_pu_feature; \ +} while (0) feature_wrapper check_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES]; #define setup_check_crtc_pu_feature_wrappers(wrappers) \ -memset(wrappers, 0, sizeof(wrappers)) +do { \ + memset(wrappers, 0, sizeof(wrappers)); \ + wrappers[SDE_CP_CRTC_DSPP_RC_PU] = check_rc_pu_feature; \ +} while (0) #define INIT_PROP_ATTACH(p, crtc, prop, node, feature, val) \ do { \ @@ -1372,6 +1499,7 @@ static const int dspp_feature_to_sub_blk_tbl[SDE_CP_CRTC_MAX_FEATURES] = { [SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = SDE_DSPP_LTM, [SDE_CP_CRTC_DSPP_LTM_VLUT] = SDE_DSPP_LTM, [SDE_CP_CRTC_DSPP_SB] = SDE_DSPP_SB, + [SDE_CP_CRTC_DSPP_RC_MASK] = SDE_DSPP_RC, [SDE_CP_CRTC_DSPP_MAX] = SDE_DSPP_MAX, [SDE_CP_CRTC_LM_GC] = SDE_DSPP_MAX, }; @@ -2391,6 +2519,36 @@ static void dspp_ltm_install_property(struct drm_crtc *crtc) } } +static void dspp_rc_install_property(struct drm_crtc *crtc) +{ + char feature_name[256]; + struct sde_kms *kms = NULL; + struct sde_mdss_cfg *catalog = NULL; + u32 version; + + if (!crtc) { + DRM_ERROR("invalid arguments"); + return; + } + + kms = get_kms(crtc); + catalog = kms->catalog; + version = catalog->dspp[0].sblk->rc.version >> 16; + switch (version) { + case 1: + snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d", + "SDE_DSPP_RC_MASK_V", version); + sde_cp_crtc_install_blob_property(crtc, feature_name, + SDE_CP_CRTC_DSPP_RC_MASK, + sizeof(struct drm_msm_rc_mask_cfg)); + + break; + default: + DRM_ERROR("version %d not supported\n", version); + break; + } +} + static void lm_gc_install_property(struct drm_crtc *crtc) { char feature_name[256]; diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 22f4230a21a0..91f711137c5c 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -755,6 +755,9 @@ static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc, } sde_kms_rect_merge_rectangles(&sde_conn_state->rois, &conn_roi); + SDE_DEBUG("conn_roi x:%u, y:%u, w:%u, h:%u\n", + conn_roi.x, conn_roi.y, + conn_roi.w, conn_roi.h); SDE_EVT32_VERBOSE(DRMID(crtc), DRMID(conn), conn_roi.x, conn_roi.y, conn_roi.w, conn_roi.h); @@ -5003,8 +5006,13 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, sde_crtc_install_dest_scale_properties(sde_crtc, catalog, info); + if (catalog->dspp_count && catalog->rc_count) + sde_kms_info_add_keyint(info, "rc_mem_size", + catalog->dspp[0].sblk->rc.mem_total_size); + msm_property_install_blob(&sde_crtc->property_info, "capabilities", DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO); + msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info, info->data, SDE_KMS_INFO_DATALEN(info), CRTC_PROP_INFO); diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index b47c39d54cfc..d70fa507e927 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -383,6 +383,14 @@ enum { LTM_PROP_MAX, }; +enum { + RC_OFF, + RC_LEN, + RC_VERSION, + RC_MEM_TOTAL_SIZE, + RC_PROP_MAX, +}; + enum { MIXER_OFF, MIXER_LEN, @@ -699,6 +707,13 @@ static struct sde_prop_type ltm_prop[] = { {LTM_VERSION, "qcom,sde-dspp-ltm-version", false, PROP_TYPE_U32}, }; +static struct sde_prop_type rc_prop[] = { + {RC_OFF, "qcom,sde-dspp-rc-off", false, PROP_TYPE_U32_ARRAY}, + {RC_LEN, "qcom,sde-dspp-rc-size", false, PROP_TYPE_U32}, + {RC_VERSION, "qcom,sde-dspp-rc-version", false, PROP_TYPE_U32}, + {RC_MEM_TOTAL_SIZE, "qcom,sde-dspp-rc-mem-size", false, PROP_TYPE_U32}, +}; + static struct sde_prop_type ds_top_prop[] = { {DS_TOP_OFF, "qcom,sde-dest-scaler-top-off", false, PROP_TYPE_U32}, {DS_TOP_LEN, "qcom,sde-dest-scaler-top-size", false, PROP_TYPE_U32}, @@ -2471,8 +2486,8 @@ static int sde_dspp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) { int rc = 0, i; - u32 off_count, ad_off_count, ltm_off_count; - struct sde_dt_props *props, *ad_props, *ltm_props; + u32 off_count, ad_off_count, ltm_off_count, rc_off_count; + struct sde_dt_props *props, *ad_props, *ltm_props, *rc_props; struct sde_dt_props *blocks_props = NULL; struct sde_dspp_cfg *dspp; struct sde_dspp_sub_blks *sblk; @@ -2506,6 +2521,14 @@ static int sde_dspp_parse_dt(struct device_node *np, goto put_ad_props; } + /* Parse RC dtsi entries */ + rc_props = sde_get_dt_props(np, RC_PROP_MAX, rc_prop, + ARRAY_SIZE(rc_prop), &rc_off_count); + if (IS_ERR_OR_NULL(rc_props)) { + rc = PTR_ERR(rc_props); + goto put_ltm_props; + } + /* get DSPP feature dt properties if they exist */ snp = of_get_child_by_name(np, dspp_prop[DSPP_BLOCKS].prop_name); if (snp) { @@ -2514,7 +2537,7 @@ static int sde_dspp_parse_dt(struct device_node *np, NULL); if (IS_ERR_OR_NULL(blocks_props)) { rc = PTR_ERR(blocks_props); - goto put_ltm_props; + goto put_rc_props; } } @@ -2571,10 +2594,28 @@ static int sde_dspp_parse_dt(struct device_node *np, goto end; } + sblk->rc.id = SDE_DSPP_RC; + sde_cfg->rc_count = rc_off_count; + if (rc_props && (i < rc_off_count) && + rc_props->exists[RC_OFF]) { + sblk->rc.base = PROP_VALUE_ACCESS(rc_props->values, + RC_OFF, i); + sblk->rc.len = PROP_VALUE_ACCESS(rc_props->values, + RC_LEN, 0); + sblk->rc.version = PROP_VALUE_ACCESS(rc_props->values, + RC_VERSION, 0); + sblk->rc.mem_total_size = PROP_VALUE_ACCESS( + rc_props->values, RC_MEM_TOTAL_SIZE, + 0); + sblk->rc.idx = i; + set_bit(SDE_DSPP_RC, &dspp->features); + } } end: sde_put_dt_props(blocks_props); +put_rc_props: + sde_put_dt_props(rc_props); put_ltm_props: sde_put_dt_props(ltm_props); put_ad_props: diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index cc7251279d72..51396ceb112b 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -755,6 +755,20 @@ struct sde_lm_sub_blks { struct sde_pp_blk gc; }; +/** + * struct sde_dspp_rc: Pixel processing rounded corner sub-blk information + * @info: HW register and features supported by this sub-blk. + * @version: HW Algorithm version. + * @idx: HW block instance id. + * @mem_total_size: data memory size. + */ +struct sde_dspp_rc { + SDE_HW_SUBBLK_INFO; + u32 version; + u32 idx; + u32 mem_total_size; +}; + struct sde_dspp_sub_blks { struct sde_pp_blk igc; struct sde_pp_blk pcc; @@ -768,6 +782,7 @@ struct sde_dspp_sub_blks { struct sde_pp_blk ad; struct sde_pp_blk ltm; struct sde_pp_blk vlut; + struct sde_dspp_rc rc; }; struct sde_pingpong_sub_blks { @@ -1426,6 +1441,7 @@ struct sde_limit_cfg { * @has_vig_p010 indicates if vig pipe supports p010 format * @inline_rot_formats formats supported by the inline rotator feature * @irq_offset_list list of sde_intr_irq_offsets to initialize irq table + * @rc_count number of rounded corner hardware instances */ struct sde_mdss_cfg { u32 hwversion; @@ -1536,6 +1552,7 @@ struct sde_mdss_cfg { u32 ad_count; u32 ltm_count; + u32 rc_count; u32 merge_3d_count; struct sde_merge_3d_cfg merge_3d[MAX_BLOCKS]; @@ -1583,6 +1600,7 @@ struct sde_mdss_hw_cfg_handler { #define BLK_WB(s) ((s)->wb) #define BLK_AD(s) ((s)->ad) #define BLK_LTM(s) ((s)->ltm) +#define BLK_RC(s) ((s)->rc) /** * sde_hw_set_preference: populate the individual hw lm preferences, diff --git a/msm/sde/sde_hw_dspp.c b/msm/sde/sde_hw_dspp.c index f67f736ba054..8ec284e13375 100644 --- a/msm/sde/sde_hw_dspp.c +++ b/msm/sde/sde_hw_dspp.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ + #include #include "sde_hw_mdss.h" #include "sde_hwio.h" @@ -10,6 +11,7 @@ #include "sde_hw_color_processing.h" #include "sde_dbg.h" #include "sde_ad4.h" +#include "sde_hw_rc.h" #include "sde_kms.h" static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp, @@ -241,6 +243,37 @@ static void dspp_ltm(struct sde_hw_dspp *c) } } +static void dspp_rc(struct sde_hw_dspp *c) +{ + int ret = 0; + + if (!c) { + SDE_ERROR("invalid arguments\n"); + return; + } + + if (c->cap->sblk->rc.version == SDE_COLOR_PROCESS_VER(0x1, 0x0)) { + ret = sde_hw_rc_init(c); + if (ret) { + SDE_ERROR("rc init failed, ret %d\n", ret); + return; + } + + ret = reg_dmav1_init_dspp_op_v4(SDE_DSPP_RC, c->idx); + if (!ret) + c->ops.setup_rc_data = + sde_hw_rc_setup_data_dma; + else + c->ops.setup_rc_data = + sde_hw_rc_setup_data_ahb; + + c->ops.validate_rc_mask = sde_hw_rc_check_mask; + c->ops.setup_rc_mask = sde_hw_rc_setup_mask; + c->ops.validate_rc_pu_roi = sde_hw_rc_check_pu_roi; + c->ops.setup_rc_pu_roi = sde_hw_rc_setup_pu_roi; + } +} + static void (*dspp_blocks[SDE_DSPP_MAX])(struct sde_hw_dspp *c); static void _init_dspp_ops(void) @@ -257,6 +290,7 @@ static void _init_dspp_ops(void) dspp_blocks[SDE_DSPP_VLUT] = dspp_vlut; dspp_blocks[SDE_DSPP_AD] = dspp_ad; dspp_blocks[SDE_DSPP_LTM] = dspp_ltm; + dspp_blocks[SDE_DSPP_RC] = dspp_rc; } static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) @@ -286,6 +320,7 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, struct sde_hw_dspp *c; struct sde_dspp_cfg *cfg; int rc; + char buf[256]; if (!addr || !m) return ERR_PTR(-EINVAL); @@ -329,6 +364,13 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, c->hw.xin_id); } + if ((cfg->sblk->rc.id == SDE_DSPP_RC) && cfg->sblk->rc.base) { + snprintf(buf, ARRAY_SIZE(buf), "%s_%d", "rc", c->idx - DSPP_0); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, buf, + c->hw.blk_off + cfg->sblk->rc.base, + c->hw.blk_off + cfg->sblk->rc.base + + cfg->sblk->rc.len, c->hw.xin_id); + } return c; blk_init_error: diff --git a/msm/sde/sde_hw_dspp.h b/msm/sde/sde_hw_dspp.h index 82932741aadd..0b493e5906a5 100644 --- a/msm/sde/sde_hw_dspp.h +++ b/msm/sde/sde_hw_dspp.h @@ -209,6 +209,46 @@ struct sde_hw_dspp_ops { * @status: Pointer to u32 where ltm status value is dumped. */ void (*ltm_read_intr_status)(struct sde_hw_dspp *ctx, u32 *status); + + /** + * validate_rc_mask - Validate RC mask configuration + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*validate_rc_mask)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_rc_mask - Setup RC mask configuration + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*setup_rc_mask)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * validate_rc_pu_roi - Validate RC regions in during partial update. + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*validate_rc_pu_roi)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_rc_pu_roi - Setup RC regions in during partial update. + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*setup_rc_pu_roi)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_rc_data - Program RC mask data + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*setup_rc_data)(struct sde_hw_dspp *ctx, void *cfg); }; /** diff --git a/msm/sde/sde_hw_mdss.h b/msm/sde/sde_hw_mdss.h index ff2c19cff2f8..627f76238da7 100644 --- a/msm/sde/sde_hw_mdss.h +++ b/msm/sde/sde_hw_mdss.h @@ -172,6 +172,7 @@ enum sde_stage { SDE_STAGE_10, SDE_STAGE_MAX }; + enum sde_dspp { DSPP_0 = 1, DSPP_1, @@ -186,6 +187,12 @@ enum sde_ltm { LTM_MAX }; +enum sde_rc { + RC_0 = DSPP_0, + RC_1, + RC_MAX +}; + enum sde_ds { DS_TOP, DS_0, diff --git a/msm/sde/sde_hw_rc.c b/msm/sde/sde_hw_rc.c new file mode 100644 index 000000000000..8957eef8fa29 --- /dev/null +++ b/msm/sde/sde_hw_rc.c @@ -0,0 +1,974 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include +#include "sde_kms.h" +#include "sde_reg_dma.h" +#include "sde_hw_rc.h" +#include "sde_hw_catalog.h" +#include "sde_hw_util.h" +#include "sde_hw_dspp.h" +#include "sde_hw_reg_dma_v1_color_proc.h" + +/** + * Hardware register set + */ +#define SDE_HW_RC_REG0 0x00 +#define SDE_HW_RC_REG1 0x04 +#define SDE_HW_RC_REG2 0x08 +#define SDE_HW_RC_REG3 0x0C +#define SDE_HW_RC_REG4 0x10 +#define SDE_HW_RC_REG5 0x14 +#define SDE_HW_RC_REG6 0x18 +#define SDE_HW_RC_REG7 0x1C +#define SDE_HW_RC_REG8 0x20 +#define SDE_HW_RC_REG9 0x24 +#define SDE_HW_RC_REG10 0x28 +#define SDE_HW_RC_REG11 0x2C +#define SDE_HW_RC_REG12 0x30 +#define SDE_HW_RC_REG13 0x34 + +#define SDE_HW_RC_DATA_REG_SIZE 18 +#define SDE_HW_RC_SKIP_DATA_PROG 0x1 + +#define SDE_HW_RC_DISABLE_R1 0x01E +#define SDE_HW_RC_DISABLE_R2 0x1E0 + +#define SDE_HW_RC_PU_SKIP_OP 0x1 + +/** + * struct sde_hw_rc_state - rounded corner cached state per RC instance + * + * @last_rc_mask_cfg: cached value of most recent programmed mask. + * @mask_programmed: true if mask was programmed at least once to RC hardware. + * @last_roi_list: cached value of most recent processed list of ROIs. + * @roi_programmed: true if list of ROIs were processed at least once. + */ +struct sde_hw_rc_state { + struct drm_msm_rc_mask_cfg *last_rc_mask_cfg; + bool mask_programmed; + struct msm_roi_list *last_roi_list; + bool roi_programmed; +}; + +static struct sde_hw_rc_state rc_state[RC_MAX - RC_0] = { + { + .last_rc_mask_cfg = NULL, + .last_roi_list = NULL, + .mask_programmed = false, + .roi_programmed = false, + }, + { + .last_rc_mask_cfg = NULL, + .last_roi_list = NULL, + .mask_programmed = false, + .roi_programmed = false, + }, +}; +#define RC_STATE(hw_dspp) rc_state[hw_dspp->cap->sblk->rc.idx] + +enum rc_param_r { + RC_PARAM_R0 = 0x0, + RC_PARAM_R1 = 0x1, + RC_PARAM_R2 = 0x2, + RC_PARAM_R1R2 = (RC_PARAM_R1 | RC_PARAM_R2), +}; + +enum rc_param_a { + RC_PARAM_A0 = 0x2, + RC_PARAM_A1 = 0x4, +}; + +enum rc_param_b { + RC_PARAM_B0 = 0x0, + RC_PARAM_B1 = 0x1, + RC_PARAM_B2 = 0x2, + RC_PARAM_B1B2 = (RC_PARAM_B1 | RC_PARAM_B2), +}; + +enum rc_param_c { + RC_PARAM_C0 = (BIT(8)), + RC_PARAM_C1 = (BIT(10)), + RC_PARAM_C2 = (BIT(10) | BIT(11)), + RC_PARAM_C3 = (BIT(8) | BIT(10)), + RC_PARAM_C4 = (BIT(8) | BIT(9)), + RC_PARAM_C5 = (BIT(8) | BIT(9) | BIT(10) | BIT(11)), +}; + +enum rc_merge_mode { + RC_MERGE_SINGLE_PIPE = 0x0, + RC_MERGE_DUAL_PIPE = 0x1 +}; +struct rc_config_table { + enum rc_param_a param_a; + enum rc_param_b param_b; + enum rc_param_c param_c; + enum rc_merge_mode merge_mode; + enum rc_merge_mode merge_mode_en; +}; + +static struct rc_config_table config_table[] = { + /* RC_PARAM_A0 configurations */ + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C3, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C0, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C1, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C3, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_DUAL_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C0, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C1, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + }, + + /* RC_PARAM_A1 configurations */ + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C4, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C2, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_DUAL_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C4, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C2, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, +}; + +static inline void _sde_hw_rc_reg_write( + struct sde_hw_dspp *hw_dspp, + int offset, + u32 value) +{ + u32 address = hw_dspp->cap->sblk->rc.base + offset; + + SDE_DEBUG("rc:%u, address:0x%08X, value:0x%08X\n", + hw_dspp->cap->sblk->rc.idx, + hw_dspp->hw.blk_off + address, value); + SDE_REG_WRITE(&hw_dspp->hw, address, value); +} + +static int _sde_hw_rc_get_enable_bits( + enum rc_param_a param_a, + enum rc_param_b param_b, + enum rc_param_c *param_c, + u32 merge_mode, + u32 *merge_mode_en) +{ + int i = 0; + + if (!param_c || !merge_mode_en) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(config_table); i++) { + if (merge_mode == config_table[i].merge_mode && + param_a == config_table[i].param_a && + param_b == config_table[i].param_b) { + *param_c = config_table[i].param_c; + *merge_mode_en = config_table[i].merge_mode_en; + SDE_DEBUG("found param_c:0x%08X, merge_mode_en:%d\n", + *param_c, *merge_mode_en); + return 0; + } + } + SDE_ERROR("configuration not supported"); + + return -EINVAL; +} + +static int _sde_hw_rc_get_merge_mode( + const struct sde_hw_cp_cfg *hw_cfg, + u32 *merge_mode) +{ + int rc = 0; + + if (!hw_cfg || !merge_mode) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (hw_cfg->num_of_mixers == 1) + *merge_mode = RC_MERGE_SINGLE_PIPE; + else if (hw_cfg->num_of_mixers == 2) + *merge_mode = RC_MERGE_DUAL_PIPE; + else { + SDE_ERROR("invalid number of mixers:%d\n", + hw_cfg->num_of_mixers); + return -EINVAL; + } + + SDE_DEBUG("number mixers:%u, merge mode:%u\n", + hw_cfg->num_of_mixers, *merge_mode); + + return rc; +} + +static int _sde_hw_rc_get_ajusted_roi( + const struct sde_hw_cp_cfg *hw_cfg, + const struct sde_rect *pu_roi, + struct sde_rect *rc_roi) +{ + int rc = 0; + + if (!hw_cfg || !pu_roi || !rc_roi) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + /*when partial update is disabled, use full screen ROI*/ + if (pu_roi->w == 0 && pu_roi->h == 0) { + rc_roi->x = pu_roi->x; + rc_roi->y = pu_roi->y; + rc_roi->w = hw_cfg->displayh; + rc_roi->h = hw_cfg->displayv; + } else { + memcpy(rc_roi, pu_roi, sizeof(struct sde_rect)); + } + + SDE_DEBUG("displayh:%u, displayv:%u\n", hw_cfg->displayh, + hw_cfg->displayv); + SDE_DEBUG("pu_roi x:%u, y:%u, w:%u, h:%u\n", pu_roi->x, pu_roi->y, + pu_roi->w, pu_roi->h); + SDE_DEBUG("rc_roi x:%u, y:%u, w:%u, h:%u\n", rc_roi->x, rc_roi->y, + rc_roi->w, rc_roi->h); + + return rc; +} + +static int _sde_hw_rc_get_param_rb( + const struct drm_msm_rc_mask_cfg *rc_mask_cfg, + const struct sde_rect *rc_roi, + enum rc_param_r *param_r, + enum rc_param_b *param_b) +{ + int rc = 0; + int half_panel_x = 0, half_panel_w = 0; + int cfg_param_01 = 0, cfg_param_02 = 0; + int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (!rc_mask_cfg || !rc_roi || !param_r || !param_b) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A1) + half_panel_w = rc_mask_cfg->cfg_param_04[0] + + rc_mask_cfg->cfg_param_04[1]; + else if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A0) + half_panel_w = rc_mask_cfg->cfg_param_04[0]; + else { + SDE_ERROR("invalid cfg_param_03:%u\n", + rc_mask_cfg->cfg_param_03); + return -EINVAL; + } + + cfg_param_01 = rc_mask_cfg->cfg_param_01; + cfg_param_02 = rc_mask_cfg->cfg_param_02; + x1 = rc_roi->x; + x2 = rc_roi->x + rc_roi->w - 1; + y1 = rc_roi->y; + y2 = rc_roi->y + rc_roi->h - 1; + half_panel_x = half_panel_w - 1; + + SDE_DEBUG("x1:%u y1:%u x2:%u y2:%u\n", x1, y1, x2, y2); + SDE_DEBUG("cfg_param_01:%u cfg_param_02:%u half_panel_x:%u", + cfg_param_01, cfg_param_02, half_panel_x); + SDE_DEBUG("param_r:0x%08X param_b:0x%08X\n", + *param_r, *param_b); + + if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || half_panel_x < 0 || + x1 >= x2 || y1 >= y2) { + SDE_ERROR("invalid coordinates\n"); + return -EINVAL; + } + + if (y1 <= cfg_param_01) { + *param_r |= RC_PARAM_R1; + if (x1 <= half_panel_x && x2 <= half_panel_x) + *param_b |= RC_PARAM_B1; + else if (x1 > half_panel_x && x2 > half_panel_x) + *param_b |= RC_PARAM_B2; + else + *param_b |= RC_PARAM_B1B2; + } + + if (y2 >= cfg_param_02) { + *param_r |= RC_PARAM_R2; + if (x1 <= half_panel_x && x2 <= half_panel_x) + *param_b |= RC_PARAM_B1; + else if (x1 > half_panel_x && x2 > half_panel_x) + *param_b |= RC_PARAM_B2; + else + *param_b |= RC_PARAM_B1B2; + } + + return rc; +} + +static int _sde_hw_rc_program_enable_bits( + struct sde_hw_dspp *hw_dspp, + struct drm_msm_rc_mask_cfg *rc_mask_cfg, + enum rc_param_r param_r, + enum rc_param_a param_a, + enum rc_param_b param_b, + int merge_mode, + struct sde_rect *rc_roi) +{ + int rc = 0; + u32 val = 0, param_c = 0, rc_merge_mode = 0, ystart = 0; + + if (!hw_dspp || !rc_mask_cfg || !rc_roi) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + rc = _sde_hw_rc_get_enable_bits(param_a, param_b, ¶m_c, + merge_mode, &rc_merge_mode); + if (rc) { + SDE_ERROR("invalid enable bits, rc:%d\n", rc); + return rc; + } + + if (param_r & RC_PARAM_R1) { + val |= BIT(0); + SDE_DEBUG("enable R1\n"); + } + + if (param_r & RC_PARAM_R2) { + val |= BIT(4); + SDE_DEBUG("enable R2\n"); + } + + /*corner case for partial update*/ + if (param_r == RC_PARAM_R0) { + ystart = rc_roi->y; + SDE_DEBUG("set partial update ystart:%u\n", ystart); + } + + if ((rc_mask_cfg->flags & SDE_HW_RC_DISABLE_R1) + == SDE_HW_RC_DISABLE_R1) { + val &= ~BIT(0); + SDE_DEBUG("override disable R1\n"); + } + + if ((rc_mask_cfg->flags & SDE_HW_RC_DISABLE_R2) + == SDE_HW_RC_DISABLE_R2) { + val &= ~BIT(4); + SDE_DEBUG("override disable R2\n"); + } + + val |= param_c; + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG1, val); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG13, ystart); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG9, rc_merge_mode); + + return rc; +} + +static int _sde_hw_rc_program_roi( + struct sde_hw_dspp *hw_dspp, + struct drm_msm_rc_mask_cfg *rc_mask_cfg, + int merge_mode, + struct sde_rect *rc_roi) +{ + int rc = 0; + u32 val2 = 0, val3 = 0, val4 = 0; + enum rc_param_r param_r = RC_PARAM_R0; + enum rc_param_a param_a = RC_PARAM_A0; + enum rc_param_b param_b = RC_PARAM_B0; + + if (!hw_dspp || !rc_mask_cfg || !rc_roi) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, rc_roi, ¶m_r, + ¶m_b); + if (rc) { + SDE_ERROR("invalid rc roi, rc:%d\n", rc); + return rc; + } + + param_a = rc_mask_cfg->cfg_param_03; + rc = _sde_hw_rc_program_enable_bits(hw_dspp, rc_mask_cfg, + param_r, param_a, param_b, merge_mode, rc_roi); + if (rc) { + SDE_ERROR("failed to program enable bits, rc:%d\n", rc); + return rc; + } + + val2 = ((rc_mask_cfg->cfg_param_01 & 0x0000FFFF) | + ((rc_mask_cfg->cfg_param_02 << 16) & 0xFFFF0000)); + if (param_a == RC_PARAM_A1) { + val3 = (rc_mask_cfg->cfg_param_04[0] | + (rc_mask_cfg->cfg_param_04[1] << 16)); + val4 = (rc_mask_cfg->cfg_param_04[2] | + (rc_mask_cfg->cfg_param_04[3] << 16)); + } else if (param_a == RC_PARAM_A0) { + val3 = (rc_mask_cfg->cfg_param_04[0]); + val4 = (rc_mask_cfg->cfg_param_04[1]); + } + + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG2, val2); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG3, val3); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG4, val4); + + return 0; +} + +static int _sde_hw_rc_program_data_offset( + struct sde_hw_dspp *hw_dspp, + struct drm_msm_rc_mask_cfg *rc_mask_cfg) +{ + int rc = 0; + u32 val5 = 0, val6 = 0, val7 = 0, val8 = 0; + u32 cfg_param_07; + + if (!hw_dspp || !rc_mask_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + cfg_param_07 = rc_mask_cfg->cfg_param_07; + if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A1) { + val5 = ((rc_mask_cfg->cfg_param_05[0] + cfg_param_07) | + ((rc_mask_cfg->cfg_param_05[1] + cfg_param_07) + << 16)); + val6 = ((rc_mask_cfg->cfg_param_05[2] + cfg_param_07)| + ((rc_mask_cfg->cfg_param_05[3] + cfg_param_07) + << 16)); + val7 = ((rc_mask_cfg->cfg_param_06[0] + cfg_param_07) | + ((rc_mask_cfg->cfg_param_06[1] + cfg_param_07) + << 16)); + val8 = ((rc_mask_cfg->cfg_param_06[2] + cfg_param_07) | + ((rc_mask_cfg->cfg_param_06[3] + cfg_param_07) + << 16)); + } else if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A0) { + val5 = (rc_mask_cfg->cfg_param_05[0] + cfg_param_07); + val6 = (rc_mask_cfg->cfg_param_05[1] + cfg_param_07); + val7 = (rc_mask_cfg->cfg_param_06[0] + cfg_param_07); + val8 = (rc_mask_cfg->cfg_param_06[1] + cfg_param_07); + } + + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG5, val5); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG6, val6); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG7, val7); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG8, val8); + + return rc; +} + +static int sde_hw_rc_check_mask_cfg( + struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct drm_msm_rc_mask_cfg *rc_mask_cfg) +{ + int rc = 0; + u32 i = 0; + u32 half_panel_width; + + if (!hw_dspp || !hw_cfg || !rc_mask_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (!rc_mask_cfg->cfg_param_08 || + rc_mask_cfg->cfg_param_08 > RC_DATA_SIZE_MAX) { + SDE_ERROR("invalid cfg_param_08:%d\n", + rc_mask_cfg->cfg_param_08); + return -EINVAL; + } + + if (rc_mask_cfg->cfg_param_07 + rc_mask_cfg->cfg_param_08 > + hw_dspp->cap->sblk->rc.mem_total_size) { + SDE_ERROR("invalid cfg_param_08:%d, cfg_param_07:%d, max:%u\n", + rc_mask_cfg->cfg_param_08, + rc_mask_cfg->cfg_param_07, + hw_dspp->cap->sblk->rc.mem_total_size); + return -EINVAL; + } + + if (!rc_mask_cfg->cfg_param_03 || + (rc_mask_cfg->cfg_param_03 != RC_PARAM_A1 && + rc_mask_cfg->cfg_param_03 != RC_PARAM_A0)) { + SDE_ERROR("invalid cfg_param_03:%d\n", + rc_mask_cfg->cfg_param_03); + return -EINVAL; + } + + if ((rc_mask_cfg->cfg_param_01 < 1) || + ((hw_cfg->displayv - rc_mask_cfg->cfg_param_02) < 1)) { + SDE_ERROR("invalid min cfg_param_01:%d or cfg_param_02:%d\n", + rc_mask_cfg->cfg_param_01, + rc_mask_cfg->cfg_param_02); + return -EINVAL; + } + + if (rc_mask_cfg->cfg_param_01 > rc_mask_cfg->cfg_param_02) { + SDE_ERROR("invalid cfg_param_01:%d or cfg_param_02:%d\n", + rc_mask_cfg->cfg_param_01, + rc_mask_cfg->cfg_param_02); + return -EINVAL; + } + + for (i = 0; i < rc_mask_cfg->cfg_param_03; i++) { + if (rc_mask_cfg->cfg_param_04[i] < 4) { + SDE_ERROR("invalid cfg_param_04[%d]:%d\n", i, + rc_mask_cfg->cfg_param_04[i]); + return -EINVAL; + } + } + + half_panel_width = hw_cfg->displayh / rc_mask_cfg->cfg_param_03 * 2; + for (i = 0; i < rc_mask_cfg->cfg_param_03; i += 2) { + if (rc_mask_cfg->cfg_param_04[i] + + rc_mask_cfg->cfg_param_04[i+1] != + half_panel_width) { + SDE_ERROR("invalid ratio [%d]:%d, [%d]:%d, %d\n", + i, + rc_mask_cfg->cfg_param_04[i], + i+1, + rc_mask_cfg->cfg_param_04[i+1], + half_panel_width); + return -EINVAL; + } + } + + for (i = 0; i < rc_mask_cfg->cfg_param_03 - 1; i++) { + if (rc_mask_cfg->cfg_param_05[i] >= + rc_mask_cfg->cfg_param_05[i+1]) { + SDE_ERROR("invalid cfg_param_05 overlap %d, %d\n", + rc_mask_cfg->cfg_param_05[i], + rc_mask_cfg->cfg_param_05[i+1]); + return -EINVAL; + } + } + + for (i = 0; i < rc_mask_cfg->cfg_param_03; i++) { + if (rc_mask_cfg->cfg_param_05[i] > + RC_DATA_SIZE_MAX) { + SDE_ERROR("invalid cfg_param_05[%d]:%d\n", i, + rc_mask_cfg->cfg_param_05[i]); + return -EINVAL; + } + + } + + return rc; +} + +int sde_hw_rc_check_mask(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled, skip mask checks\n"); + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + if (hw_cfg->num_of_mixers != 1 && hw_cfg->num_of_mixers != 2) { + SDE_ERROR("invalid number of mixers:%d\n", + hw_cfg->num_of_mixers); + return -EINVAL; + } + + rc = sde_hw_rc_check_mask_cfg(hw_dspp, hw_cfg, rc_mask_cfg); + if (rc) { + SDE_ERROR("invalid rc mask configuration, rc:%d\n", rc); + return rc; + } + + return 0; +} + +int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct msm_roi_list *roi_list; + struct sde_rect rc_roi, merged_roi; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + bool mask_programmed = false; + enum rc_param_r param_r = RC_PARAM_R0; + enum rc_param_b param_b = RC_PARAM_B0; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (hw_cfg->len != sizeof(struct sde_drm_roi_v1)) { + SDE_ERROR("invalid payload size\n"); + return -EINVAL; + } + + rc_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg; + mask_programmed = RC_STATE(hw_dspp).mask_programmed; + + /* early return when there is no mask in memory */ + if (!mask_programmed || !rc_mask_cfg) { + SDE_DEBUG("no previous rc mask programmed\n"); + return SDE_HW_RC_PU_SKIP_OP; + } + + rc = sde_hw_rc_check_mask_cfg(hw_dspp, hw_cfg, rc_mask_cfg); + if (rc) { + SDE_ERROR("invalid rc mask configuration, rc:%d\n", rc); + return rc; + } + + roi_list = hw_cfg->payload; + sde_kms_rect_merge_rectangles(roi_list, &merged_roi); + rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi); + if (rc) { + SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, &rc_roi, + ¶m_r, ¶m_b); + if (rc) { + SDE_ERROR("invalid rc roi, rc:%d\n", rc); + return rc; + } + + return 0; +} + +int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct msm_roi_list *roi_list; + struct sde_rect rc_roi, merged_roi; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + enum rc_param_r param_r = RC_PARAM_R0; + enum rc_param_a param_a = RC_PARAM_A0; + enum rc_param_b param_b = RC_PARAM_B0; + u32 merge_mode = 0; + bool mask_programmed = false; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (hw_cfg->len != sizeof(struct sde_drm_roi_v1)) { + SDE_ERROR("invalid payload size\n"); + return -EINVAL; + } + + rc_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg; + mask_programmed = RC_STATE(hw_dspp).mask_programmed; + + /* early return when there is no mask in memory */ + if (!mask_programmed || !rc_mask_cfg) { + SDE_DEBUG("no previous rc mask programmed\n"); + return SDE_HW_RC_PU_SKIP_OP; + } + + roi_list = hw_cfg->payload; + sde_kms_rect_merge_rectangles(roi_list, &merged_roi); + rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi); + if (rc) { + SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_get_merge_mode(hw_cfg, &merge_mode); + if (rc) { + SDE_ERROR("invalid merge_mode, rc:%d\n"); + return rc; + } + + rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, &rc_roi, ¶m_r, + ¶m_b); + if (rc) { + SDE_ERROR("invalid roi, rc:%d\n", rc); + return rc; + } + + param_a = rc_mask_cfg->cfg_param_03; + rc = _sde_hw_rc_program_enable_bits(hw_dspp, rc_mask_cfg, + param_r, param_a, param_b, merge_mode, &rc_roi); + if (rc) { + SDE_ERROR("failed to program enable bits, rc:%d\n", rc); + return rc; + } + + memcpy(RC_STATE(hw_dspp).last_roi_list, + roi_list, sizeof(struct msm_roi_list)); + RC_STATE(hw_dspp).roi_programmed = true; + + return 0; +} + +int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + struct sde_rect rc_roi, merged_roi; + struct msm_roi_list *last_roi_list; + u32 merge_mode = 0; + bool roi_programmed = false; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled\n"); + + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG1, 0); + + memset(RC_STATE(hw_dspp).last_rc_mask_cfg, 0, + sizeof(struct drm_msm_rc_mask_cfg)); + RC_STATE(hw_dspp).mask_programmed = false; + + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + last_roi_list = RC_STATE(hw_dspp).last_roi_list; + roi_programmed = RC_STATE(hw_dspp).roi_programmed; + + if (!roi_programmed) { + SDE_DEBUG("no previously programmed partial update rois\n"); + memset(&merged_roi, 0, sizeof(struct sde_rect)); + } else { + sde_kms_rect_merge_rectangles(last_roi_list, &merged_roi); + } + + rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi); + if (rc) { + SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_get_merge_mode(hw_cfg, &merge_mode); + if (rc) { + SDE_ERROR("invalid merge_mode, rc:%d\n"); + return rc; + } + + rc = _sde_hw_rc_program_roi(hw_dspp, rc_mask_cfg, + merge_mode, &rc_roi); + if (rc) { + SDE_ERROR("unable to program rc roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_program_data_offset(hw_dspp, rc_mask_cfg); + if (rc) { + SDE_ERROR("unable to program data offsets, rc:%d\n", rc); + return rc; + } + + memcpy(RC_STATE(hw_dspp).last_rc_mask_cfg, rc_mask_cfg, + sizeof(struct drm_msm_rc_mask_cfg)); + RC_STATE(hw_dspp).mask_programmed = true; + + return 0; +} + +int sde_hw_rc_setup_data_dma(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled, skip data programming\n"); + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + + if (rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) { + SDE_DEBUG("skip data programming\n"); + return 0; + } + + rc = reg_dmav1_setup_rc_datav1(hw_dspp, cfg); + if (rc) { + SDE_ERROR("unable to setup rc with dma, rc:%d\n", rc); + return rc; + } + + return rc; +} + +int sde_hw_rc_setup_data_ahb(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0, i = 0; + u32 data = 0, cfg_param_07 = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled, skip data programming\n"); + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + + if (rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) { + SDE_DEBUG("skip data programming\n"); + return 0; + } + + cfg_param_07 = rc_mask_cfg->cfg_param_07; + SDE_DEBUG("cfg_param_07:%u\n", cfg_param_07); + + for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) { + SDE_DEBUG("cfg_param_09[%d] = 0x%016lX at %u\n", i, + rc_mask_cfg->cfg_param_09[i], i + cfg_param_07); + + data = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0; + data |= (rc_mask_cfg->cfg_param_09[i] & 0x3FFFF); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data); + data = ((rc_mask_cfg->cfg_param_09[i] >> + SDE_HW_RC_DATA_REG_SIZE) & 0x3FFFF); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data); + } + + return rc; +} + +int sde_hw_rc_init(struct sde_hw_dspp *hw_dspp) +{ + int rc = 0; + + RC_STATE(hw_dspp).last_roi_list = kzalloc( + sizeof(struct msm_roi_list), GFP_KERNEL); + if (!RC_STATE(hw_dspp).last_roi_list) + return -ENOMEM; + + RC_STATE(hw_dspp).last_rc_mask_cfg = kzalloc( + sizeof(struct drm_msm_rc_mask_cfg), GFP_KERNEL); + if (!RC_STATE(hw_dspp).last_rc_mask_cfg) + return -ENOMEM; + + return rc; +} diff --git a/msm/sde/sde_hw_rc.h b/msm/sde/sde_hw_rc.h new file mode 100644 index 000000000000..9cf3e6c55235 --- /dev/null +++ b/msm/sde/sde_hw_rc.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _SDE_HW_RC_H +#define _SDE_HW_RC_H + +#include "sde_hw_mdss.h" + + +/** + * sde_hw_rc_init - Initialize RC internal state object and ops + * @hw_dspp: DSPP instance. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_init(struct sde_hw_dspp *hw_dspp); + +/** + * sde_hw_rc_check_mask - Validate RC mask configuration + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_check_mask(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_mask - Setup RC mask configuration + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_check_pu_roi - Validate RC partial update region of interest + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success. + * > 0 on early return. + * < 0 on error. + */ +int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_pu_roi - Setup RC partial update region of interest + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success. + * > 0 on early return. + * < 0 on error. + */ +int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_data_ahb - Program mask data with AHB + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_setup_data_ahb(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_data_dma - Program mask data with DMA + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_setup_data_dma(struct sde_hw_dspp *hw_dspp, void *cfg); + +#endif diff --git a/msm/sde/sde_hw_reg_dma_v1.c b/msm/sde/sde_hw_reg_dma_v1.c index bb53a79b3fd4..2f39de3627b6 100644 --- a/msm/sde/sde_hw_reg_dma_v1.c +++ b/msm/sde/sde_hw_reg_dma_v1.c @@ -806,6 +806,8 @@ int init_v12(struct sde_hw_reg_dma *cfg) v1_supported[LTM_INIT] = GRP_LTM_HW_BLK_SELECT; v1_supported[LTM_ROI] = GRP_LTM_HW_BLK_SELECT; v1_supported[LTM_VLUT] = GRP_LTM_HW_BLK_SELECT; + v1_supported[RC_DATA] = (GRP_DSPP_HW_BLK_SELECT | + GRP_MDSS_HW_BLK_SELECT); return 0; } diff --git a/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/msm/sde/sde_hw_reg_dma_v1_color_proc.c index 0cc78e1ccf39..a22787f6bf6d 100644 --- a/msm/sde/sde_hw_reg_dma_v1_color_proc.c +++ b/msm/sde/sde_hw_reg_dma_v1_color_proc.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ + #include #include "sde_reg_dma.h" #include "sde_hw_reg_dma_v1_color_proc.h" @@ -65,6 +66,9 @@ #define MEMCOLOR_MEM_SIZE ((sizeof(struct drm_msm_memcol)) + \ REG_DMA_HEADERS_BUFFER_SZ) +#define RC_MEM_SIZE ((RC_DATA_SIZE_MAX * 2 * sizeof(u32)) + \ + REG_DMA_HEADERS_BUFFER_SZ) + #define QSEED3_MEM_SIZE (sizeof(struct sde_hw_scaler3_cfg) + \ (450 * sizeof(u32)) + \ REG_DMA_HEADERS_BUFFER_SZ) @@ -133,6 +137,7 @@ static u32 feature_map[SDE_DSPP_MAX] = { [SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX, [SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX, [SDE_DSPP_AD] = REG_DMA_FEATURES_MAX, + [SDE_DSPP_RC] = RC_DATA, }; static u32 sspp_feature_map[SDE_SSPP_MAX] = { @@ -159,6 +164,7 @@ static u32 feature_reg_dma_sz[SDE_DSPP_MAX] = { [SDE_DSPP_HSIC] = HSIC_MEM_SIZE, [SDE_DSPP_SIXZONE] = SIXZONE_MEM_SIZE, [SDE_DSPP_MEMCOLOR] = MEMCOLOR_MEM_SIZE, + [SDE_DSPP_RC] = RC_MEM_SIZE, }; static u32 sspp_feature_reg_dma_sz[SDE_SSPP_MAX] = { @@ -1117,6 +1123,89 @@ void reg_dmav1_setup_dspp_igcv31(struct sde_hw_dspp *ctx, void *cfg) DRM_ERROR("failed to kick off ret %d\n", rc); } +int reg_dmav1_setup_rc_datav1(struct sde_hw_dspp *ctx, void *cfg) +{ + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + struct sde_hw_reg_dma_ops *dma_ops; + struct sde_reg_dma_kickoff_cfg kick_off; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct sde_reg_dma_setup_ops_cfg dma_write_cfg; + int rc = 0; + u32 i = 0; + u32 *data = NULL; + u32 buf_sz = 0, abs_offset = 0; + u32 cfg_param_07; + u64 cfg_param_09; + + rc = reg_dma_dspp_check(ctx, cfg, RC_DATA); + if (rc) { + DRM_ERROR("invalid dma dspp check rc = %d\n"); + return -EINVAL; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg)) { + DRM_ERROR("invalid size of payload len %d exp %zd\n", + hw_cfg->len, sizeof(struct drm_msm_rc_mask_cfg)); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + buf_sz = rc_mask_cfg->cfg_param_08 * 2 * sizeof(u32); + abs_offset = ctx->hw.blk_off + ctx->cap->sblk->rc.base + 0x28; + + dma_ops = sde_reg_dma_get_ops(); + dma_ops->reset_reg_dma_buf(dspp_buf[RC_DATA][ctx->idx]); + REG_DMA_INIT_OPS(dma_write_cfg, MDSS, RC_DATA, + dspp_buf[RC_DATA][ctx->idx]); + + REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); + rc = dma_ops->setup_payload(&dma_write_cfg); + if (rc) { + DRM_ERROR("write decode select failed ret %d\n", rc); + return -ENOMEM; + } + + DRM_DEBUG_DRIVER("allocating %u bytes of memory for dma\n", buf_sz); + data = kzalloc(buf_sz, GFP_KERNEL); + if (!data) { + DRM_ERROR("memory allocation failed ret %d\n", rc); + return -ENOMEM; + } + + cfg_param_07 = rc_mask_cfg->cfg_param_07; + for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) { + cfg_param_09 = rc_mask_cfg->cfg_param_09[i]; + DRM_DEBUG_DRIVER("cfg_param_09[%d] = 0x%016lX at %u\n", i, + cfg_param_09, + i + cfg_param_07); + data[i * 2] = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0; + data[i * 2] |= (cfg_param_09 & 0x3FFFF); + data[i * 2 + 1] = ((cfg_param_09 >> 18) & 0x3FFFF); + } + + REG_DMA_SETUP_OPS(dma_write_cfg, abs_offset, data, buf_sz, + REG_BLK_WRITE_INC, 0, 0, 0); + rc = dma_ops->setup_payload(&dma_write_cfg); + if (rc) { + DRM_ERROR("rc dma write failed ret %d\n", rc); + goto exit; + } + + /* defer trigger to kickoff phase */ + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + dspp_buf[RC_DATA][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_TRIGGER); + rc = dma_ops->kick_off(&kick_off); + if (rc) { + DRM_ERROR("failed to kick off ret %d\n", rc); + goto exit; + } + +exit: + kfree(data); + return rc; +} + static void _dspp_pccv4_off(struct sde_hw_dspp *ctx, void *cfg) { struct sde_reg_dma_kickoff_cfg kick_off; diff --git a/msm/sde/sde_hw_reg_dma_v1_color_proc.h b/msm/sde/sde_hw_reg_dma_v1_color_proc.h index 6a58aa3b5c07..af6378836ed8 100644 --- a/msm/sde/sde_hw_reg_dma_v1_color_proc.h +++ b/msm/sde/sde_hw_reg_dma_v1_color_proc.h @@ -2,6 +2,7 @@ /* * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ + #ifndef _SDE_HW_REG_DMA_V1_COLOR_PROC_H #define _SDE_HW_REG_DMA_V1_COLOR_PROC_H @@ -242,6 +243,14 @@ void reg_dmav1_setup_ltm_roiv1(struct sde_hw_dspp *ctx, void *cfg); */ void reg_dmav1_setup_ltm_vlutv1(struct sde_hw_dspp *ctx, void *cfg); +/** + * reg_dmav1_setup_rc_datav1() - RC DATA v1 implementation using reg dma v1. + * @ctx: dspp ctx info + * @cfg: pointer to struct sde_hw_cp_cfg + * @returns: 0 if success, non-zero otherwise + */ +int reg_dmav1_setup_rc_datav1(struct sde_hw_dspp *ctx, void *cfg); + /** * reg_dmav1_deinit_ltm_ops() - deinitialize the ltm feature op for sde v4 * which were initialized. diff --git a/msm/sde/sde_reg_dma.h b/msm/sde/sde_reg_dma.h index 50cd5cea5668..428cbcff6ad4 100644 --- a/msm/sde/sde_reg_dma.h +++ b/msm/sde/sde_reg_dma.h @@ -57,6 +57,7 @@ enum sde_reg_dma_read_sel { * @LTM_INIT: LTM INIT * @LTM_ROI: LTM ROI * @LTM_VLUT: LTM VLUT + * @RC_DATA: Rounded corner data * @REG_DMA_FEATURES_MAX: invalid selection */ enum sde_reg_dma_features { @@ -75,6 +76,7 @@ enum sde_reg_dma_features { LTM_INIT, LTM_ROI, LTM_VLUT, + RC_DATA, REG_DMA_FEATURES_MAX, }; @@ -152,6 +154,7 @@ enum sde_reg_dma_setup_ops { }; #define REG_DMA_BLK_MAX 32 + /** * enum sde_reg_dma_blk - defines blocks for which reg dma op should be * performed From 1690129d6074c2879ef128c7be07764bb91cfbc1 Mon Sep 17 00:00:00 2001 From: Gopikrishnaiah Anandan Date: Tue, 10 Dec 2019 18:24:20 -0800 Subject: [PATCH 5/5] drm: msm: handle resolution switch for LTM DPU configuration should be changed when resolution switch happens at the layer mixer level for ltm feature. Driver should mark ltm properties as dirty when resolution switch happens. Change handles dynamic resolution switch for ltm by marking the properties as dirty. Change-Id: I5ffc8e74c42da6c2902eb42fd2e3ed1b9f9e3e4c Signed-off-by: Gopikrishnaiah Anandan --- msm/sde/sde_color_processing.c | 23 +++++++++++++++++++++++ msm/sde/sde_color_processing.h | 6 ++++++ msm/sde/sde_crtc.c | 1 + 3 files changed, 30 insertions(+) diff --git a/msm/sde/sde_color_processing.c b/msm/sde/sde_color_processing.c index 35bf9aca8018..97b8df157c56 100644 --- a/msm/sde/sde_color_processing.c +++ b/msm/sde/sde_color_processing.c @@ -3933,3 +3933,26 @@ int sde_cp_ltm_off_event_handler(struct drm_crtc *crtc_drm, bool en, { return 0; } + +void sde_cp_mode_switch_prop_dirty(struct drm_crtc *crtc_drm) +{ + struct sde_cp_node *prop_node = NULL, *n = NULL; + struct sde_crtc *crtc; + + if (!crtc_drm) { + DRM_ERROR("invalid crtc handle"); + return; + } + crtc = to_sde_crtc(crtc_drm); + mutex_lock(&crtc->crtc_cp_lock); + list_for_each_entry_safe(prop_node, n, &crtc->active_list, + active_list) { + if (prop_node->feature == SDE_CP_CRTC_DSPP_LTM_INIT || + prop_node->feature == SDE_CP_CRTC_DSPP_LTM_VLUT) { + list_del_init(&prop_node->active_list); + list_add_tail(&prop_node->dirty_list, + &crtc->dirty_list); + } + } + mutex_unlock(&crtc->crtc_cp_lock); +} diff --git a/msm/sde/sde_color_processing.h b/msm/sde/sde_color_processing.h index 9c03e65b8e81..45f7781cb32d 100644 --- a/msm/sde/sde_color_processing.h +++ b/msm/sde/sde_color_processing.h @@ -199,4 +199,10 @@ int sde_cp_ltm_wb_pb_interrupt(struct drm_crtc *crtc_drm, bool en, */ int sde_cp_ltm_off_event_handler(struct drm_crtc *crtc_drm, bool en, struct sde_irq_callback *hist_irq); + +/** + * sde_cp_mode_switch_prop_dirty: API marks mode dependent features as dirty + * @crtc_drm: Pointer to crtc. + */ +void sde_cp_mode_switch_prop_dirty(struct drm_crtc *crtc_drm); #endif /*_SDE_COLOR_PROCESSING_H */ diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 91f711137c5c..e9af5f49810f 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -429,6 +429,7 @@ static bool sde_crtc_mode_fixup(struct drm_crtc *crtc, { SDE_DEBUG("\n"); + sde_cp_mode_switch_prop_dirty(crtc); if ((msm_is_mode_seamless(adjusted_mode) || (msm_is_mode_seamless_vrr(adjusted_mode) || msm_is_mode_seamless_dyn_clk(adjusted_mode))) &&