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..97b8df157c56 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]; @@ -3775,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 22f4230a21a0..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))) && @@ -755,6 +756,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 +5007,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 5c06a445aeec..b87269a52f20 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 @@ -380,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, @@ -475,6 +486,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 *************************************************************/ @@ -682,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}, @@ -912,6 +944,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 +969,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); @@ -1091,6 +1133,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) { @@ -1709,43 +1795,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); @@ -1766,9 +1838,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, @@ -1840,14 +1912,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; @@ -1855,30 +1920,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; @@ -1887,47 +1940,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; @@ -1942,15 +1978,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; @@ -1960,7 +1996,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) @@ -1995,11 +2031,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); } @@ -2007,9 +2043,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; } @@ -2455,102 +2493,66 @@ 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; - u32 off_count, ad_off_count, ltm_off_count; + int rc = 0, i; + 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; 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; + } + + /* 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; } - 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_rc_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); @@ -2563,17 +2565,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, @@ -2585,11 +2588,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, @@ -2599,13 +2602,34 @@ 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: - kfree(prop_value); - kfree(ad_prop_value); - kfree(ltm_prop_value); - kfree(blocks_prop_value); + 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: + sde_put_dt_props(ad_props); +put_props: + sde_put_dt_props(props); return rc; } @@ -2713,7 +2737,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: diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index d7e40af16709..f7754840a60b 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 { @@ -1428,6 +1443,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; @@ -1540,6 +1556,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]; @@ -1587,6 +1604,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