Explorar el Código

Merge "disp: msm: dp: add debug node to capture source and sink crc"

qctecmdr hace 2 años
padre
commit
5ef4dc0b4d
Se han modificado 9 ficheros con 332 adiciones y 1 borrados
  1. 107 0
      msm/dp/dp_catalog.c
  2. 8 0
      msm/dp/dp_catalog.h
  3. 27 0
      msm/dp/dp_ctrl.c
  4. 3 0
      msm/dp/dp_ctrl.h
  5. 97 1
      msm/dp/dp_debug.c
  6. 2 0
      msm/dp/dp_link.h
  7. 71 0
      msm/dp/dp_panel.c
  8. 3 0
      msm/dp/dp_panel.h
  9. 14 0
      msm/dp/dp_reg.h

+ 107 - 0
msm/dp/dp_catalog.c

@@ -1118,6 +1118,7 @@ static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel,
 	struct dp_catalog_private *catalog;
 	struct dp_io_data *io_data;
 	u32 strm_reg_off = 0, mainlink_ctrl;
+	u32 reg;
 
 	if (!panel) {
 		DP_ERR("invalid input\n");
@@ -1150,6 +1151,10 @@ static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel,
 		dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x01);
 	else
 		dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x00);
+
+	reg = dp_read(MMSS_DP_TIMING_ENGINE_EN);
+	reg |= BIT(8);
+	dp_write(MMSS_DP_TIMING_ENGINE_EN, reg);
 }
 
 static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel,
@@ -1419,6 +1424,77 @@ static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip)
 	wmb();
 }
 
+static int dp_catalog_ctrl_setup_misr(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 val;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_phy;
+	dp_write(DP_PHY_MISR_CTRL, 0x3);
+	/* make sure misr hw is reset */
+	wmb();
+	dp_write(DP_PHY_MISR_CTRL, 0x1);
+	/* make sure misr is brought out of reset */
+	wmb();
+
+	io_data = catalog->io.dp_link;
+	val = 1;	// frame count
+	val |= BIT(10); // clear status
+	val |= BIT(8);  // enable
+	dp_write(DP_MISR40_CTRL, val);
+	/* make sure misr control is applied */
+	wmb();
+
+	return 0;
+}
+
+static int dp_catalog_ctrl_read_misr(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 val;
+	int i, j;
+	u32 addr;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_phy;
+	val = dp_read(DP_PHY_MISR_STATUS);
+	if (!val) {
+		DP_WARN("phy misr not ready!");
+		return -EAGAIN;
+	}
+
+	addr = DP_PHY_MISR_TX0;
+	for (i = 0; i < 8; i++) {
+		data->phy_misr[i] = 0;
+		for (j = 0; j < 4; j++) {
+			val = dp_read(addr) & 0xff;
+			data->phy_misr[i] |= val << (j * 8);
+			addr += 4;
+		}
+	}
+
+	io_data = catalog->io.dp_link;
+	for (i = 0; i < 8; i++)
+		data->ctrl_misr[i] = dp_read(DP_MISR40_TX0 + (i * 4));
+
+	return 0;
+}
+
 static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel, u32 pattern)
 {
 	struct dp_catalog_private *catalog;
@@ -1627,6 +1703,34 @@ static bool dp_catalog_panel_dhdr_busy(struct dp_catalog_panel *panel)
 	return dp_flush & BIT(DP_DHDR_FLUSH) ? true : false;
 }
 
