Procházet zdrojové kódy

disp: msm: sde: program dither based on input data

This change programs dither based on user mode input data and
reprograms the dither when device comes out of power collapse.

Change-Id: I83be20c8eb2dc2221cc57cd2395f6512338ff6ef
Signed-off-by: Narendra Muppalla <[email protected]>
Narendra Muppalla před 6 roky
rodič
revize
f402d8e542

+ 3 - 1
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

+ 27 - 80
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;

+ 3 - 1
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

+ 57 - 49
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)
 {

+ 4 - 0
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;

+ 10 - 6
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;
 

+ 14 - 4
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;