Browse Source

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
Gopikrishnaiah Anandan 5 years ago
parent
commit
e7c7283510
7 changed files with 282 additions and 1 deletions
  1. 23 0
      include/uapi/display/drm/sde_drm.h
  2. 1 0
      msm/msm_drv.h
  3. 139 1
      msm/sde/sde_crtc.c
  4. 5 0
      msm/sde/sde_crtc.h
  5. 93 0
      msm/sde/sde_hw_lm.c
  6. 4 0
      msm/sde/sde_hw_lm.h
  7. 17 0
      msm/sde/sde_hw_mdss.h

+ 23 - 0
include/uapi/display/drm/sde_drm.h

@@ -680,6 +680,29 @@ struct drm_msm_display_hint {
 	__u32 hint_flags;
 	__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_SDE_WB_CONFIG              0x40
 #define DRM_MSM_REGISTER_EVENT         0x41
 #define DRM_MSM_REGISTER_EVENT         0x41
 #define DRM_MSM_DEREGISTER_EVENT       0x42
 #define DRM_MSM_DEREGISTER_EVENT       0x42

+ 1 - 0
msm/msm_drv.h

@@ -180,6 +180,7 @@ enum msm_mdp_crtc_property {
 	CRTC_PROP_IDLE_PC_STATE,
 	CRTC_PROP_IDLE_PC_STATE,
 	CRTC_PROP_CACHE_STATE,
 	CRTC_PROP_CACHE_STATE,
 	CRTC_PROP_VM_REQ_STATE,
 	CRTC_PROP_VM_REQ_STATE,
+	CRTC_PROP_NOISE_LAYER_V1,
 
 
 	/* total # of properties */
 	/* total # of properties */
 	CRTC_PROP_COUNT
 	CRTC_PROP_COUNT

+ 139 - 1
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);
 	bool en, struct sde_irq_callback *idle_irq);
 static int sde_crtc_pm_event_handler(struct drm_crtc *crtc, bool en,
 static int sde_crtc_pm_event_handler(struct drm_crtc *crtc, bool en,
 		struct sde_irq_callback *noirq);
 		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[] = {
 static struct sde_crtc_custom_events custom_events[] = {
 	{DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt},
 	{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
 #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)
 static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
 {
 {
 	struct msm_drm_private *priv;
 	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_blend_setup(crtc, old_state, true);
 	_sde_crtc_dest_scaler_setup(crtc);
 	_sde_crtc_dest_scaler_setup(crtc);
+	sde_cp_crtc_apply_noise(crtc, old_state);
 
 
 	if (old_state->mode_changed) {
 	if (old_state->mode_changed) {
 		sde_core_perf_crtc_update_uidle(crtc, true);
 		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;
 	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,
 static int _sde_crtc_check_zpos(struct drm_crtc_state *state,
 		struct sde_crtc *sde_crtc,
 		struct sde_crtc *sde_crtc,
 		struct sde_crtc_state *cstate,
 		struct sde_crtc_state *cstate,
@@ -4978,7 +5004,9 @@ static int _sde_crtc_check_zpos(struct drm_crtc_state *state,
 		} else {
 		} else {
 			zpos_cnt++;
 			zpos_cnt++;
 		}
 		}
-
+		rc = _sde_crtc_noise_layer_check_zpos(cstate, z_pos);
+		if (rc)
+			break;
 		if (!kms->catalog->has_base_layer)
 		if (!kms->catalog->has_base_layer)
 			pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0;
 			pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0;
 		else
 		else
@@ -5580,6 +5608,8 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
 			info->data, SDE_KMS_INFO_DATALEN(info),
 			info->data, SDE_KMS_INFO_DATALEN(info),
 			CRTC_PROP_INFO);
 			CRTC_PROP_INFO);
 
 
+	sde_crtc_install_noise_layer_properties(sde_crtc, catalog, info);
+
 	kfree(info);
 	kfree(info);
 }
 }
 
 
@@ -5731,6 +5761,10 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc,
 			}
 			}
 		}
 		}
 		break;
 		break;
