diff --git a/msm/Makefile b/msm/Makefile index 36c65e9134..e2f9f64594 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 7f07222cd3..35bf9aca80 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 22f4230a21..91f711137c 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 b47c39d54c..d70fa507e9 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 cc7251279d..51396ceb11 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 f67f736ba0..8ec284e133 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 82932741aa..0b493e5906 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 ff2c19cff2..627f76238d 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 0000000000..8957eef8fa --- /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 0000000000..9cf3e6c552 --- /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 bb53a79b3f..2f39de3627 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 0cc78e1ccf..a22787f6bf 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 6a58aa3b5c..af6378836e 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 50cd5cea56..428cbcff6a 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