diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 3a5c19a73c..35ef717ffd 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -193,6 +193,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_HDR_INFO, CONNECTOR_PROP_EXT_HDR_INFO, CONNECTOR_PROP_PP_DITHER, + CONNECTOR_PROP_PP_CWB_DITHER, CONNECTOR_PROP_HDR_METADATA, CONNECTOR_PROP_DEMURA_PANEL_ID, diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index da0a6be02e..f0ff97c148 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -922,6 +922,10 @@ static void _sde_encoder_phys_wb_update_cwb_flush( enum sde_cwb src_pp_idx = 0; bool dspp_out = false; bool need_merge = false; + struct sde_connector *c_conn = NULL; + struct sde_connector_state *c_state = NULL; + void *dither_cfg = NULL; + size_t dither_sz = 0; if (!phys_enc->in_clone_mode) { SDE_DEBUG("not in CWB mode. early return\n"); @@ -976,11 +980,30 @@ static void _sde_encoder_phys_wb_update_cwb_flush( if (test_bit(SDE_WB_CWB_CTRL, &hw_wb->caps->features) || test_bit(SDE_WB_DCWB_CTRL, &hw_wb->caps->features)) { + if (test_bit(SDE_WB_CWB_DITHER_CTRL, &hw_wb->caps->features)) { + if (cwb_capture_mode) { + c_conn = to_sde_connector(phys_enc->connector); + c_state = to_sde_connector_state(phys_enc->connector->state); + dither_cfg = msm_property_get_blob(&c_conn->property_info, + &c_state->property_state, &dither_sz, + CONNECTOR_PROP_PP_CWB_DITHER); + SDE_DEBUG("Read cwb dither setting from blob %pK\n", dither_cfg); + } else { + /* disable case: tap is lm */ + dither_cfg = NULL; + } + } + for (i = 0; i < crtc->num_mixers; i++) { src_pp_idx = (enum sde_cwb) (src_pp_idx + i); if (test_bit(SDE_WB_DCWB_CTRL, &hw_wb->caps->features)) { dcwb_idx = (enum sde_dcwb) ((hw_pp->idx % 2) + i); + if (test_bit(SDE_WB_CWB_DITHER_CTRL, &hw_wb->caps->features)) { + if (hw_wb->ops.program_cwb_dither_ctrl) + hw_wb->ops.program_cwb_dither_ctrl(hw_wb, + dcwb_idx, dither_cfg, dither_sz, enable); + } if (hw_wb->ops.program_dcwb_ctrl) hw_wb->ops.program_dcwb_ctrl(hw_wb, dcwb_idx, src_pp_idx, cwb_capture_mode, diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 7ba228ce1f..0d2627dff9 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -328,6 +328,7 @@ enum { DITHER_OFF, DITHER_LEN, DITHER_VER, + CWB_DITHER, PP_MERGE_3D_ID, PP_PROP_MAX, }; @@ -838,6 +839,7 @@ static struct sde_prop_type pp_prop[] = { {DITHER_OFF, "qcom,sde-dither-off", false, PROP_TYPE_U32_ARRAY}, {DITHER_LEN, "qcom,sde-dither-size", false, PROP_TYPE_U32}, {DITHER_VER, "qcom,sde-dither-version", false, PROP_TYPE_U32}, + {CWB_DITHER, "qcom,sde-cwb-dither", false, PROP_TYPE_U32_ARRAY}, {PP_MERGE_3D_ID, "qcom,sde-pp-merge-3d-id", false, PROP_TYPE_U32_ARRAY}, }; @@ -2548,6 +2550,10 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) sde_cfg->cwb_blk_off = 0x83000; sde_cfg->cwb_blk_stride = 0x100; } + + if (sde_cfg->has_cwb_dither) + set_bit(SDE_WB_CWB_DITHER_CTRL, &wb->features); + } else if (sde_cfg->has_cwb_support) { set_bit(SDE_WB_HAS_CWB, &wb->features); if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev)) @@ -3842,6 +3848,11 @@ 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->has_cwb_dither && + PROP_VALUE_ACCESS(prop_value, CWB_DITHER, i)) { + set_bit(SDE_PINGPONG_CWB_DITHER, &pp->features); + } + if (sde_cfg->dither_luma_mode_support) set_bit(SDE_PINGPONG_DITHER_LUMA, &pp->features); @@ -5031,6 +5042,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) sde_cfg->syscache_supported = true; } else if (IS_WAIPIO_TARGET(hw_rev)) { sde_cfg->has_dedicated_cwb_support = true; + sde_cfg->has_cwb_dither = true; sde_cfg->has_wb_ubwc = true; sde_cfg->has_cwb_crop = true; sde_cfg->has_qsync = true; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 02ebdfe511..85a4eb9309 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -429,6 +429,7 @@ enum { * @SDE_PINGPONG_DITHER_LUMA, Dither sub-blocks and features * @SDE_PINGPONG_MERGE_3D, Separate MERGE_3D block exists * @SDE_PINGPONG_CWB, PP block supports CWB + * @SDE_PINGPONG_CWB_DITHER, PP block supports CWB dither * @SDE_PINGPONG_MAX */ enum { @@ -441,6 +442,7 @@ enum { SDE_PINGPONG_DITHER_LUMA, SDE_PINGPONG_MERGE_3D, SDE_PINGPONG_CWB, + SDE_PINGPONG_CWB_DITHER, SDE_PINGPONG_MAX }; @@ -549,6 +551,7 @@ enum { * @SDE_WB_CROP CWB supports cropping * @SDE_WB_CWB_CTRL Separate CWB control is available for configuring * @SDE_WB_DCWB_CTRL Separate DCWB control is available for configuring + * @SDE_WB_CWB_DITHER_CTRL CWB dither is available for configuring * @SDE_WB_MAX maximum value */ enum { @@ -573,6 +576,7 @@ enum { SDE_WB_CROP, SDE_WB_CWB_CTRL, SDE_WB_DCWB_CTRL, + SDE_WB_CWB_DITHER_CTRL, SDE_WB_MAX }; @@ -1487,6 +1491,7 @@ struct sde_perf_cfg { * @has_cwb_crop CWB cropping is supported * @has_cwb_support indicates if device supports primary capture through CWB * @has_dedicated_cwb_support indicates if device supports dedicated path for CWB capture + * @has_cwb_dither indicates if device supports cwb dither feature * @cwb_blk_off CWB offset address * @cwb_blk_stride offset between each CWB blk * @ubwc_version UBWC feature version (0x0 for not supported) @@ -1573,6 +1578,7 @@ struct sde_mdss_cfg { bool has_cwb_crop; bool has_cwb_support; bool has_dedicated_cwb_support; + bool has_cwb_dither; u32 cwb_blk_off; u32 cwb_blk_stride; u32 ubwc_version; diff --git a/msm/sde/sde_hw_pingpong.c b/msm/sde/sde_hw_pingpong.c index 5d4edd7b40..cc2b27f334 100644 --- a/msm/sde/sde_hw_pingpong.c +++ b/msm/sde/sde_hw_pingpong.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ #include @@ -33,11 +33,6 @@ #define PP_DCE_DATA_IN_SWAP 0x0ac #define PP_DCE_DATA_OUT_SWAP 0x0c8 -#define DITHER_DEPTH_MAP_INDEX 9 -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 diff --git a/msm/sde/sde_hw_pingpong.h b/msm/sde/sde_hw_pingpong.h index 91760432be..ba6f304c97 100644 --- a/msm/sde/sde_hw_pingpong.h +++ b/msm/sde/sde_hw_pingpong.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ #ifndef _SDE_HW_PINGPONG_H @@ -15,6 +15,11 @@ struct sde_hw_pingpong; struct sde_hw_merge_3d; +#define DITHER_DEPTH_MAP_INDEX 9 +static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = { + 0, 0, 0, 0, 0, 1, 2, 3, 3 +}; + struct sde_hw_dsc_cfg { u8 enable; }; diff --git a/msm/sde/sde_hw_wb.c b/msm/sde/sde_hw_wb.c index 71480f86e8..03f878c7e1 100644 --- a/msm/sde/sde_hw_wb.c +++ b/msm/sde/sde_hw_wb.c @@ -126,6 +126,38 @@ static void _sde_hw_dcwb_ctrl_init(struct sde_mdss_cfg *m, } } +static void _sde_hw_dcwb_pp_ctrl_init(struct sde_mdss_cfg *m, + void __iomem *addr, struct sde_hw_wb *hw_wb) +{ + int i = 0, dcwb_pp_count = 0; + struct sde_pingpong_cfg *pp_blk = NULL; + + if (!hw_wb) { + DRM_ERROR("hw_wb is null\n"); + return; + } + + for (i = 0; i < m->pingpong_count; i++) { + pp_blk = &m->pingpong[i]; + if (test_bit(SDE_PINGPONG_CWB_DITHER, &pp_blk->features)) { + if (dcwb_pp_count < DCWB_MAX - DCWB_0) { + hw_wb->dcwb_pp_hw[dcwb_pp_count].caps = pp_blk; + hw_wb->dcwb_pp_hw[dcwb_pp_count].idx = pp_blk->id; + hw_wb->dcwb_pp_hw[dcwb_pp_count].hw.base_off = addr; + hw_wb->dcwb_pp_hw[dcwb_pp_count].hw.blk_off = pp_blk->base; + hw_wb->dcwb_pp_hw[dcwb_pp_count].hw.length = pp_blk->len; + hw_wb->dcwb_pp_hw[dcwb_pp_count].hw.hwversion = m->hwversion; + hw_wb->dcwb_pp_hw[dcwb_pp_count].hw.log_mask = SDE_DBG_MASK_WB; + } else { + DRM_ERROR("Invalid dcwb pp count %d more than %d", + dcwb_pp_count, DCWB_MAX - DCWB_0); + return; + } + ++dcwb_pp_count; + } + } +} + static void sde_hw_wb_setup_outaddress(struct sde_hw_wb *ctx, struct sde_hw_wb_cfg *data) { @@ -374,6 +406,102 @@ static void sde_hw_wb_program_cwb_ctrl(struct sde_hw_wb *ctx, } } +static void sde_hw_wb_program_cwb_dither_ctrl(struct sde_hw_wb *ctx, + const enum sde_dcwb dcwb_idx, void *cfg, size_t len, bool enable) +{ + struct sde_hw_pingpong *pp = NULL; + struct sde_hw_blk_reg_map *c = NULL; + struct drm_msm_dither *dither_data = NULL; + enum sde_pingpong pp_id = PINGPONG_MAX; + u32 dither_base = 0, offset = 0, data = 0, idx = 0; + bool found = false; + + if (!ctx) { + DRM_ERROR("Invalid pointer ctx is null\n"); + return; + } + + /* map to pp_id from dcwb id */ + if (dcwb_idx == DCWB_0) { + pp_id = PINGPONG_CWB_0; + } else if (dcwb_idx == DCWB_1) { + pp_id = PINGPONG_CWB_1; + } else { + DRM_ERROR("Invalid dcwb_idx %d\n", dcwb_idx); + return; + } + + /* find pp blk with pp_id */ + for (idx = 0; idx < DCWB_MAX - DCWB_0; ++idx) { + pp = &ctx->dcwb_pp_hw[idx]; + if (pp && pp->idx == pp_id) { + found = true; + break; + } + } + + if (!found) { + DRM_ERROR("Not found pp id %d\n", pp_id); + return; + } + + if (!test_bit(SDE_PINGPONG_CWB_DITHER, &pp->caps->features)) { + DRM_ERROR("Invalid ping-pong cwb config dcwb idx %d pp id %d\n", + dcwb_idx, pp_id); + return; + } + + c = &pp->hw; + dither_base = pp->caps->sblk->dither.base; + dither_data = (struct drm_msm_dither *)cfg; + if (!dither_data || !enable) { + SDE_REG_WRITE(c, dither_base, 0); + SDE_DEBUG("cwb dither disabled, dcwb_idx %u pp_id %u\n", dcwb_idx, pp_id); + return; + } + + if (len != sizeof(struct drm_msm_dither)) { + SDE_ERROR("input len %zu, expected len %zu\n", len, + sizeof(struct drm_msm_dither)); + return; + } + + if (dither_data->c0_bitdepth >= DITHER_DEPTH_MAP_INDEX || + dither_data->c1_bitdepth >= DITHER_DEPTH_MAP_INDEX || + dither_data->c2_bitdepth >= DITHER_DEPTH_MAP_INDEX || + dither_data->c3_bitdepth >= DITHER_DEPTH_MAP_INDEX) { + SDE_ERROR("Invalid bitdepth [c0, c1, c2, c3] = [%u, %u, %u, %u]\n", + dither_data->c0_bitdepth, dither_data->c1_bitdepth, + dither_data->c2_bitdepth, dither_data->c3_bitdepth); + return; + } + + offset += 4; + data = dither_depth_map[dither_data->c0_bitdepth] & REG_MASK(2); + data |= (dither_depth_map[dither_data->c1_bitdepth] & REG_MASK(2)) << 2; + data |= (dither_depth_map[dither_data->c2_bitdepth] & REG_MASK(2)) << 4; + data |= (dither_depth_map[dither_data->c3_bitdepth] & REG_MASK(2)) << 6; + data |= (dither_data->temporal_en) ? (1 << 8) : 0; + SDE_REG_WRITE(c, dither_base + offset, data); + + for (idx = 0; idx < DITHER_MATRIX_SZ - 3; idx += 4) { + offset += 4; + data = (dither_data->matrix[idx] & REG_MASK(4)) | + ((dither_data->matrix[idx + 1] & REG_MASK(4)) << 4) | + ((dither_data->matrix[idx + 2] & REG_MASK(4)) << 8) | + ((dither_data->matrix[idx + 3] & REG_MASK(4)) << 12); + SDE_REG_WRITE(c, dither_base + offset, data); + } + + /* Enable dither */ + if (test_bit(SDE_PINGPONG_DITHER_LUMA, &pp->caps->features) + && (dither_data->flags & DITHER_LUMA_MODE)) + SDE_REG_WRITE(c, dither_base, 0x11); + else + SDE_REG_WRITE(c, dither_base, 1); + SDE_DEBUG("cwb dither enabled, dcwb_idx %u pp_id %u\n", dcwb_idx, pp_id); +} + static void _setup_wb_ops(struct sde_hw_wb_ops *ops, unsigned long features) { @@ -402,6 +530,9 @@ static void _setup_wb_ops(struct sde_hw_wb_ops *ops, ops->program_dcwb_ctrl = sde_hw_wb_program_dcwb_ctrl; ops->bind_dcwb_pp_blk = sde_hw_wb_bind_dcwb_pp_blk; } + + if (test_bit(SDE_WB_CWB_DITHER_CTRL, &features)) + ops->program_cwb_dither_ctrl = sde_hw_wb_program_cwb_dither_ctrl; } static struct sde_hw_blk_ops sde_hw_ops = { @@ -452,8 +583,10 @@ struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx, if (test_bit(SDE_WB_CWB_CTRL, &cfg->features)) _sde_hw_cwb_ctrl_init(m, addr, &c->cwb_hw); - if (test_bit(SDE_WB_DCWB_CTRL, &cfg->features)) + if (test_bit(SDE_WB_DCWB_CTRL, &cfg->features)) { _sde_hw_dcwb_ctrl_init(m, addr, &c->dcwb_hw); + _sde_hw_dcwb_pp_ctrl_init(m, addr, c); + } return c; diff --git a/msm/sde/sde_hw_wb.h b/msm/sde/sde_hw_wb.h index 61ced9c939..0af60900ef 100644 --- a/msm/sde/sde_hw_wb.h +++ b/msm/sde/sde_hw_wb.h @@ -10,6 +10,7 @@ #include "sde_hw_mdss.h" #include "sde_hw_top.h" #include "sde_hw_util.h" +#include "sde_hw_pingpong.h" struct sde_hw_wb; @@ -149,6 +150,17 @@ struct sde_hw_wb_ops { */ void (*program_dcwb_ctrl)(struct sde_hw_wb *ctx, const enum sde_dcwb cwb, const enum sde_cwb data_src, int tap_location, bool enable); + + /** + * program_cwb_dither_ctrl - program cwb dither block config + * @ctx: Pointer to wb context + * @dcwb_idx: Current Ping-Pong CWB block index to program + * @cfg: cwb dither data + * @len: the size of cwb dither data + * @enable: enable or disable the cwb dither + */ + void (*program_cwb_dither_ctrl)(struct sde_hw_wb *ctx, + const enum sde_dcwb dcwb_idx, void *cfg, size_t len, bool enable); }; /** @@ -163,6 +175,7 @@ struct sde_hw_wb_ops { * @hw_mdp: MDP top level hardware block * @cwb_hw: CWB control hwio details * @dcwb_hw: DCWB control hwio details + * @dcwb_pp_hw: DCWB PingPong control hwio details */ struct sde_hw_wb { struct sde_hw_blk base; @@ -180,6 +193,7 @@ struct sde_hw_wb { struct sde_hw_mdp *hw_mdp; struct sde_hw_blk_reg_map cwb_hw; struct sde_hw_blk_reg_map dcwb_hw; + struct sde_hw_pingpong dcwb_pp_hw[DCWB_MAX - DCWB_0]; }; /** diff --git a/msm/sde/sde_wb.c b/msm/sde/sde_wb.c index 0ef6cdd134..4b8b386ccf 100644 --- a/msm/sde/sde_wb.c +++ b/msm/sde/sde_wb.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -366,6 +366,8 @@ int sde_wb_connector_set_info_blob(struct drm_connector *connector, { struct sde_wb_device *wb_dev = display; const struct sde_format_extended *format_list; + struct msm_drm_private *priv = NULL; + struct sde_kms *sde_kms = NULL; if (!connector || !info || !display || !wb_dev->wb_cfg) { SDE_ERROR("invalid params\n"); @@ -405,9 +407,74 @@ int sde_wb_connector_set_info_blob(struct drm_connector *connector, sde_kms_info_append(info, "wb_ubwc"); sde_kms_info_stop(info); + if (wb_dev->drm_dev && wb_dev->drm_dev->dev_private) { + priv = wb_dev->drm_dev->dev_private; + if (!priv->kms) { + SDE_ERROR("invalid kms reference\n"); + return -EINVAL; + } + + sde_kms = to_sde_kms(priv->kms); + sde_kms_info_add_keyint(info, "has_cwb_dither", sde_kms->catalog->has_cwb_dither); + } else { + SDE_ERROR("invalid params %pK\n", wb_dev->drm_dev); + return -EINVAL; + } + return 0; } +static void sde_wb_connector_install_dither_property(struct sde_wb_device *wb_dev, + struct sde_connector *c_conn) +{ + char prop_name[DRM_PROP_NAME_LEN]; + struct sde_kms *sde_kms = NULL; + struct msm_drm_private *priv = NULL; + struct sde_mdss_cfg *catalog = NULL; + u32 version = 0; + + if (!wb_dev || !c_conn) { + SDE_ERROR("invalid args (s), wb_dev %pK, c_conn %pK\n", wb_dev, c_conn); + return; + } + + if (!wb_dev->drm_dev) { + SDE_ERROR("invalid drm_dev is null\n"); + return; + } + + if (!wb_dev->drm_dev->dev_private) { + SDE_ERROR("invalid dev_private is null\n"); + return; + } + + priv = wb_dev->drm_dev->dev_private; + if (!priv->kms) { + SDE_ERROR("invalid kms reference is null\n"); + return; + } + + sde_kms = to_sde_kms(priv->kms); + catalog = sde_kms->catalog; + + if (!catalog->has_cwb_dither) + return; + + version = SDE_COLOR_PROCESS_MAJOR( + catalog->pingpong[0].sblk->dither.version); + snprintf(prop_name, ARRAY_SIZE(prop_name), "%s%d", + "SDE_PP_CWB_DITHER_V", version); + switch (version) { + case 2: + msm_property_install_blob(&c_conn->property_info, prop_name, + DRM_MODE_PROP_BLOB, CONNECTOR_PROP_PP_CWB_DITHER); + break; + default: + SDE_ERROR("unsupported cwb dither version %d\n", version); + return; + } +} + int sde_wb_connector_post_init(struct drm_connector *connector, void *display) { struct sde_connector *c_conn; @@ -446,6 +513,8 @@ int sde_wb_connector_post_init(struct drm_connector *connector, void *display) ARRAY_SIZE(e_fb_translation_mode), 0, CONNECTOR_PROP_FB_TRANSLATION_MODE); + sde_wb_connector_install_dither_property(wb_dev, c_conn); + return 0; }