+static int dp_catalog_panel_get_src_crc(struct dp_catalog_panel *panel, u16 *crc)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 offset;
+	u32 reg;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_0)
+		offset = MMSS_DP_PSR_CRC_RG;
+	else
+		offset = MMSS_DP1_CRC_RG;
+
+	reg = dp_read(offset); //GR
+	crc[0] = reg & 0xffff;
+	crc[1] = reg >> 16;
+	crc[2] = dp_read(offset + 4); //B
+
+	return 0;
+}
+
 static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl)
 {
 	u32 sw_reset;
@@ -2896,6 +3000,8 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
 		.fec_config = dp_catalog_ctrl_fec_config,
 		.mainlink_levels = dp_catalog_ctrl_mainlink_levels,
 		.late_phy_init = dp_catalog_ctrl_late_phy_init,
+		.setup_misr = dp_catalog_ctrl_setup_misr,
+		.read_misr = dp_catalog_ctrl_read_misr,
 	};
 	struct dp_catalog_hpd hpd = {
 		.config_hpd	= dp_catalog_hpd_config_hpd,
@@ -2925,6 +3031,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
 		.pps_flush = dp_catalog_panel_pps_flush,
 		.dhdr_flush = dp_catalog_panel_dhdr_flush,
 		.dhdr_busy = dp_catalog_panel_dhdr_busy,
+		.get_src_crc = dp_catalog_panel_get_src_crc,
 	};
 
 	if (!dev || !parser) {

+ 8 - 0
msm/dp/dp_catalog.h

@@ -46,6 +46,11 @@ struct dp_catalog_vsc_sdp_colorimetry {
 	u8 data[32];
 };
 
+struct dp_misr40_data {
+	u32 ctrl_misr[8];
+	u32 phy_misr[8];
+};
+
 struct dp_catalog_aux {
 	u32 data;
 	u32 isr;
@@ -103,6 +108,8 @@ struct dp_catalog_ctrl {
 
 	int (*late_phy_init)(struct dp_catalog_ctrl *ctrl,
 					u8 lane_cnt, bool flipped);
+	int (*setup_misr)(struct dp_catalog_ctrl *ctrl);
+	int (*read_misr)(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data);
 };
 
 struct dp_catalog_hpd {
@@ -221,6 +228,7 @@ struct dp_catalog_panel {
 	void (*pps_flush)(struct dp_catalog_panel *panel);
 	void (*dhdr_flush)(struct dp_catalog_panel *panel);
 	bool (*dhdr_busy)(struct dp_catalog_panel *panel);
+	int (*get_src_crc)(struct dp_catalog_panel *panel, u16 *crc);
 };
 
 struct dp_catalog;

+ 27 - 0
msm/dp/dp_ctrl.c

@@ -1308,6 +1308,7 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
 	/* wait for link training completion before fec config as per spec */
 	dp_ctrl_fec_setup(ctrl);
 	dp_ctrl_dsc_setup(ctrl, panel);
+	panel->sink_crc_enable(panel, true);
 
 	return rc;
 }
@@ -1515,6 +1516,30 @@ void dp_ctrl_set_sim_mode(struct dp_ctrl *dp_ctrl, bool en)
 	DP_INFO("sim_mode=%d\n", ctrl->sim_mode);
 }
 
+int dp_ctrl_setup_misr(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return -EINVAL;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	return ctrl->catalog->setup_misr(ctrl->catalog);
+}
+
+int dp_ctrl_read_misr(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return -EINVAL;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	return ctrl->catalog->read_misr(ctrl->catalog, data);
+}
+
 struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
 {
 	int rc = 0;
@@ -1565,6 +1590,8 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
 	dp_ctrl->stream_pre_off = dp_ctrl_stream_pre_off;
 	dp_ctrl->set_mst_channel_info = dp_ctrl_set_mst_channel_info;
 	dp_ctrl->set_sim_mode = dp_ctrl_set_sim_mode;
+	dp_ctrl->setup_misr = dp_ctrl_setup_misr;
+	dp_ctrl->read_misr = dp_ctrl_read_misr;
 
 	return dp_ctrl;
 error:

+ 3 - 0
msm/dp/dp_ctrl.h

@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
  */
 
@@ -32,6 +33,8 @@ struct dp_ctrl {
 			enum dp_stream_id strm,
 			u32 ch_start_slot, u32 ch_tot_slots);
 	void (*set_sim_mode)(struct dp_ctrl *dp_ctrl, bool en);
+	int (*setup_misr)(struct dp_ctrl *dp_ctrl);
+	int (*read_misr)(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data);
 };
 
 struct dp_ctrl_in {

+ 97 - 1
msm/dp/dp_debug.c

@@ -333,7 +333,7 @@ static ssize_t dp_debug_read_dpcd(struct file *file,
 		}
 	}
 
-	len += scnprintf(buf + len , buf_size - len, "%04x: ", debug->dpcd_offset);
+	len += scnprintf(buf + len, buf_size - len, "%04x: ", debug->dpcd_offset);
 
 	while (offset < debug->dpcd_size)
 		len += scnprintf(buf + len, buf_size - len, "%02x ", dpcd[offset++]);
@@ -351,6 +351,90 @@ bail:
 	return len;
 }
 
