From e7c72835102b88e1e9e7900758deccf76f69dc8d Mon Sep 17 00:00:00 2001 From: Gopikrishnaiah Anandan Date: Fri, 17 Jul 2020 14:31:51 -0700 Subject: [PATCH] disp: msm: sde: add support for noise layer DPU has added support for noise injection into the layer stack. Change adds support for noise layer programming and exposes the hardware block to the user space modules. Change-Id: Id176eea54fcdcd5d399457b14133a1ccde07299f --- include/uapi/display/drm/sde_drm.h | 23 +++++ msm/msm_drv.h | 1 + msm/sde/sde_crtc.c | 140 ++++++++++++++++++++++++++++- msm/sde/sde_crtc.h | 5 ++ msm/sde/sde_hw_lm.c | 93 +++++++++++++++++++ msm/sde/sde_hw_lm.h | 4 + msm/sde/sde_hw_mdss.h | 17 ++++ 7 files changed, 282 insertions(+), 1 deletion(-) diff --git a/include/uapi/display/drm/sde_drm.h b/include/uapi/display/drm/sde_drm.h index 9e385c67d6..4bd05e9962 100644 --- a/include/uapi/display/drm/sde_drm.h +++ b/include/uapi/display/drm/sde_drm.h @@ -680,6 +680,29 @@ struct drm_msm_display_hint { __u32 hint_flags; }; +#define DRM_NOISE_LAYER_CFG +#define DRM_NOISE_TEMPORAL_FLAG (1 << 0) +#define DRM_NOISE_ATTN_MAX 255 +#define DRM_NOISE_STREN_MAX 6 + +/** + * struct drm_msm_noise_layer_cfg: Payload to enable/disable noise blend + * @flags: operation control flags, for future use + * @zposn: noise zorder + * @zposattn: attenuation zorder + * @attn_factor: attenuation factor in range of 1 to 255 + * @stength: strength in range of 0 to 6 + * @alpha_noise: attenuation in range of 1 to 255 +*/ +struct drm_msm_noise_layer_cfg { + __u64 flags; + __u32 zposn; + __u32 zposattn; + __u32 attn_factor; + __u32 strength; + __u32 alpha_noise; +}; + #define DRM_SDE_WB_CONFIG 0x40 #define DRM_MSM_REGISTER_EVENT 0x41 #define DRM_MSM_DEREGISTER_EVENT 0x42 diff --git a/msm/msm_drv.h b/msm/msm_drv.h index e61e5d9da1..c4a76fbec4 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -180,6 +180,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_IDLE_PC_STATE, CRTC_PROP_CACHE_STATE, CRTC_PROP_VM_REQ_STATE, + CRTC_PROP_NOISE_LAYER_V1, /* total # of properties */ CRTC_PROP_COUNT diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 6fcd7973b1..64ef082e7f 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -63,6 +63,9 @@ static int sde_crtc_mmrm_interrupt_handler(struct drm_crtc *crtc_drm, bool en, struct sde_irq_callback *idle_irq); static int sde_crtc_pm_event_handler(struct drm_crtc *crtc, bool en, struct sde_irq_callback *noirq); +static int _sde_crtc_set_noise_layer(struct sde_crtc *sde_crtc, + struct sde_crtc_state *cstate, + void __user *usr_ptr); static struct sde_crtc_custom_events custom_events[] = { {DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt}, @@ -103,6 +106,11 @@ static struct sde_crtc_custom_events custom_events[] = { #define SKIP_STAGING_PIPE_ZPOS 255 +static void sde_crtc_install_noise_layer_properties(struct sde_crtc *sde_crtc, + struct sde_mdss_cfg *catalog, struct sde_kms_info *info); +static void sde_cp_crtc_apply_noise(struct drm_crtc *crtc, + struct drm_crtc_state *state); + static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) { struct msm_drm_private *priv; @@ -3386,6 +3394,7 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, _sde_crtc_blend_setup(crtc, old_state, true); _sde_crtc_dest_scaler_setup(crtc); + sde_cp_crtc_apply_noise(crtc, old_state); if (old_state->mode_changed) { sde_core_perf_crtc_update_uidle(crtc, true); @@ -4914,6 +4923,23 @@ static int _sde_crtc_check_get_pstates(struct drm_crtc *crtc, return rc; } +static int _sde_crtc_noise_layer_check_zpos(struct sde_crtc_state *cstate, + u32 zpos) { + if (!test_bit(SDE_CRTC_NOISE_LAYER, cstate->dirty) || + !cstate->noise_layer_en) { + SDE_DEBUG("noise layer not enabled %d\n", cstate->noise_layer_en); + return 0; + } + + if (cstate->layer_cfg.zposn == zpos || + cstate->layer_cfg.zposattn == zpos) { + SDE_ERROR("invalid zpos %d zposn %d zposattn %d\n", zpos, + cstate->layer_cfg.zposn, cstate->layer_cfg.zposattn); + return -EINVAL; + } + return 0; +} + static int _sde_crtc_check_zpos(struct drm_crtc_state *state, struct sde_crtc *sde_crtc, struct sde_crtc_state *cstate, @@ -4978,7 +5004,9 @@ static int _sde_crtc_check_zpos(struct drm_crtc_state *state, } else { zpos_cnt++; } - + rc = _sde_crtc_noise_layer_check_zpos(cstate, z_pos); + if (rc) + break; if (!kms->catalog->has_base_layer) pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0; else @@ -5580,6 +5608,8 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, info->data, SDE_KMS_INFO_DATALEN(info), CRTC_PROP_INFO); + sde_crtc_install_noise_layer_properties(sde_crtc, catalog, info); + kfree(info); } @@ -5731,6 +5761,10 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc, } } break; + case CRTC_PROP_NOISE_LAYER_V1: + _sde_crtc_set_noise_layer(sde_crtc, cstate, + (void __user *)(uintptr_t)val); + break; default: /* nothing to do */ break; @@ -7117,3 +7151,107 @@ void sde_crtc_update_cont_splash_settings(struct drm_crtc *crtc) rate : kms->perf.max_core_clk_rate; sde_crtc->cur_perf.core_clk_rate = kms->perf.max_core_clk_rate; } + +static void sde_crtc_install_noise_layer_properties(struct sde_crtc *sde_crtc, + struct sde_mdss_cfg *catalog, struct sde_kms_info *info) +{ + struct sde_lm_cfg *lm; + char feature_name[256]; + u32 version; + + if (!catalog->mixer_count) + return; + + lm = &catalog->mixer[0]; + if (!(lm->features & BIT(SDE_MIXER_NOISE_LAYER))) + return; + + version = lm->sblk->nlayer.version >> 16; + snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d", "noise_layer_v", version); + + switch (version) { + case 1: + sde_kms_info_add_keyint(info, "has_noise_layer", 1); + msm_property_install_volatile_range(&sde_crtc->property_info, + feature_name, 0x0, 0, ~0, 0, CRTC_PROP_NOISE_LAYER_V1); + break; + default: + SDE_ERROR("unsupported noise layer version %d\n", version); + break; + } +} + +static int _sde_crtc_set_noise_layer(struct sde_crtc *sde_crtc, + struct sde_crtc_state *cstate, + void __user *usr_ptr) +{ + int ret; + + if (!sde_crtc || !cstate) { + SDE_ERROR("invalid sde_crtc/state\n"); + return -EINVAL; + } + + SDE_DEBUG("crtc %s\n", sde_crtc->name); + + if (!usr_ptr) { + SDE_DEBUG("noise layer removed\n"); + cstate->noise_layer_en = false; + set_bit(SDE_CRTC_NOISE_LAYER, cstate->dirty); + return 0; + } + ret = copy_from_user(&cstate->layer_cfg, usr_ptr, + sizeof(cstate->layer_cfg)); + if (ret) { + SDE_ERROR("failed to copy noise layer %d\n", ret); + return -EFAULT; + } + if (cstate->layer_cfg.zposn != cstate->layer_cfg.zposattn - 1 || + cstate->layer_cfg.zposattn >= SDE_STAGE_MAX || + !cstate->layer_cfg.attn_factor || + cstate->layer_cfg.attn_factor > DRM_NOISE_ATTN_MAX || + cstate->layer_cfg.strength > DRM_NOISE_STREN_MAX || + !cstate->layer_cfg.alpha_noise || + cstate->layer_cfg.alpha_noise > DRM_NOISE_ATTN_MAX) { + SDE_ERROR("invalid param zposn %d zposattn %d attn_factor %d \ + strength %d alpha noise %d\n", cstate->layer_cfg.zposn, + cstate->layer_cfg.zposattn, cstate->layer_cfg.attn_factor, + cstate->layer_cfg.strength, cstate->layer_cfg.alpha_noise); + return -EINVAL; + } + cstate->noise_layer_en = true; + set_bit(SDE_CRTC_NOISE_LAYER, cstate->dirty); + return 0; +} + +static void sde_cp_crtc_apply_noise(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct sde_crtc *scrtc = to_sde_crtc(crtc); + struct sde_crtc_state *cstate = to_sde_crtc_state(crtc->state); + struct sde_hw_mixer *lm; + int i; + struct sde_hw_noise_layer_cfg cfg; + + if (!test_bit(SDE_CRTC_NOISE_LAYER, cstate->dirty)) + return; + + cfg.flags = cstate->layer_cfg.flags; + cfg.alpha_noise = cstate->layer_cfg.alpha_noise; + cfg.attn_factor = cstate->layer_cfg.attn_factor; + cfg.strength = cstate->layer_cfg.strength; + cfg.zposn = cstate->layer_cfg.zposn; + cfg.zposattn = cstate->layer_cfg.zposattn; + + for (i = 0; i < scrtc->num_mixers; i++) { + lm = scrtc->mixers[i].hw_lm; + if (!lm->ops.setup_noise_layer) + break; + if (!cstate->noise_layer_en) + lm->ops.setup_noise_layer(lm, NULL); + else + lm->ops.setup_noise_layer(lm, &cfg); + } + if (!cstate->noise_layer_en) + clear_bit(SDE_CRTC_NOISE_LAYER, cstate->dirty); +} diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index db6c578c30..724b230ea0 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -418,6 +418,7 @@ struct sde_crtc { enum sde_crtc_dirty_flags { SDE_CRTC_DIRTY_DEST_SCALER, SDE_CRTC_DIRTY_DIM_LAYERS, + SDE_CRTC_NOISE_LAYER, SDE_CRTC_DIRTY_MAX, }; @@ -450,6 +451,8 @@ enum sde_crtc_dirty_flags { * @ds_cfg: Destination scaler config * @scl3_lut_cfg: QSEED3 lut config * @new_perf: new performance state being requested + * @noise_layer_en: flag to indicate if noise layer cfg is valid + * @drm_msm_noise_layer_cfg: noise layer configuration */ struct sde_crtc_state { struct drm_crtc_state base; @@ -479,6 +482,8 @@ struct sde_crtc_state { struct sde_hw_scaler3_lut_cfg scl3_lut_cfg; struct sde_core_perf_params new_perf; + bool noise_layer_en; + struct drm_msm_noise_layer_cfg layer_cfg; }; enum sde_crtc_irq_state { diff --git a/msm/sde/sde_hw_lm.c b/msm/sde/sde_hw_lm.c index 946fd17ef2..3a18646a91 100644 --- a/msm/sde/sde_hw_lm.c +++ b/msm/sde/sde_hw_lm.c @@ -31,6 +31,7 @@ #define LM_MISR_CTRL 0x310 #define LM_MISR_SIGNATURE 0x314 +#define LM_NOISE_LAYER 0x320 static struct sde_lm_cfg *_lm_offset(enum sde_lm mixer, struct sde_mdss_cfg *m, @@ -279,6 +280,95 @@ static int sde_hw_lm_collect_misr(struct sde_hw_mixer *ctx, bool nonblock, return 0; } +static void sde_hw_clear_noise_layer(struct sde_hw_mixer *ctx) +{ + struct sde_hw_blk_reg_map *c = &ctx->hw; + const struct sde_lm_sub_blks *sblk = ctx->cap->sblk; + int stage_off, i; + u32 reset = BIT(18) | BIT(31), val; + + reset = ~reset; + for (i = SDE_STAGE_0; i <= sblk->maxblendstages; i++) { + stage_off = _stage_offset(ctx, i); + if (WARN_ON(stage_off < 0)) + return; + + /** + * read the blendn_op register and clear only noise layer + */ + val = SDE_REG_READ(c, LM_BLEND0_OP + stage_off); + val &= reset; + SDE_REG_WRITE(c, LM_BLEND0_OP + stage_off, val); + } + SDE_REG_WRITE(c, LM_NOISE_LAYER, 0); +} + +static int sde_hw_lm_setup_noise_layer(struct sde_hw_mixer *ctx, + struct sde_hw_noise_layer_cfg *cfg) +{ + struct sde_hw_blk_reg_map *c = &ctx->hw; + int stage_off; + u32 val = 0, alpha = 0; + const struct sde_lm_sub_blks *sblk = ctx->cap->sblk; + struct sde_hw_mixer_cfg *mixer = &ctx->cfg; + + sde_hw_clear_noise_layer(ctx); + if (!cfg) + return 0; + + if (cfg->zposn == SDE_STAGE_BASE || cfg->zposn + 1 != cfg->zposattn || + cfg->zposattn >= sblk->maxblendstages) { + SDE_ERROR("invalid zposn %d zposattn %d max stage %d\n", + cfg->zposn, cfg->zposattn, sblk->maxblendstages); + return -EINVAL; + } + stage_off = _stage_offset(ctx, cfg->zposn); + if (stage_off < 0) { + SDE_ERROR("invalid stage_off:%d for noise layer stage_off %d\n", + cfg->zposn, stage_off); + return -EINVAL; + } + val = BIT(18) | BIT(31); + val |= (1 << 8); + alpha = 255 | (cfg->alpha_noise << 16); + SDE_REG_WRITE(c, LM_BLEND0_OP + stage_off, val); + SDE_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, alpha); + val = ctx->cfg.out_width | (ctx->cfg.out_height << 16); + SDE_REG_WRITE(c, LM_FG_COLOR_FILL_SIZE + stage_off, val); + val = SDE_REG_READ(c, LM_OP_MODE); + val = (1 << cfg->zposn) | val; + SDE_REG_WRITE(c, LM_OP_MODE, val); + + stage_off = _stage_offset(ctx, cfg->zposattn); + if (stage_off < 0) { + SDE_ERROR("invalid stage_off:%d for noise layer\n", + cfg->zposattn); + sde_hw_clear_noise_layer(ctx); + return -EINVAL; + } + val = 1 | BIT(31) | BIT(16); + val |= BIT(2); + val |= (1 << 8); + alpha = cfg->attn_factor; + SDE_REG_WRITE(c, LM_BLEND0_OP + stage_off, val); + SDE_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, alpha); + val = SDE_REG_READ(c, LM_OP_MODE); + val = (1 << cfg->zposattn) | val; + SDE_REG_WRITE(c, LM_OP_MODE, val); + val = ctx->cfg.out_width | (ctx->cfg.out_height << 16); + SDE_REG_WRITE(c, LM_FG_COLOR_FILL_SIZE + stage_off, val); + + val = 1; + if (mixer->right_mixer) + val |= (((mixer->out_width % 4) & 0x3) << 4); + + if (cfg->flags & DRM_NOISE_TEMPORAL_FLAG) + val |= BIT(1); + val |= ((cfg->strength & 0x7) << 8); + SDE_REG_WRITE(c, LM_NOISE_LAYER, val); + return 0; +} + static void _setup_mixer_ops(struct sde_mdss_cfg *m, struct sde_hw_lm_ops *ops, unsigned long features) @@ -299,6 +389,9 @@ static void _setup_mixer_ops(struct sde_mdss_cfg *m, ops->setup_dim_layer = sde_hw_lm_setup_dim_layer; ops->clear_dim_layer = sde_hw_lm_clear_dim_layer; } + + if (test_bit(SDE_MIXER_NOISE_LAYER, &features)) + ops->setup_noise_layer = sde_hw_lm_setup_noise_layer; }; static struct sde_hw_blk_ops sde_hw_ops = { diff --git a/msm/sde/sde_hw_lm.h b/msm/sde/sde_hw_lm.h index 07574d3f65..931e318517 100644 --- a/msm/sde/sde_hw_lm.h +++ b/msm/sde/sde_hw_lm.h @@ -81,6 +81,10 @@ struct sde_hw_lm_ops { /* collect_misr: reads and stores MISR data from HW register */ int (*collect_misr)(struct sde_hw_mixer *ctx, bool nonblock, u32 *misr_value); + + /* setup_noise_layer: enables/disables noise layer */ + int (*setup_noise_layer)(struct sde_hw_mixer *ctx, + struct sde_hw_noise_layer_cfg *cfg); }; struct sde_hw_mixer { diff --git a/msm/sde/sde_hw_mdss.h b/msm/sde/sde_hw_mdss.h index ae824c1aa7..ceedfeb210 100644 --- a/msm/sde/sde_hw_mdss.h +++ b/msm/sde/sde_hw_mdss.h @@ -746,4 +746,21 @@ struct sde_hw_pp_vsync_info { u32 intf_frame_count; }; +/** + * struct sde_hw_noise_layer_cfg: Payload to enable/disable noise blend + * @flags: operation control flags, for future use + * @zposn: zorder required for noise + * @zposattn: zorder required for noise + * @attn_factor: factor in range of 1 to 255 + * @stength: strength in range of 0 to 6 + * @alpha_noise: factor in range of 1 to 255 +*/ +struct sde_hw_noise_layer_cfg { + u64 flags; + u32 zposn; + u32 zposattn; + u32 attn_factor; + u32 strength; + u32 alpha_noise; +}; #endif /* _SDE_HW_MDSS_H */