+	case CRTC_PROP_NOISE_LAYER_V1:
+		_sde_crtc_set_noise_layer(sde_crtc, cstate,
+					(void __user *)(uintptr_t)val);
+		break;
 	default:
 	default:
 		/* nothing to do */
 		/* nothing to do */
 		break;
 		break;
@@ -7117,3 +7151,107 @@ void sde_crtc_update_cont_splash_settings(struct drm_crtc *crtc)
 					rate : kms->perf.max_core_clk_rate;
 					rate : kms->perf.max_core_clk_rate;
 	sde_crtc->cur_perf.core_clk_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);
+}

+ 5 - 0
msm/sde/sde_crtc.h

@@ -418,6 +418,7 @@ struct sde_crtc {
 enum sde_crtc_dirty_flags {
 enum sde_crtc_dirty_flags {
 	SDE_CRTC_DIRTY_DEST_SCALER,
 	SDE_CRTC_DIRTY_DEST_SCALER,
 	SDE_CRTC_DIRTY_DIM_LAYERS,
 	SDE_CRTC_DIRTY_DIM_LAYERS,
+	SDE_CRTC_NOISE_LAYER,
 	SDE_CRTC_DIRTY_MAX,
 	SDE_CRTC_DIRTY_MAX,
 };
 };
 
 
@@ -450,6 +451,8 @@ enum sde_crtc_dirty_flags {
  * @ds_cfg: Destination scaler config
  * @ds_cfg: Destination scaler config
  * @scl3_lut_cfg: QSEED3 lut config
  * @scl3_lut_cfg: QSEED3 lut config
  * @new_perf: new performance state being requested
  * @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 sde_crtc_state {
 	struct drm_crtc_state base;
 	struct drm_crtc_state base;
@@ -479,6 +482,8 @@ struct sde_crtc_state {
 	struct sde_hw_scaler3_lut_cfg scl3_lut_cfg;
 	struct sde_hw_scaler3_lut_cfg scl3_lut_cfg;
 
 
 	struct sde_core_perf_params new_perf;
 	struct sde_core_perf_params new_perf;
+	bool noise_layer_en;
+	struct drm_msm_noise_layer_cfg layer_cfg;
 };
 };
 
 
 enum sde_crtc_irq_state {
 enum sde_crtc_irq_state {

+ 93 - 0
msm/sde/sde_hw_lm.c

@@ -31,6 +31,7 @@
 
 
 #define LM_MISR_CTRL			0x310
 #define LM_MISR_CTRL			0x310
 #define LM_MISR_SIGNATURE		0x314
 #define LM_MISR_SIGNATURE		0x314
+#define LM_NOISE_LAYER			0x320
 
 
 static struct sde_lm_cfg *_lm_offset(enum sde_lm mixer,
 static struct sde_lm_cfg *_lm_offset(enum sde_lm mixer,
 		struct sde_mdss_cfg *m,
 		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;
 	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,
 static void _setup_mixer_ops(struct sde_mdss_cfg *m,
 		struct sde_hw_lm_ops *ops,
 		struct sde_hw_lm_ops *ops,
 		unsigned long features)
 		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->setup_dim_layer = sde_hw_lm_setup_dim_layer;
 		ops->clear_dim_layer = sde_hw_lm_clear_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 = {
 static struct sde_hw_blk_ops sde_hw_ops = {

+ 4 - 0
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 */
 	/* collect_misr: reads and stores MISR data from HW register */
 	int (*collect_misr)(struct sde_hw_mixer *ctx, bool nonblock,
 	int (*collect_misr)(struct sde_hw_mixer *ctx, bool nonblock,
 			u32 *misr_value);
 			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 {
 struct sde_hw_mixer {

+ 17 - 0
msm/sde/sde_hw_mdss.h

@@ -746,4 +746,21 @@ struct sde_hw_pp_vsync_info {
 	u32 intf_frame_count;
 	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 */
 #endif  /* _SDE_HW_MDSS_H */