+static ssize_t dp_debug_read_crc(struct file *file, char __user *user_buff, size_t count,
+		loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	int const buf_size = SZ_4K;
+	u32 len = 0;
+	u16 src_crc[3] = {0};
+	u16 sink_crc[3] = {0};
+	struct dp_misr40_data misr40 = {0};
+	u32 retries = 2;
+	struct drm_connector *drm_conn;
+	struct sde_connector *sde_conn;
+	struct dp_panel *panel;
+	int i;
+	int rc;
+
+	if (!debug || !debug->aux)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&debug->lock);
+
+	if (!debug->panel || !debug->ctrl)
+		goto bail;
+
+	if (debug->panel->mst_state) {
+		drm_conn = drm_connector_lookup((*debug->connector)->dev, NULL, debug->mst_con_id);
+		if (!drm_conn) {
+			DP_ERR("connector %u not in mst list\n", debug->mst_con_id);
+			goto bail;
+		}
+
+		sde_conn = to_sde_connector(drm_conn);
+		panel = sde_conn->drv_panel;
+	} else {
+		panel = debug->panel;
+	}
+
+	panel->get_src_crc(panel, src_crc);
+	panel->get_sink_crc(panel, sink_crc);
+
+	len += scnprintf(buf + len, buf_size - len, "FRAME_CRC:\nSource vs Sink\n");
+
+	len += scnprintf(buf + len, buf_size - len, "CRC_R: %04X %04X\n", src_crc[0], sink_crc[0]);
+	len += scnprintf(buf + len, buf_size - len, "CRC_G: %04X %04X\n", src_crc[1], sink_crc[1]);
+	len += scnprintf(buf + len, buf_size - len, "CRC_B: %04X %04X\n", src_crc[2], sink_crc[2]);
+
+	debug->ctrl->setup_misr(debug->ctrl);
+
+	while (retries--) {
+		mutex_unlock(&debug->lock);
+		msleep(30);
+		mutex_lock(&debug->lock);
+
+		rc = debug->ctrl->read_misr(debug->ctrl, &misr40);
+		if (rc != -EAGAIN)
+			break;
+	}
+
+	len += scnprintf(buf + len, buf_size - len, "\nMISR40:\nCTLR vs PHY\n");
+	for (i = 0; i < 4; i++) {
+		len += scnprintf(buf + len, buf_size - len, "Lane%d %08X%08X %08X%08X\n", i,
+				misr40.ctrl_misr[2 * i], misr40.ctrl_misr[(2 * i) + 1],
+				misr40.phy_misr[2 * i], misr40.phy_misr[(2 * i) + 1]);
+	}
+
+	len = min_t(size_t, count, len);
+	if (!copy_to_user(user_buff, buf, len))
+		*ppos += len;
+
+bail:
+	mutex_unlock(&debug->lock);
+	kfree(buf);
+
+	return len;
+}
+
 static ssize_t dp_debug_write_hpd(struct file *file,
 		const char __user *user_buff, size_t count, loff_t *ppos)
 {
@@ -1871,6 +1955,11 @@ static const struct file_operations dpcd_fops = {
 	.read = dp_debug_read_dpcd,
 };
 
+static const struct file_operations crc_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_crc,
+};
+
 static const struct file_operations connected_fops = {
 	.open = simple_open,
 	.read = dp_debug_read_connected,
@@ -2104,6 +2193,13 @@ static int dp_debug_init_sink_caps(struct dp_debug_private *debug,
 		return rc;
 	}
 
+	file = debugfs_create_file("crc", 0644, dir, debug, &crc_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs crc failed, rc=%d\n", DEBUG_NAME, rc);
+		return rc;
+	}
+
 	return rc;
 }
 

+ 2 - 0
msm/dp/dp_link.h

@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
  *
  * Copyright (c) 2008 Keith Packard
@@ -38,6 +39,7 @@
 struct drm_dp_aux;
 
 #define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
+#define DP_LINK_CAP_CRC (1 << 1)
 
 struct drm_dp_link {
 	unsigned char revision;

+ 71 - 0
msm/dp/dp_panel.c

@@ -1682,6 +1682,10 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
 	if (drm_dp_enhanced_frame_cap(dpcd))
 		link_info->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
 
+	rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_TEST_SINK_MISC, &temp, 1);
+	if ((rlen == 1) && (temp & DP_TEST_CRC_SUPPORTED))
+		link_info->capabilities |= DP_LINK_CAP_CRC;
+
 	dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] &
 						DP_DOWN_STREAM_PORT_COUNT;
 
@@ -3050,6 +3054,70 @@ static void dp_panel_update_pps(struct dp_panel *dp_panel, char *pps_cmd)
 	catalog->pps_flush(catalog);
 }
 
