diff mbox series

[RFC,06/15] drm/drm_scdc_helper: Add SCDC helper funcs for HDMI2.1

Message ID 20221107072045.628895-7-ankit.k.nautiyal@intel.com (mailing list archive)
State New, archived
Headers show
Series Add support for HDMI2.1 FRL | expand

Commit Message

Nautiyal, Ankit K Nov. 7, 2022, 7:20 a.m. UTC
HDMI2.1 specifies new SCDC registers to configure FRL Training
between source and sink and get the FRL Training updated from
and HDMI2.1 sink.

This patch adds new SCDC registers and helper functions to
read and configure these registers.

Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
---
 drivers/gpu/drm/display/drm_scdc_helper.c | 196 ++++++++++++++++++++++
 include/drm/display/drm_scdc.h            |  23 +++
 include/drm/display/drm_scdc_helper.h     |  21 +++
 3 files changed, 240 insertions(+)
diff mbox series

Patch

diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c
index c3ad4ab2b456..dcf05167473d 100644
--- a/drivers/gpu/drm/display/drm_scdc_helper.c
+++ b/drivers/gpu/drm/display/drm_scdc_helper.c
@@ -261,3 +261,199 @@  bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
 	return true;
 }
 EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);
+
+/**
+ * drm_scdc_read_update_flags - read the SCDC update flags
+ * @adapter: I2C adapter for DDC channel
+ *
+ * Returns:
+ * 8bit SCDC update
+ */
+u8 drm_scdc_read_update_flags(struct i2c_adapter *adapter)
+{
+	u8 update = 0;
+	int ret;
+
+	ret = drm_scdc_readb(adapter, SCDC_UPDATE_0, &update);
+	if (ret < 0)
+		DRM_DEBUG_KMS("Failed to read scdc update: %d\n", ret);
+
+	return update;
+}
+EXPORT_SYMBOL(drm_scdc_read_update_flags);
+
+/**
+ * drm_scdc_clear_update_flags - Clears the given update flag bits
+ * @adapter: I2C adapter for DDC channel
+ * @status: update flag bits to be cleared
+ *
+ * Returns:
+ * 0 on success, negative error code otherwise.
+ */
+int drm_scdc_clear_update_flags(struct i2c_adapter *adapter, u8 update_flags)
+{
+	u8 buf;
+	int ret;
+
+	/* Not all flags can be cleared by source */
+	if (update_flags & ~(SCDC_STATUS_UPDATE | SCDC_CED_UPDATE |
+			     SCDC_SOURCE_TEST_UPDATE | SCDC_FLT_UPDATE |
+			     SCDC_RSED_UPDATE)) {
+		DRM_DEBUG_KMS("SCDC Update flag/s %u cannot be cleared\n",
+			      update_flags);
+
+		return false;
+	}
+
+	ret = drm_scdc_readb(adapter, SCDC_UPDATE_0, &buf);
+	if (ret < 0) {
+		DRM_DEBUG_KMS("Failed to read SCDC_UPDATE_0\n");
+
+		return ret;
+	}
+
+	buf = buf | update_flags;
+
+	ret = drm_scdc_writeb(adapter, SCDC_UPDATE_0, buf);
+	if (ret < 0) {
+		DRM_DEBUG_KMS("Failed to clear SCDC Update flag/s\n");
+
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_scdc_clear_update_flags);
+
+/**
+ * drm_scdc_read_status_flags - Read the status flags from offset 0x40
+ * @adapter: I2C adapter for DDC channel
+ *
+ * Returns:
+ * 8 bit value read from the 0ffset 0x40
+ */
+u8 drm_scdc_read_status_flags(struct i2c_adapter *adapter)
+{
+	u8 update = 0;
+	int ret;
+
+	ret = drm_scdc_readb(adapter, SCDC_STATUS_FLAGS_0, &update);
+	if (ret < 0)
+		DRM_DEBUG_KMS("Failed to read scdc status flag: %d\n", ret);
+
+	return update;
+}
+EXPORT_SYMBOL(drm_scdc_read_status_flags);
+
+/**
+ * drm_scdc_config_frl - configure the sink for starting FRL training
+ * @adapter: I2C adapter for DDC channel
+ * @frl_rate: FRL rate per lane required.
+ * @num_lanes: no. of lanes required, can be either 3 or 4.
+ * @ffe_levelw: max FFE Levelw supported for current rate for the given FRL rate.
+ *
+ * Returns:
+ * 0 if the SCDC offsets for FRL training are successfully configure,
+ * negative error code otherwise.
+ */
+int drm_scdc_config_frl(struct i2c_adapter *adapter, int frl_rate,
+			int num_lanes, int ffe_levels)
+{
+	u8 write_buf = 0;
+	int ret;
+
+	if (num_lanes > 4 || num_lanes < 3) {
+		DRM_DEBUG_KMS("No. of lanes can be 3 or 4 only\n");
+
+		return -EINVAL;
+	}
+	if (ffe_levels > 3) {
+		DRM_DEBUG_KMS("Max FFE levels can be 3 or less\n");
+
+		return -EINVAL;
+	}
+	switch (frl_rate) {
+	case 3:
+		write_buf |= (num_lanes == 3) ? SCDC_FRL_RATE_3GBPS_3LANES : 0;
+		break;
+	case 6:
+		write_buf |= (num_lanes == 3) ? SCDC_FRL_RATE_6GBPS_3LANES :
+			     SCDC_FRL_RATE_6GBPS_4LANES;
+		break;
+	case 8:
+		write_buf |= (num_lanes == 4) ? SCDC_FRL_RATE_8GBPS_4LANES : 0;
+		break;
+	case 10:
+		write_buf |= (num_lanes == 4) ? SCDC_FRL_RATE_10GBPS_4LANES : 0;
+		break;
+	case 12:
+		write_buf |= (num_lanes == 4) ? SCDC_FRL_RATE_12GBPS_4LANES : 0;
+		break;
+	default:
+		write_buf |= SCDC_FRL_DISABLE;
+	}
+
+	write_buf |= (ffe_levels << SCDC_FFE_LEVELS_SHIFT);
+
+	ret = drm_scdc_writeb(adapter, SCDC_CONFIG_1, write_buf);
+	if (ret < 0) {
+		DRM_DEBUG_KMS("Failed to write SCDC config: %d\n", ret);
+
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_scdc_config_frl);
+
+/**
+ * drm_scdc_get_ltp - get the Link training patterns for the 4 lanes
+ * @adapter: I2C adapter for DDC channel
+ * @ltp: pointer array for reading Link training patterns for the 4 lanes.
+ *
+ * Returns:
+ * 0 on success also filling ltp out argument, negetive error code otherwise.
+ */
+int drm_scdc_get_ltp(struct i2c_adapter *adapter,
+		     enum drm_scdc_frl_ltp ltp[4])
+{
+	u8 buf;
+	u8 ltp_reg;
+	u8 ltp_mask;
+	int ret;
+	int lane;
+
+	for (lane = 0; lane < 4; lane++) {
+		switch (lane) {
+		case 0:
+			ltp_reg = SCDC_STATUS_FLAGS_1;
+			ltp_mask = SCDC_LN0_LTP_REQ;
+			break;
+		case 1:
+			ltp_reg = SCDC_STATUS_FLAGS_1;
+			ltp_mask = SCDC_LN1_LTP_REQ;
+			break;
+		case 2:
+			ltp_reg = SCDC_STATUS_FLAGS_2;
+			ltp_mask = SCDC_LN2_LTP_REQ;
+			break;
+		case 3:
+		default:
+			ltp_reg = SCDC_STATUS_FLAGS_2;
+			ltp_mask = SCDC_LN3_LTP_REQ;
+			break;
+		}
+		ret = drm_scdc_readb(adapter, ltp_reg, &buf);
+		if (ret < 0) {
+			DRM_DEBUG_KMS("Failed to read link training pattern for Lane%d: %d\n",
+				      lane, ret);
+
+			return ret;
+		}
+
+		ltp[lane] = (buf & ltp_mask) >> lane % 2;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_scdc_get_ltp);
diff --git a/include/drm/display/drm_scdc.h b/include/drm/display/drm_scdc.h
index 3d58f37e8ed8..c7e621f4cc31 100644
--- a/include/drm/display/drm_scdc.h
+++ b/include/drm/display/drm_scdc.h
@@ -29,6 +29,10 @@ 
 #define SCDC_SOURCE_VERSION 0x02
 
 #define SCDC_UPDATE_0 0x10
+#define  SCDC_RSED_UPDATE (1 << 6)
+#define  SCDC_FLT_UPDATE (1 << 5)
+#define  SCDC_FRL_START (1 << 4)
+#define  SCDC_SOURCE_TEST_UPDATE (1 << 3)
 #define  SCDC_READ_REQUEST_TEST (1 << 2)
 #define  SCDC_CED_UPDATE (1 << 1)
 #define  SCDC_STATUS_UPDATE (1 << 0)
@@ -46,7 +50,20 @@ 
 #define SCDC_CONFIG_0 0x30
 #define  SCDC_READ_REQUEST_ENABLE (1 << 0)
 
+#define SCDC_CONFIG_1 0x31
+#define  SCDC_FRL_DISABLE		0
+#define  SCDC_FRL_RATE_3GBPS_3LANES	1
+#define  SCDC_FRL_RATE_6GBPS_3LANES	2
+#define  SCDC_FRL_RATE_6GBPS_4LANES	3
+#define  SCDC_FRL_RATE_8GBPS_4LANES	4
+#define  SCDC_FRL_RATE_10GBPS_4LANES	5
+#define  SCDC_FRL_RATE_12GBPS_4LANES	6
+#define  SCDC_FFE_LEVELS_SHIFT		4
+
 #define SCDC_STATUS_FLAGS_0 0x40
+#define  SCDC_DSC_DECODE_FAIL (1 << 7)
+#define  SCDC_FLT_READY (1 << 6)
+#define  SCDC_LANE3_LOCKED (1 << 4)
 #define  SCDC_CH2_LOCK (1 << 3)
 #define  SCDC_CH1_LOCK (1 << 2)
 #define  SCDC_CH0_LOCK (1 << 1)
@@ -54,6 +71,12 @@ 
 #define  SCDC_CLOCK_DETECT (1 << 0)
 
 #define SCDC_STATUS_FLAGS_1 0x41
+#define  SCDC_LN0_LTP_REQ (0xF << 0)
+#define  SCDC_LN1_LTP_REQ (0xF << 4)
+
+#define SCDC_STATUS_FLAGS_2 0x42
+#define  SCDC_LN2_LTP_REQ (0xF << 0)
+#define  SCDC_LN3_LTP_REQ (0xF << 4)
 
 #define SCDC_ERR_DET_0_L 0x50
 #define SCDC_ERR_DET_0_H 0x51
diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h
index ded01fd948b4..405386335f20 100644
--- a/include/drm/display/drm_scdc_helper.h
+++ b/include/drm/display/drm_scdc_helper.h
@@ -35,6 +35,20 @@  ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
 ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
 		       const void *buffer, size_t size);
 
+enum drm_scdc_frl_ltp {
+	SCDC_FRL_NO_LTP = 0,
+	SCDC_FRL_LTP1,
+	SCDC_FRL_LTP2,
+	SCDC_FRL_LTP3,
+	SCDC_FRL_LTP4,
+	SCDC_FRL_LTP5,
+	SCDC_FRL_LTP6,
+	SCDC_FRL_LTP7,
+	SCDC_FRL_LTP8,
+	SCDC_FRL_CHNG_FFE = 0xE,
+	SCDC_FRL_CHNG_RATE = 0xF,
+};
+
 /**
  * drm_scdc_readb - read a single byte from SCDC
  * @adapter: I2C adapter
@@ -75,5 +89,12 @@  bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter);
 
 bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable);
 bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set);
+u8 drm_scdc_read_update_flags(struct i2c_adapter *adapter);
+int drm_scdc_clear_update_flags(struct i2c_adapter *adapter, u8 update_flags);
+u8 drm_scdc_read_status_flags(struct i2c_adapter *adapter);
+int drm_scdc_config_frl(struct i2c_adapter *adapter, int frl_rate,
+			int num_lanes, int ffe_levels);
+int drm_scdc_get_ltp(struct i2c_adapter *adapter,
+		     enum drm_scdc_frl_ltp ltp[4]);
 
 #endif