diff --git a/include/uapi/display/drm/msm_drm_pp.h b/include/uapi/display/drm/msm_drm_pp.h index 59d23bef39..d2eb1f2a5d 100644 --- a/include/uapi/display/drm/msm_drm_pp.h +++ b/include/uapi/display/drm/msm_drm_pp.h @@ -412,10 +412,12 @@ struct drm_msm_ad4_cfg { }; #define DITHER_MATRIX_SZ 16 +#define DITHER_LUMA_MODE (1 << 0) /** * struct drm_msm_dither - dither feature structure - * @flags: for customizing operations + * @flags: flags for the feature customization, values can be: + -DITHER_LUMA_MODE: Enable LUMA dither mode * @temporal_en: temperal dither enable * @c0_bitdepth: c0 component bit depth * @c1_bitdepth: c1 component bit depth diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 561c0d1724..16c566779a 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -29,9 +29,6 @@ #define SDE_ERROR_CONN(c, fmt, ...) SDE_ERROR("conn%d " fmt,\ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) -static u32 dither_matrix[DITHER_MATRIX_SZ] = { - 15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10 -}; static const struct drm_prop_enum_list e_topology_name[] = { {SDE_RM_TOPOLOGY_NONE, "sde_none"}, @@ -245,60 +242,12 @@ void sde_connector_unregister_event(struct drm_connector *connector, (void)sde_connector_register_event(connector, event_idx, 0, 0); } -static int _sde_connector_get_default_dither_cfg_v1( - struct sde_connector *c_conn, void *cfg) -{ - struct drm_msm_dither *dither_cfg = (struct drm_msm_dither *)cfg; - enum dsi_pixel_format dst_format = DSI_PIXEL_FORMAT_MAX; - - if (!c_conn || !cfg) { - SDE_ERROR("invalid argument(s), c_conn %pK, cfg %pK\n", - c_conn, cfg); - return -EINVAL; - } - - if (!c_conn->ops.get_dst_format) { - SDE_DEBUG("get_dst_format is unavailable\n"); - return 0; - } - - dst_format = c_conn->ops.get_dst_format(&c_conn->base, c_conn->display); - switch (dst_format) { - case DSI_PIXEL_FORMAT_RGB888: - dither_cfg->c0_bitdepth = 8; - dither_cfg->c1_bitdepth = 8; - dither_cfg->c2_bitdepth = 8; - dither_cfg->c3_bitdepth = 8; - break; - case DSI_PIXEL_FORMAT_RGB666: - case DSI_PIXEL_FORMAT_RGB666_LOOSE: - dither_cfg->c0_bitdepth = 6; - dither_cfg->c1_bitdepth = 6; - dither_cfg->c2_bitdepth = 6; - dither_cfg->c3_bitdepth = 6; - break; - default: - SDE_DEBUG("no default dither config for dst_format %d\n", - dst_format); - return -ENODATA; - } - - memcpy(&dither_cfg->matrix, dither_matrix, - sizeof(u32) * DITHER_MATRIX_SZ); - dither_cfg->temporal_en = 0; - return 0; -} - static void _sde_connector_install_dither_property(struct drm_device *dev, struct sde_kms *sde_kms, struct sde_connector *c_conn) { char prop_name[DRM_PROP_NAME_LEN]; struct sde_mdss_cfg *catalog = NULL; - struct drm_property_blob *blob_ptr; - void *cfg; - int ret = 0; - u32 version = 0, len = 0; - bool defalut_dither_needed = false; + u32 version = 0; if (!dev || !sde_kms || !c_conn) { SDE_ERROR("invld args (s), dev %pK, sde_kms %pK, c_conn %pK\n", @@ -313,57 +262,55 @@ static void _sde_connector_install_dither_property(struct drm_device *dev, "SDE_PP_DITHER_V", version); switch (version) { case 1: + case 2: msm_property_install_blob(&c_conn->property_info, prop_name, DRM_MODE_PROP_BLOB, CONNECTOR_PROP_PP_DITHER); - len = sizeof(struct drm_msm_dither); - cfg = kzalloc(len, GFP_KERNEL); - if (!cfg) - return; - - ret = _sde_connector_get_default_dither_cfg_v1(c_conn, cfg); - if (!ret) - defalut_dither_needed = true; break; default: SDE_ERROR("unsupported dither version %d\n", version); return; } - - if (defalut_dither_needed) { - blob_ptr = drm_property_create_blob(dev, len, cfg); - if (IS_ERR_OR_NULL(blob_ptr)) - goto exit; - c_conn->blob_dither = blob_ptr; - } -exit: - kfree(cfg); } int sde_connector_get_dither_cfg(struct drm_connector *conn, struct drm_connector_state *state, void **cfg, - size_t *len) + size_t *len, bool idle_pc) { struct sde_connector *c_conn = NULL; struct sde_connector_state *c_state = NULL; size_t dither_sz = 0; + bool is_dirty; u32 *p = (u32 *)cfg; - if (!conn || !state || !p) + if (!conn || !state || !p) { + SDE_ERROR("invalid arguments\n"); return -EINVAL; + } c_conn = to_sde_connector(conn); c_state = to_sde_connector_state(state); - /* try to get user config data first */ - *cfg = msm_property_get_blob(&c_conn->property_info, - &c_state->property_state, - &dither_sz, - CONNECTOR_PROP_PP_DITHER); - /* if user config data doesn't exist, use default dither blob */ - if (*cfg == NULL && c_conn->blob_dither) { - *cfg = &c_conn->blob_dither->data; - dither_sz = c_conn->blob_dither->length; + is_dirty = msm_property_is_dirty(&c_conn->property_info, + &c_state->property_state, + CONNECTOR_PROP_PP_DITHER); + + if (!is_dirty && !idle_pc) { + return -ENODATA; + } else if (is_dirty || idle_pc) { + *cfg = msm_property_get_blob(&c_conn->property_info, + &c_state->property_state, + &dither_sz, + CONNECTOR_PROP_PP_DITHER); + /* + * in idle_pc use case return early, + * when dither is already disabled. + */ + if (idle_pc && *cfg == NULL) + return -ENODATA; + /* disable dither based on user config data */ + else if (*cfg == NULL) + return 0; } *len = dither_sz; return 0; diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index bc523669d1..c40a194220 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -854,10 +854,12 @@ static inline bool sde_connector_needs_offset(struct drm_connector *connector) * @state: Pointer to drm_connector_state struct * @cfg: Pointer to pointer to dither cfg * @len: length of the dither data + * @idle_pc: flag to indicate idle_pc_restore happened * Returns: Zero on success */ int sde_connector_get_dither_cfg(struct drm_connector *conn, - struct drm_connector_state *state, void **cfg, size_t *len); + struct drm_connector_state *state, void **cfg, + size_t *len, bool idle_pc); /** * sde_connector_set_blob_data - set connector blob property data diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index e2e3aa248b..e350163fce 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -2354,6 +2354,61 @@ static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc) memset(&sde_enc->cur_conn_roi, 0, sizeof(sde_enc->cur_conn_roi)); } +static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) +{ + void *dither_cfg = NULL; + int ret = 0, i = 0; + size_t len = 0; + enum sde_rm_topology_name topology; + struct drm_encoder *drm_enc; + struct msm_display_dsc_info *dsc = NULL; + struct sde_encoder_virt *sde_enc; + struct sde_hw_pingpong *hw_pp; + u32 bpp, bpc; + + if (!phys || !phys->connector || !phys->hw_pp || + !phys->hw_pp->ops.setup_dither || !phys->parent) + return; + + topology = sde_connector_get_topology_name(phys->connector); + if ((topology == SDE_RM_TOPOLOGY_PPSPLIT) && + (phys->split_role == ENC_ROLE_SLAVE)) + return; + + drm_enc = phys->parent; + sde_enc = to_sde_encoder_virt(drm_enc); + dsc = &sde_enc->mode_info.comp_info.dsc_info; + + bpc = dsc->config.bits_per_component; + bpp = dsc->config.bits_per_pixel; + + /* disable dither for 10 bpp or 10bpc dsc config */ + if (bpp == 10 || bpc == 10) { + phys->hw_pp->ops.setup_dither(phys->hw_pp, NULL, 0); + return; + } + + ret = sde_connector_get_dither_cfg(phys->connector, + phys->connector->state, &dither_cfg, + &len, sde_enc->idle_pc_restore); + + /* skip reg writes when return values are invalid or no data */ + if (ret && ret == -ENODATA) + return; + + if (TOPOLOGY_DUALPIPE_MERGE_MODE(topology)) { + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + hw_pp = sde_enc->hw_pp[i]; + phys->hw_pp->ops.setup_dither(hw_pp, + dither_cfg, len); + } + + } else { + phys->hw_pp->ops.setup_dither(phys->hw_pp, + dither_cfg, len); + } +} + void sde_encoder_virt_restore(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; @@ -2386,6 +2441,8 @@ void sde_encoder_virt_restore(struct drm_encoder *drm_enc) if ((phys != sde_enc->cur_master) && phys->ops.restore) phys->ops.restore(phys); + + _sde_encoder_setup_dither(phys); } if (sde_enc->cur_master->ops.restore) @@ -3452,55 +3509,6 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc) sde_enc->idle_pc_restore = false; } -static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) -{ - void *dither_cfg; - int ret = 0, i = 0; - size_t len = 0; - enum sde_rm_topology_name topology; - struct drm_encoder *drm_enc; - struct msm_display_dsc_info *dsc = NULL; - struct sde_encoder_virt *sde_enc; - struct sde_hw_pingpong *hw_pp; - u16 bpp; - - if (!phys || !phys->connector || !phys->hw_pp || - !phys->hw_pp->ops.setup_dither || !phys->parent) - return; - - topology = sde_connector_get_topology_name(phys->connector); - if ((topology == SDE_RM_TOPOLOGY_PPSPLIT) && - (phys->split_role == ENC_ROLE_SLAVE)) - return; - - drm_enc = phys->parent; - sde_enc = to_sde_encoder_virt(drm_enc); - dsc = &sde_enc->mode_info.comp_info.dsc_info; - /* disable dither for 10 bpp or 10bpc dsc config */ - bpp = DSC_BPP(dsc->config); - if (bpp == 10 || dsc->config.bits_per_component == 10) { - phys->hw_pp->ops.setup_dither(phys->hw_pp, NULL, 0); - return; - } - - ret = sde_connector_get_dither_cfg(phys->connector, - phys->connector->state, &dither_cfg, &len); - if (ret) - return; - - if (TOPOLOGY_DUALPIPE_MERGE_MODE(topology)) { - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { - hw_pp = sde_enc->hw_pp[i]; - if (hw_pp) { - phys->hw_pp->ops.setup_dither(hw_pp, dither_cfg, - len); - } - } - } else { - phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len); - } -} - static u32 _sde_encoder_calculate_linetime(struct sde_encoder_virt *sde_enc, struct drm_display_mode *mode) { diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 3a3bad493b..ed6126d340 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -3344,6 +3344,9 @@ static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) sblk->dither.version = PROP_VALUE_ACCESS(prop_value, DITHER_VER, 0); + if (sde_cfg->dither_luma_mode_support) + set_bit(SDE_PINGPONG_DITHER_LUMA, &pp->features); + if (prop_exists[PP_MERGE_3D_ID]) { set_bit(SDE_PINGPONG_MERGE_3D, &pp->features); pp->merge_3d_id = PROP_VALUE_ACCESS(prop_value, @@ -4561,6 +4564,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) sde_cfg->true_inline_rot_rev = SDE_INLINE_ROT_VERSION_2_0_0; sde_cfg->uidle_cfg.uidle_rev = SDE_UIDLE_VERSION_1_0_1; sde_cfg->vbif_disable_inner_outer_shareable = true; + sde_cfg->dither_luma_mode_support = true; } else { SDE_ERROR("unsupported chipset id:%X\n", hw_rev); sde_cfg->perf.min_prefill_lines = 0xffff; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 2318050d75..cc7251279d 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -367,12 +367,13 @@ enum { /** * PINGPONG sub-blocks - * @SDE_PINGPONG_TE Tear check block - * @SDE_PINGPONG_TE2 Additional tear check block for split pipes - * @SDE_PINGPONG_SPLIT PP block supports split fifo - * @SDE_PINGPONG_SLAVE PP block is a suitable slave for split fifo - * @SDE_PINGPONG_DSC, Display stream compression blocks - * @SDE_PINGPONG_DITHER, Dither blocks + * @SDE_PINGPONG_TE Tear check block + * @SDE_PINGPONG_TE2 Additional tear check block for split pipes + * @SDE_PINGPONG_SPLIT PP block supports split fifo + * @SDE_PINGPONG_SLAVE PP block is a suitable slave for split fifo + * @SDE_PINGPONG_DSC, Display stream compression blocks + * @SDE_PINGPONG_DITHER, Dither blocks + * @SDE_PINGPONG_DITHER_LUMA, Dither sub-blocks and features * @SDE_PINGPONG_MERGE_3D, Separate MERGE_3D block exists * @SDE_PINGPONG_MAX */ @@ -383,6 +384,7 @@ enum { SDE_PINGPONG_SLAVE, SDE_PINGPONG_DSC, SDE_PINGPONG_DITHER, + SDE_PINGPONG_DITHER_LUMA, SDE_PINGPONG_MERGE_3D, SDE_PINGPONG_MAX }; @@ -1407,6 +1409,7 @@ struct sde_limit_cfg { * @has_mixer_combined_alpha Mixer has single register for FG & BG alpha * @vbif_disable_inner_outer_shareable VBIF requires disabling shareables * @inline_disable_const_clr Disable constant color during inline rotate + * @dither_luma_mode_support Enables dither luma mode * @sc_cfg: system cache configuration * @uidle_cfg Settings for uidle feature * @sui_misr_supported indicate if secure-ui-misr is supported @@ -1464,6 +1467,7 @@ struct sde_mdss_cfg { bool has_mixer_combined_alpha; bool vbif_disable_inner_outer_shareable; bool inline_disable_const_clr; + bool dither_luma_mode_support; struct sde_sc_cfg sc_cfg; diff --git a/msm/sde/sde_hw_pingpong.c b/msm/sde/sde_hw_pingpong.c index d49a7895ba..2df4dd4496 100644 --- a/msm/sde/sde_hw_pingpong.c +++ b/msm/sde/sde_hw_pingpong.c @@ -38,6 +38,10 @@ static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = { 0, 0, 0, 0, 0, 1, 2, 3, 3 }; +#define DITHER_VER_MAJOR_1 1 +/* supports LUMA Dither */ +#define DITHER_VER_MAJOR_2 2 + #define MERGE_3D_MODE 0x004 #define MERGE_3D_MUX 0x000 @@ -311,7 +315,7 @@ static int sde_hw_pp_setup_dsc(struct sde_hw_pingpong *pp) return 0; } -static int sde_hw_pp_setup_dither_v1(struct sde_hw_pingpong *pp, +static int sde_hw_pp_setup_dither(struct sde_hw_pingpong *pp, void *cfg, size_t len) { struct sde_hw_blk_reg_map *c; @@ -357,7 +361,12 @@ static int sde_hw_pp_setup_dither_v1(struct sde_hw_pingpong *pp, ((dither->matrix[i + 3] & REG_MASK(4)) << 12); SDE_REG_WRITE(c, base + offset, data); } - SDE_REG_WRITE(c, base, 1); + + if (test_bit(SDE_PINGPONG_DITHER_LUMA, &pp->caps->features) + && (dither->flags & DITHER_LUMA_MODE)) + SDE_REG_WRITE(c, base, 0x11); + else + SDE_REG_WRITE(c, base, 1); return 0; } @@ -491,8 +500,9 @@ static void _setup_pingpong_ops(struct sde_hw_pingpong_ops *ops, version = SDE_COLOR_PROCESS_MAJOR(hw_cap->sblk->dither.version); switch (version) { - case 1: - ops->setup_dither = sde_hw_pp_setup_dither_v1; + case DITHER_VER_MAJOR_1: + case DITHER_VER_MAJOR_2: + ops->setup_dither = sde_hw_pp_setup_dither; break; default: ops->setup_dither = NULL;