+int dp_panel_get_src_crc(struct dp_panel *dp_panel, u16 *crc)
+{
+	struct dp_catalog_panel *catalog;
+	struct dp_panel_private *panel;
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	catalog = panel->catalog;
+	return catalog->get_src_crc(catalog, crc);
+}
+
+int dp_panel_get_sink_crc(struct dp_panel *dp_panel, u16 *crc)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+	struct drm_dp_aux *drm_aux;
+	u8 crc_bytes[6];
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	drm_aux = panel->aux->drm_aux;
+
+	/*
+	 * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes
+	 * per component (RGB or CrYCb).
+	 */
+	rc = drm_dp_dpcd_read(drm_aux, DP_TEST_CRC_R_CR, crc_bytes, 6);
+	if (rc < 0)
+		return rc;
+
+	rc = 0;
+	crc[0] = crc_bytes[0] | crc_bytes[1] << 8;
+	crc[1] = crc_bytes[2] | crc_bytes[3] << 8;
+	crc[2] = crc_bytes[4] | crc_bytes[5] << 8;
+
+	return rc;
+}
+
+int dp_panel_sink_crc_enable(struct dp_panel *dp_panel, bool enable)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+	struct drm_dp_aux *drm_aux;
+	ssize_t ret;
+	u8 buf;
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	drm_aux = panel->aux->drm_aux;
+
+	if (dp_panel->link_info.capabilities & DP_LINK_CAP_CRC) {
+		ret = drm_dp_dpcd_readb(drm_aux, DP_TEST_SINK, &buf);
+		if (ret < 0)
+			return ret;
+
+		ret = drm_dp_dpcd_writeb(drm_aux, DP_TEST_SINK, buf | DP_TEST_SINK_START);
+		if (ret < 0)
+			return ret;
+
+		drm_dp_dpcd_readb(drm_aux, DP_TEST_SINK, &buf);
+		DP_DEBUG("Enabled CRC: %x\n", buf);
+	}
+
+	return rc;
+}
+
 struct dp_panel *dp_panel_get(struct dp_panel_in *in)
 {
 	int rc = 0;
@@ -3122,6 +3190,9 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
 	dp_panel->read_mst_cap = dp_panel_read_mst_cap;
 	dp_panel->convert_to_dp_mode = dp_panel_convert_to_dp_mode;
 	dp_panel->update_pps = dp_panel_update_pps;
+	dp_panel->get_src_crc = dp_panel_get_src_crc;
+	dp_panel->get_sink_crc = dp_panel_get_sink_crc;
+	dp_panel->sink_crc_enable = dp_panel_sink_crc_enable;
 
 	sde_conn = to_sde_connector(dp_panel->connector);
 	sde_conn->drv_panel = dp_panel;

+ 3 - 0
msm/dp/dp_panel.h

@@ -193,6 +193,9 @@ struct dp_panel {
 		const struct drm_display_mode *drm_mode,
 		struct dp_display_mode *dp_mode);
 	void (*update_pps)(struct dp_panel *dp_panel, char *pps_cmd);
+	int (*sink_crc_enable)(struct dp_panel *dp_panel, bool enable);
+	int (*get_src_crc)(struct dp_panel *dp_panel, u16 *crc);
+	int (*get_sink_crc)(struct dp_panel *dp_panel, u16 *crc);
 };
 
 struct dp_tu_calc_input {

+ 14 - 0
msm/dp/dp_reg.h

@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
@@ -86,9 +87,16 @@
 #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
 #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
 
+#define DP_MISR40_CTRL				(0x000000D0)
+#define DP_MISR40_TX0				(0x000000D4)
+#define DP_MISR40_TX1				(0x000000DC)
+#define DP_MISR40_TX2				(0x000000E4)
+#define DP_MISR40_TX3				(0x000000EC)
 #define MMSS_DP_PSR_CRC_RG			(0x00000154)
 #define MMSS_DP_PSR_CRC_B			(0x00000158)
 
+#define MMSS_DP1_CRC_RG				(0x00000164)
+#define MMSS_DP1_CRC_B				(0x00000168)
 #define DP_COMPRESSION_MODE_CTRL		(0x00000180)
 #define DP_PPS_HB_0_3				(0x00000184)
 #define DP_PPS_PB_0_3				(0x00000188)
@@ -384,6 +392,12 @@
 #define DP_PHY_AUX_INTERRUPT_STATUS_V420	(0x00D8)
 #define DP_PHY_AUX_INTERRUPT_STATUS_V600        (0x00E0)
 #define DP_PHY_SPARE0_V420			(0x00C8)
+#define DP_PHY_MISR_CTRL			(0x00C0)
+#define DP_PHY_MISR_STATUS			(0x010C)
+#define DP_PHY_MISR_TX0				(0x0110)
+#define DP_PHY_MISR_TX1				(0x0130)
+#define DP_PHY_MISR_TX2				(0x0150)
+#define DP_PHY_MISR_TX3				(0x0170)
 #define TXn_TX_DRV_LVL_V420			(0x0014)
 #define TXn_TRANSCEIVER_BIAS_EN_V420		(0x0054)
 #define TXn_HIGHZ_DRVR_EN_V420			(0x0058)