فهرست منبع

Merge "disp: msm: add cwb dither support"

qctecmdr 4 سال پیش
والد
کامیت
749c899317
9فایلهای تغییر یافته به همراه267 افزوده شده و 9 حذف شده
  1. 1 0
      msm/msm_drv.h
  2. 23 0
      msm/sde/sde_encoder_phys_wb.c
  3. 12 0
      msm/sde/sde_hw_catalog.c
  4. 6 0
      msm/sde/sde_hw_catalog.h
  5. 1 6
      msm/sde/sde_hw_pingpong.c
  6. 6 1
      msm/sde/sde_hw_pingpong.h
  7. 134 1
      msm/sde/sde_hw_wb.c
  8. 14 0
      msm/sde/sde_hw_wb.h
  9. 70 1
      msm/sde/sde_wb.c

+ 1 - 0
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,
 

+ 23 - 0
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,

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

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

+ 1 - 6
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 <linux/iopoll.h>
@@ -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

+ 6 - 1
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;
 };

+ 134 - 1
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;
 

+ 14 - 0
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];
 };
 
 /**

+ 70 - 1
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;
 }