All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
@ 2018-05-04  8:08 ` Lin Huang
  0 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: seanpaul, airlied, zyw
  Cc: dianders, briannorris, linux-rockchip, heiko, daniel.vetter,
	jani.nikula, dri-devel, linux-arm-kernel, linux-kernel,
	Lin Huang

From: Chris Zhong <zyw@rock-chips.com>

We may support training outside firmware, so we need support
dpcd read/write to get the message or do some setting with
display.

Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 ++++++++++++++++++++++++----
 drivers/gpu/drm/rockchip/cdn-dp-core.h |  1 +
 drivers/gpu/drm/rockchip/cdn-dp-reg.c  | 66 +++++++++++++++++++++++++++++-----
 drivers/gpu/drm/rockchip/cdn-dp-reg.h  | 14 ++++++--
 4 files changed, 119 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index c6fbdcd..268c190 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
 	u8 value;
 
 	*sink_count = 0;
-	ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
-	if (ret)
+	ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
+	if (ret < 0)
 		return ret;
 
 	*sink_count = DP_GET_SINK_COUNT(value);
@@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
 	if (!cdn_dp_check_sink_connection(dp))
 		return -ENODEV;
 
-	ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
-			       DP_RECEIVER_CAP_SIZE);
-	if (ret) {
+	ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
+			       sizeof(dp->dpcd));
+	if (ret < 0) {
 		DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
 		return ret;
 	}
@@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
 	if (!port || !dp->link.rate || !dp->link.num_lanes)
 		return false;
 
-	if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
-			     DP_LINK_STATUS_SIZE)) {
+	if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+	    DP_LINK_STATUS_SIZE) {
 		DRM_ERROR("Failed to get link status\n");
 		return false;
 	}
@@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
 	return NOTIFY_DONE;
 }
 
+static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
+	int ret;
+	u8 status;
+
+	switch (msg->request & ~DP_AUX_I2C_MOT) {
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+		ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
+					msg->size);
+		break;
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ:
+		ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
+				       msg->size);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	status = cdn_dp_get_aux_status(dp);
+	if (status == AUX_STAUS_ACK)
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+	else if (status == AUX_STAUS_NACK)
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+	else if (status == AUX_STAUS_DEFER)
+		msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+
+	return ret;
+}
+
 static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
@@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 	dp->active = false;
 	dp->active_port = -1;
 	dp->fw_loaded = false;
+	dp->aux.name = "DP-AUX";
+	dp->aux.transfer = cdn_dp_aux_transfer;
+	dp->aux.dev = dev;
+
+	ret = drm_dp_aux_register(&dp->aux);
+	if (ret)
+		return ret;
 
 	INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index f57e296..46159b2 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -78,6 +78,7 @@ struct cdn_dp_device {
 	struct platform_device *audio_pdev;
 	struct work_struct event_work;
 	struct edid *edid;
+	struct drm_dp_aux aux;
 
 	struct mutex lock;
 	bool connected;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index eb3042c..b2f532a 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
 				   sizeof(field), field);
 }
 
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+/*
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
+ */
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 {
 	u8 msg[5], reg[5];
 	int ret;
@@ -247,24 +251,40 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 		goto err_dpcd_read;
 
 	ret = cdn_dp_mailbox_read_receive(dp, data, len);
+	if (!ret)
+		return len;
 
 err_dpcd_read:
+	DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
 	return ret;
 }
 
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+#define CDN_AUX_HEADER_SIZE	5
+#define CDN_AUX_MSG_SIZE	20
+/*
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
+ * -EINVAL is return if get the wrong data size after message send.
+ */
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 {
-	u8 msg[6], reg[5];
+	u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
+	u8 reg[CDN_AUX_HEADER_SIZE];
 	int ret;
 
-	msg[0] = 0;
-	msg[1] = 1;
+	if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
+		return -EINVAL;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
 	msg[2] = (addr >> 16) & 0xff;
 	msg[3] = (addr >> 8) & 0xff;
 	msg[4] = addr & 0xff;
-	msg[5] = value;
+
+	memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
+
 	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
-				  sizeof(msg), msg);
+				  CDN_AUX_HEADER_SIZE + len, msg);
 	if (ret)
 		goto err_dpcd_write;
 
@@ -277,8 +297,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
 	if (ret)
 		goto err_dpcd_write;
 
-	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
+	if ((len != (reg[0] << 8 | reg[1])) ||
+	    (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
 		ret = -EINVAL;
+	} else {
+		return len;
+	}
 
 err_dpcd_write:
 	if (ret)
@@ -286,6 +310,32 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
 	return ret;
 }
 
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_LAST_AUX_STAUS,
+				  0, NULL);
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_LAST_AUX_STAUS, sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	return status;
+
+err_get_hpd:
+	DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
+	return ret;
+}
+
 int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
 			 u32 i_size, const u32 *d_mem, u32 d_size)
 {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index c4bbb4a83..aedf2dc 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -328,6 +328,13 @@
 #define GENERAL_BUS_SETTINGS            0x03
 #define GENERAL_TEST_ACCESS             0x04
 
+/* AUX status*/
+#define AUX_STAUS_ACK			0
+#define AUX_STAUS_NACK			1
+#define AUX_STAUS_DEFER			2
+#define AUX_STAUS_SINK_ERROR		3
+#define AUX_STAUS_BUS_ERROR		4
+
 #define DPTX_SET_POWER_MNG			0x00
 #define DPTX_SET_HOST_CAPABILITIES		0x01
 #define DPTX_GET_EDID				0x02
@@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
 int cdn_dp_event_config(struct cdn_dp_device *dp);
 u32 cdn_dp_get_event(struct cdn_dp_device *dp);
 int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
+			  u8 *data, u16 len);
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
+			 u8 *data, u16 len);
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
 int cdn_dp_get_edid_block(void *dp, u8 *edid,
 			  unsigned int block, size_t length);
 int cdn_dp_train_link(struct cdn_dp_device *dp);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
@ 2018-05-04  8:08 ` Lin Huang
  0 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: linux-arm-kernel

From: Chris Zhong <zyw@rock-chips.com>

We may support training outside firmware, so we need support
dpcd read/write to get the message or do some setting with
display.

Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 ++++++++++++++++++++++++----
 drivers/gpu/drm/rockchip/cdn-dp-core.h |  1 +
 drivers/gpu/drm/rockchip/cdn-dp-reg.c  | 66 +++++++++++++++++++++++++++++-----
 drivers/gpu/drm/rockchip/cdn-dp-reg.h  | 14 ++++++--
 4 files changed, 119 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index c6fbdcd..268c190 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
 	u8 value;
 
 	*sink_count = 0;
-	ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
-	if (ret)
+	ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
+	if (ret < 0)
 		return ret;
 
 	*sink_count = DP_GET_SINK_COUNT(value);
@@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
 	if (!cdn_dp_check_sink_connection(dp))
 		return -ENODEV;
 
-	ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
-			       DP_RECEIVER_CAP_SIZE);
-	if (ret) {
+	ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
+			       sizeof(dp->dpcd));
+	if (ret < 0) {
 		DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
 		return ret;
 	}
@@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
 	if (!port || !dp->link.rate || !dp->link.num_lanes)
 		return false;
 
-	if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
-			     DP_LINK_STATUS_SIZE)) {
+	if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+	    DP_LINK_STATUS_SIZE) {
 		DRM_ERROR("Failed to get link status\n");
 		return false;
 	}
@@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
 	return NOTIFY_DONE;
 }
 
+static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
+	int ret;
+	u8 status;
+
+	switch (msg->request & ~DP_AUX_I2C_MOT) {
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+		ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
+					msg->size);
+		break;
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ:
+		ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
+				       msg->size);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	status = cdn_dp_get_aux_status(dp);
+	if (status == AUX_STAUS_ACK)
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+	else if (status == AUX_STAUS_NACK)
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+	else if (status == AUX_STAUS_DEFER)
+		msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+
+	return ret;
+}
+
 static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
@@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 	dp->active = false;
 	dp->active_port = -1;
 	dp->fw_loaded = false;
+	dp->aux.name = "DP-AUX";
+	dp->aux.transfer = cdn_dp_aux_transfer;
+	dp->aux.dev = dev;
+
+	ret = drm_dp_aux_register(&dp->aux);
+	if (ret)
+		return ret;
 
 	INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index f57e296..46159b2 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -78,6 +78,7 @@ struct cdn_dp_device {
 	struct platform_device *audio_pdev;
 	struct work_struct event_work;
 	struct edid *edid;
+	struct drm_dp_aux aux;
 
 	struct mutex lock;
 	bool connected;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index eb3042c..b2f532a 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
 				   sizeof(field), field);
 }
 
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+/*
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
+ */
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 {
 	u8 msg[5], reg[5];
 	int ret;
@@ -247,24 +251,40 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 		goto err_dpcd_read;
 
 	ret = cdn_dp_mailbox_read_receive(dp, data, len);
+	if (!ret)
+		return len;
 
 err_dpcd_read:
+	DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
 	return ret;
 }
 
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+#define CDN_AUX_HEADER_SIZE	5
+#define CDN_AUX_MSG_SIZE	20
+/*
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
+ * -EINVAL is return if get the wrong data size after message send.
+ */
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 {
-	u8 msg[6], reg[5];
+	u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
+	u8 reg[CDN_AUX_HEADER_SIZE];
 	int ret;
 
-	msg[0] = 0;
-	msg[1] = 1;
+	if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
+		return -EINVAL;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
 	msg[2] = (addr >> 16) & 0xff;
 	msg[3] = (addr >> 8) & 0xff;
 	msg[4] = addr & 0xff;
-	msg[5] = value;
+
+	memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
+
 	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
-				  sizeof(msg), msg);
+				  CDN_AUX_HEADER_SIZE + len, msg);
 	if (ret)
 		goto err_dpcd_write;
 
@@ -277,8 +297,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
 	if (ret)
 		goto err_dpcd_write;
 
-	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
+	if ((len != (reg[0] << 8 | reg[1])) ||
+	    (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
 		ret = -EINVAL;
+	} else {
+		return len;
+	}
 
 err_dpcd_write:
 	if (ret)
@@ -286,6 +310,32 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
 	return ret;
 }
 
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_LAST_AUX_STAUS,
+				  0, NULL);
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_LAST_AUX_STAUS, sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	return status;
+
+err_get_hpd:
+	DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
+	return ret;
+}
+
 int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
 			 u32 i_size, const u32 *d_mem, u32 d_size)
 {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index c4bbb4a83..aedf2dc 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -328,6 +328,13 @@
 #define GENERAL_BUS_SETTINGS            0x03
 #define GENERAL_TEST_ACCESS             0x04
 
+/* AUX status*/
+#define AUX_STAUS_ACK			0
+#define AUX_STAUS_NACK			1
+#define AUX_STAUS_DEFER			2
+#define AUX_STAUS_SINK_ERROR		3
+#define AUX_STAUS_BUS_ERROR		4
+
 #define DPTX_SET_POWER_MNG			0x00
 #define DPTX_SET_HOST_CAPABILITIES		0x01
 #define DPTX_GET_EDID				0x02
@@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
 int cdn_dp_event_config(struct cdn_dp_device *dp);
 u32 cdn_dp_get_event(struct cdn_dp_device *dp);
 int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
+			  u8 *data, u16 len);
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
+			 u8 *data, u16 len);
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
 int cdn_dp_get_edid_block(void *dp, u8 *edid,
 			  unsigned int block, size_t length);
 int cdn_dp_train_link(struct cdn_dp_device *dp);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/4] phy: rockchip-typec: support variable phy config value
  2018-05-04  8:08 ` Lin Huang
@ 2018-05-04  8:08   ` Lin Huang
  -1 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: seanpaul, airlied, zyw
  Cc: dianders, briannorris, linux-rockchip, heiko, daniel.vetter,
	jani.nikula, dri-devel, linux-arm-kernel, linux-kernel,
	Lin Huang

the phy config values used to fix in dp firmware, but some boards
need change these values to do training and get the better eye diagram
result. So support that in phy driver.

Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++++++++++++++++++-----------
 include/soc/rockchip/rockchip_phy_typec.h |  72 ++++++++
 2 files changed, 259 insertions(+), 99 deletions(-)
 create mode 100644 include/soc/rockchip/rockchip_phy_typec.h

diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 76a4b58..831a93b 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -63,6 +63,7 @@
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -323,23 +324,31 @@
  * clock 0: PLL 0 div 1
  * clock 1: PLL 1 div 2
  */
-#define CLK_PLL_CONFIG			0X30
+#define CLK_PLL1_DIV1			0x20
+#define CLK_PLL1_DIV2			0x30
 #define CLK_PLL_MASK			0x33
 
 #define CMN_READY			BIT(0)
 
+#define DP_PLL_CLOCK_ENABLE_ACK		BIT(3)
 #define DP_PLL_CLOCK_ENABLE		BIT(2)
+#define DP_PLL_ENABLE_ACK		BIT(1)
 #define DP_PLL_ENABLE			BIT(0)
 #define DP_PLL_DATA_RATE_RBR		((2 << 12) | (4 << 8))
 #define DP_PLL_DATA_RATE_HBR		((2 << 12) | (4 << 8))
 #define DP_PLL_DATA_RATE_HBR2		((1 << 12) | (2 << 8))
+#define DP_PLL_DATA_RATE_MASK		0xff00
 
-#define DP_MODE_A0			BIT(4)
-#define DP_MODE_A2			BIT(6)
-#define DP_MODE_ENTER_A0		0xc101
-#define DP_MODE_ENTER_A2		0xc104
+#define DP_MODE_MASK			0xf
+#define DP_MODE_ENTER_A0		BIT(0)
+#define DP_MODE_ENTER_A2		BIT(2)
+#define DP_MODE_ENTER_A3		BIT(3)
+#define DP_MODE_A0_ACK			BIT(4)
+#define DP_MODE_A2_ACK			BIT(6)
+#define DP_MODE_A3_ACK			BIT(7)
+#define DP_LINK_RESET_DEASSERTED	BIT(8)
 
-#define PHY_MODE_SET_TIMEOUT		100000
+#define PHY_MODE_SET_TIMEOUT		1000000
 
 #define PIN_ASSIGN_C_E			0x51d9
 #define PIN_ASSIGN_D_F			0x5100
@@ -349,51 +358,7 @@
 #define MODE_DFP_USB			BIT(1)
 #define MODE_DFP_DP			BIT(2)
 
-struct usb3phy_reg {
-	u32 offset;
-	u32 enable_bit;
-	u32 write_enable;
-};
-
-/**
- * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
- * @reg: the base address for usb3-phy config.
- * @typec_conn_dir: the register of type-c connector direction.
- * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
- * @external_psm: the register of type-c phy external psm clock.
- * @pipe_status: the register of type-c phy pipe status.
- * @usb3_host_disable: the register of type-c usb3 host disable.
- * @usb3_host_port: the register of type-c usb3 host port.
- * @uphy_dp_sel: the register of type-c phy DP select control.
- */
-struct rockchip_usb3phy_port_cfg {
-	unsigned int reg;
-	struct usb3phy_reg typec_conn_dir;
-	struct usb3phy_reg usb3tousb2_en;
-	struct usb3phy_reg external_psm;
-	struct usb3phy_reg pipe_status;
-	struct usb3phy_reg usb3_host_disable;
-	struct usb3phy_reg usb3_host_port;
-	struct usb3phy_reg uphy_dp_sel;
-};
-
-struct rockchip_typec_phy {
-	struct device *dev;
-	void __iomem *base;
-	struct extcon_dev *extcon;
-	struct regmap *grf_regs;
-	struct clk *clk_core;
-	struct clk *clk_ref;
-	struct reset_control *uphy_rst;
-	struct reset_control *pipe_rst;
-	struct reset_control *tcphy_rst;
-	const struct rockchip_usb3phy_port_cfg *port_cfgs;
-	/* mutex to protect access to individual PHYs */
-	struct mutex lock;
-
-	bool flip;
-	u8 mode;
-};
+#define DEFAULT_RATE			162000
 
 struct phy_reg {
 	u16 value;
@@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
 	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
 };
 
-struct phy_reg dp_pll_cfg[] = {
+struct phy_reg dp_pll_rbr_cfg[] = {
 	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
 	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
 	{ 0x30b9,	CMN_PLL1_VCOCAL_START },
-	{ 0x21c,	CMN_PLL1_INTDIV },
+	{ 0x87,		CMN_PLL1_INTDIV },
 	{ 0,		CMN_PLL1_FRACDIV },
-	{ 0x5,		CMN_PLL1_HIGH_THR },
-	{ 0x35,		CMN_PLL1_SS_CTRL1 },
-	{ 0x7f1e,	CMN_PLL1_SS_CTRL2 },
+	{ 0x22,		CMN_PLL1_HIGH_THR },
+	{ 0x8000,	CMN_PLL1_SS_CTRL1 },
+	{ 0,		CMN_PLL1_SS_CTRL2 },
 	{ 0x20,		CMN_PLL1_DSM_DIAG },
 	{ 0,		CMN_PLLSM1_USER_DEF_CTRL },
 	{ 0,		CMN_DIAG_PLL1_OVRD },
@@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
 	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
 	{ 0x100,	CMN_DIAG_PLL1_PTATIS_TUNE1 },
 	{ 0x7,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
-	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
+	{ 0x1,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+struct phy_reg dp_pll_hbr_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4,	CMN_PLL1_VCOCAL_START },
+	{ 0xe1,		CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x8000,	CMN_PLL1_SS_CTRL1 },
+	{ 0,		CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0x1000,	CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x1,		CMN_DIAG_PLL1_INCLK_CTRL },
 };
 
+struct phy_reg dp_pll_hbr2_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4,	CMN_PLL1_VCOCAL_START },
+	{ 0xe1,		CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x8000,	CMN_PLL1_SS_CTRL1 },
+	{ 0,		CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0x1000,	CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x1,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
 static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
 	{
 		.reg = 0xff7c0000,
@@ -484,7 +492,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
 
 	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
 	rdata &= ~CLK_PLL_MASK;
-	rdata |= CLK_PLL_CONFIG;
+	rdata |= CLK_PLL1_DIV2;
 	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
 }
 
@@ -498,17 +506,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
 		       tcphy->base + usb3_pll_cfg[i].addr);
 }
 
-static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
 {
-	u32 i;
+	struct phy_reg *phy_cfg;
+	u32 clk_ctrl;
+	u32 i, cfg_size, hsclk_sel;
+
+	hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	hsclk_sel &= ~CLK_PLL_MASK;
+
+	switch (link_rate) {
+	case 162000:
+		clk_ctrl = DP_PLL_DATA_RATE_RBR;
+		hsclk_sel |= CLK_PLL1_DIV2;
+		phy_cfg = dp_pll_rbr_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
+		break;
+	case 270000:
+		clk_ctrl = DP_PLL_DATA_RATE_HBR;
+		hsclk_sel |= CLK_PLL1_DIV2;
+		phy_cfg = dp_pll_hbr_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
+		break;
+	case 540000:
+		clk_ctrl = DP_PLL_DATA_RATE_HBR2;
+		hsclk_sel |= CLK_PLL1_DIV1;
+		phy_cfg = dp_pll_hbr2_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
+		break;
+	}
+
+	clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
+	writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
 
-	/* set the default mode to RBR */
-	writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
-	       tcphy->base + DP_CLK_CTL);
+	writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
 
 	/* load the configuration of PLL1 */
-	for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
-		writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+	for (i = 0; i < cfg_size; i++)
+		writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
 }
 
 static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
@@ -535,9 +570,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
 	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
 }
 
-static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
+			      u8 swing, u8 pre_emp, u32 lane)
 {
-	u16 rdata;
+	u16 val;
 
 	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
 	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
@@ -545,25 +581,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
 	writel(0x98, tcphy->base + TX_PSC_A2(lane));
 	writel(0x98, tcphy->base + TX_PSC_A3(lane));
 
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
-
-	writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
-	writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
-
-	rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
-	rdata = (rdata & 0x8fff) | 0x6000;
-	writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	writel(tcphy->config[swing][pre_emp].swing,
+	       tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(tcphy->config[swing][pre_emp].pe,
+	       tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+
+	if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
+		writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
+		writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+	} else {
+		writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+		writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
+	}
+
+	val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	val = val & 0x8fff;
+	switch (link_rate) {
+	case 162000:
+	case 270000:
+		val |= (6 << 12);
+		break;
+	case 540000:
+		val |= (4 << 12);
+		break;
+	}
+	writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
 }
 
 static inline int property_enable(struct rockchip_typec_phy *tcphy,
@@ -754,30 +796,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 	tcphy_cfg_24m(tcphy);
 
 	if (mode == MODE_DFP_DP) {
-		tcphy_cfg_dp_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
 		for (i = 0; i < 4; i++)
-			tcphy_dp_cfg_lane(tcphy, i);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, i);
 
 		writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
 	} else {
 		tcphy_cfg_usb3_pll(tcphy);
-		tcphy_cfg_dp_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
 		if (tcphy->flip) {
 			tcphy_tx_usb3_cfg_lane(tcphy, 3);
 			tcphy_rx_usb3_cfg_lane(tcphy, 2);
-			tcphy_dp_cfg_lane(tcphy, 0);
-			tcphy_dp_cfg_lane(tcphy, 1);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 0);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 1);
 		} else {
 			tcphy_tx_usb3_cfg_lane(tcphy, 0);
 			tcphy_rx_usb3_cfg_lane(tcphy, 1);
-			tcphy_dp_cfg_lane(tcphy, 2);
-			tcphy_dp_cfg_lane(tcphy, 3);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 2);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 3);
 		}
 
 		writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
 	}
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	reset_control_deassert(tcphy->uphy_rst);
 
@@ -990,7 +1035,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
 
 	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A2, 1000,
+				 val, val & DP_MODE_A2_ACK, 1000,
 				 PHY_MODE_SET_TIMEOUT);
 	if (ret < 0) {
 		dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
@@ -999,13 +1044,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 
 	tcphy_dp_aux_calibration(tcphy);
 
-	writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
+	/* enter A0 mode */
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A0;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A0, 1000,
+				 val, val & DP_MODE_A0_ACK, 1000,
 				 PHY_MODE_SET_TIMEOUT);
 	if (ret < 0) {
-		writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+		val &= ~DP_MODE_MASK;
+		val |= DP_MODE_ENTER_A2;
+		writel(val, tcphy->base + DP_MODE_CTL);
 		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
 		goto power_on_finish;
 	}
@@ -1023,6 +1074,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 static int rockchip_dp_phy_power_off(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	u32 val;
 
 	mutex_lock(&tcphy->lock);
 
@@ -1031,7 +1083,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
 
 	tcphy->mode &= ~MODE_DFP_DP;
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A2;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	if (tcphy->mode == MODE_DISCONNECT)
 		tcphy_phy_deinit(tcphy);
@@ -1047,6 +1102,30 @@ static const struct phy_ops rockchip_dp_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
+static int type_c_dp_phy_config(struct phy *phy, int link_rate,
+			 int lanes, u8 swing, u8 pre_emp)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	u8 i;
+
+	tcphy_cfg_dp_pll(tcphy, link_rate);
+
+	if (tcphy->mode == MODE_DFP_DP) {
+		for (i = 0; i < 4; i++)
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
+	} else {
+		if (tcphy->flip) {
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
+		} else {
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
+		}
+	}
+
+	return 0;
+}
+
 static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 			  struct device *dev)
 {
@@ -1087,6 +1166,14 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 		return PTR_ERR(tcphy->tcphy_rst);
 	}
 
+	/*
+	 * check if phy_config pass from dts, if yes,
+	 * need to use this phy config to do software training later
+	 */
+	if (!of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
+		(u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32)))
+		tcphy->need_software_training = 1;
+
 	return 0;
 }
 
@@ -1171,6 +1258,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 		}
 	}
 
+	tcphy->typec_phy_config = type_c_dp_phy_config;
 	pm_runtime_enable(dev);
 
 	for_each_available_child_of_node(np, child_np) {
diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
new file mode 100644
index 0000000..e25840e
--- /dev/null
+++ b/include/soc/rockchip/rockchip_phy_typec.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
+#define __SOC_ROCKCHIP_PHY_TYPEC_H
+
+
+struct usb3phy_reg {
+	u32 offset;
+	u32 enable_bit;
+	u32 write_enable;
+};
+
+/**
+ * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
+ * @reg: the base address for usb3-phy config.
+ * @typec_conn_dir: the register of type-c connector direction.
+ * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
+ * @external_psm: the register of type-c phy external psm clock.
+ * @pipe_status: the register of type-c phy pipe status.
+ * @usb3_host_disable: the register of type-c usb3 host disable.
+ * @usb3_host_port: the register of type-c usb3 host port.
+ * @uphy_dp_sel: the register of type-c phy DP select control.
+ */
+struct rockchip_usb3phy_port_cfg {
+	unsigned int reg;
+	struct usb3phy_reg typec_conn_dir;
+	struct usb3phy_reg usb3tousb2_en;
+	struct usb3phy_reg external_psm;
+	struct usb3phy_reg pipe_status;
+	struct usb3phy_reg usb3_host_disable;
+	struct usb3phy_reg usb3_host_port;
+	struct usb3phy_reg uphy_dp_sel;
+};
+
+struct phy_config {
+	int swing;
+	int pe;
+};
+
+struct rockchip_typec_phy {
+	struct device *dev;
+	void __iomem *base;
+	struct extcon_dev *extcon;
+	struct regmap *grf_regs;
+	struct clk *clk_core;
+	struct clk *clk_ref;
+	struct reset_control *uphy_rst;
+	struct reset_control *pipe_rst;
+	struct reset_control *tcphy_rst;
+	struct rockchip_usb3phy_port_cfg *port_cfgs;
+	/* mutex to protect access to individual PHYs */
+	struct mutex lock;
+	struct phy_config config[3][4];
+	u8 need_software_training;
+	bool flip;
+	u8 mode;
+	int (*typec_phy_config)(struct phy *phy, int link_rate,
+				int lanes, u8 swing, u8 pre_emp);
+};
+
+#endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/4] phy: rockchip-typec: support variable phy config value
@ 2018-05-04  8:08   ` Lin Huang
  0 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: linux-arm-kernel

the phy config values used to fix in dp firmware, but some boards
need change these values to do training and get the better eye diagram
result. So support that in phy driver.

Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++++++++++++++++++-----------
 include/soc/rockchip/rockchip_phy_typec.h |  72 ++++++++
 2 files changed, 259 insertions(+), 99 deletions(-)
 create mode 100644 include/soc/rockchip/rockchip_phy_typec.h

diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 76a4b58..831a93b 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -63,6 +63,7 @@
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -323,23 +324,31 @@
  * clock 0: PLL 0 div 1
  * clock 1: PLL 1 div 2
  */
-#define CLK_PLL_CONFIG			0X30
+#define CLK_PLL1_DIV1			0x20
+#define CLK_PLL1_DIV2			0x30
 #define CLK_PLL_MASK			0x33
 
 #define CMN_READY			BIT(0)
 
+#define DP_PLL_CLOCK_ENABLE_ACK		BIT(3)
 #define DP_PLL_CLOCK_ENABLE		BIT(2)
+#define DP_PLL_ENABLE_ACK		BIT(1)
 #define DP_PLL_ENABLE			BIT(0)
 #define DP_PLL_DATA_RATE_RBR		((2 << 12) | (4 << 8))
 #define DP_PLL_DATA_RATE_HBR		((2 << 12) | (4 << 8))
 #define DP_PLL_DATA_RATE_HBR2		((1 << 12) | (2 << 8))
+#define DP_PLL_DATA_RATE_MASK		0xff00
 
-#define DP_MODE_A0			BIT(4)
-#define DP_MODE_A2			BIT(6)
-#define DP_MODE_ENTER_A0		0xc101
-#define DP_MODE_ENTER_A2		0xc104
+#define DP_MODE_MASK			0xf
+#define DP_MODE_ENTER_A0		BIT(0)
+#define DP_MODE_ENTER_A2		BIT(2)
+#define DP_MODE_ENTER_A3		BIT(3)
+#define DP_MODE_A0_ACK			BIT(4)
+#define DP_MODE_A2_ACK			BIT(6)
+#define DP_MODE_A3_ACK			BIT(7)
+#define DP_LINK_RESET_DEASSERTED	BIT(8)
 
-#define PHY_MODE_SET_TIMEOUT		100000
+#define PHY_MODE_SET_TIMEOUT		1000000
 
 #define PIN_ASSIGN_C_E			0x51d9
 #define PIN_ASSIGN_D_F			0x5100
@@ -349,51 +358,7 @@
 #define MODE_DFP_USB			BIT(1)
 #define MODE_DFP_DP			BIT(2)
 
-struct usb3phy_reg {
-	u32 offset;
-	u32 enable_bit;
-	u32 write_enable;
-};
-
-/**
- * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
- * @reg: the base address for usb3-phy config.
- * @typec_conn_dir: the register of type-c connector direction.
- * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
- * @external_psm: the register of type-c phy external psm clock.
- * @pipe_status: the register of type-c phy pipe status.
- * @usb3_host_disable: the register of type-c usb3 host disable.
- * @usb3_host_port: the register of type-c usb3 host port.
- * @uphy_dp_sel: the register of type-c phy DP select control.
- */
-struct rockchip_usb3phy_port_cfg {
-	unsigned int reg;
-	struct usb3phy_reg typec_conn_dir;
-	struct usb3phy_reg usb3tousb2_en;
-	struct usb3phy_reg external_psm;
-	struct usb3phy_reg pipe_status;
-	struct usb3phy_reg usb3_host_disable;
-	struct usb3phy_reg usb3_host_port;
-	struct usb3phy_reg uphy_dp_sel;
-};
-
-struct rockchip_typec_phy {
-	struct device *dev;
-	void __iomem *base;
-	struct extcon_dev *extcon;
-	struct regmap *grf_regs;
-	struct clk *clk_core;
-	struct clk *clk_ref;
-	struct reset_control *uphy_rst;
-	struct reset_control *pipe_rst;
-	struct reset_control *tcphy_rst;
-	const struct rockchip_usb3phy_port_cfg *port_cfgs;
-	/* mutex to protect access to individual PHYs */
-	struct mutex lock;
-
-	bool flip;
-	u8 mode;
-};
+#define DEFAULT_RATE			162000
 
 struct phy_reg {
 	u16 value;
@@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
 	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
 };
 
-struct phy_reg dp_pll_cfg[] = {
+struct phy_reg dp_pll_rbr_cfg[] = {
 	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
 	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
 	{ 0x30b9,	CMN_PLL1_VCOCAL_START },
-	{ 0x21c,	CMN_PLL1_INTDIV },
+	{ 0x87,		CMN_PLL1_INTDIV },
 	{ 0,		CMN_PLL1_FRACDIV },
-	{ 0x5,		CMN_PLL1_HIGH_THR },
-	{ 0x35,		CMN_PLL1_SS_CTRL1 },
-	{ 0x7f1e,	CMN_PLL1_SS_CTRL2 },
+	{ 0x22,		CMN_PLL1_HIGH_THR },
+	{ 0x8000,	CMN_PLL1_SS_CTRL1 },
+	{ 0,		CMN_PLL1_SS_CTRL2 },
 	{ 0x20,		CMN_PLL1_DSM_DIAG },
 	{ 0,		CMN_PLLSM1_USER_DEF_CTRL },
 	{ 0,		CMN_DIAG_PLL1_OVRD },
@@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
 	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
 	{ 0x100,	CMN_DIAG_PLL1_PTATIS_TUNE1 },
 	{ 0x7,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
-	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
+	{ 0x1,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+struct phy_reg dp_pll_hbr_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4,	CMN_PLL1_VCOCAL_START },
+	{ 0xe1,		CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x8000,	CMN_PLL1_SS_CTRL1 },
+	{ 0,		CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0x1000,	CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x1,		CMN_DIAG_PLL1_INCLK_CTRL },
 };
 
+struct phy_reg dp_pll_hbr2_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4,	CMN_PLL1_VCOCAL_START },
+	{ 0xe1,		CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x8000,	CMN_PLL1_SS_CTRL1 },
+	{ 0,		CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0x1000,	CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x1,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x1,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
 static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
 	{
 		.reg = 0xff7c0000,
@@ -484,7 +492,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
 
 	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
 	rdata &= ~CLK_PLL_MASK;
-	rdata |= CLK_PLL_CONFIG;
+	rdata |= CLK_PLL1_DIV2;
 	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
 }
 
@@ -498,17 +506,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
 		       tcphy->base + usb3_pll_cfg[i].addr);
 }
 
-static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
 {
-	u32 i;
+	struct phy_reg *phy_cfg;
+	u32 clk_ctrl;
+	u32 i, cfg_size, hsclk_sel;
+
+	hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	hsclk_sel &= ~CLK_PLL_MASK;
+
+	switch (link_rate) {
+	case 162000:
+		clk_ctrl = DP_PLL_DATA_RATE_RBR;
+		hsclk_sel |= CLK_PLL1_DIV2;
+		phy_cfg = dp_pll_rbr_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
+		break;
+	case 270000:
+		clk_ctrl = DP_PLL_DATA_RATE_HBR;
+		hsclk_sel |= CLK_PLL1_DIV2;
+		phy_cfg = dp_pll_hbr_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
+		break;
+	case 540000:
+		clk_ctrl = DP_PLL_DATA_RATE_HBR2;
+		hsclk_sel |= CLK_PLL1_DIV1;
+		phy_cfg = dp_pll_hbr2_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
+		break;
+	}
+
+	clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
+	writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
 
-	/* set the default mode to RBR */
-	writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
-	       tcphy->base + DP_CLK_CTL);
+	writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
 
 	/* load the configuration of PLL1 */
-	for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
-		writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+	for (i = 0; i < cfg_size; i++)
+		writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
 }
 
 static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
@@ -535,9 +570,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
 	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
 }
 
-static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
+			      u8 swing, u8 pre_emp, u32 lane)
 {
-	u16 rdata;
+	u16 val;
 
 	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
 	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
@@ -545,25 +581,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
 	writel(0x98, tcphy->base + TX_PSC_A2(lane));
 	writel(0x98, tcphy->base + TX_PSC_A3(lane));
 
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
-
-	writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
-	writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
-
-	rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
-	rdata = (rdata & 0x8fff) | 0x6000;
-	writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	writel(tcphy->config[swing][pre_emp].swing,
+	       tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(tcphy->config[swing][pre_emp].pe,
+	       tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+
+	if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
+		writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
+		writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+	} else {
+		writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+		writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
+	}
+
+	val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	val = val & 0x8fff;
+	switch (link_rate) {
+	case 162000:
+	case 270000:
+		val |= (6 << 12);
+		break;
+	case 540000:
+		val |= (4 << 12);
+		break;
+	}
+	writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
 }
 
 static inline int property_enable(struct rockchip_typec_phy *tcphy,
@@ -754,30 +796,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 	tcphy_cfg_24m(tcphy);
 
 	if (mode == MODE_DFP_DP) {
-		tcphy_cfg_dp_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
 		for (i = 0; i < 4; i++)
-			tcphy_dp_cfg_lane(tcphy, i);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, i);
 
 		writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
 	} else {
 		tcphy_cfg_usb3_pll(tcphy);
-		tcphy_cfg_dp_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
 		if (tcphy->flip) {
 			tcphy_tx_usb3_cfg_lane(tcphy, 3);
 			tcphy_rx_usb3_cfg_lane(tcphy, 2);
-			tcphy_dp_cfg_lane(tcphy, 0);
-			tcphy_dp_cfg_lane(tcphy, 1);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 0);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 1);
 		} else {
 			tcphy_tx_usb3_cfg_lane(tcphy, 0);
 			tcphy_rx_usb3_cfg_lane(tcphy, 1);
-			tcphy_dp_cfg_lane(tcphy, 2);
-			tcphy_dp_cfg_lane(tcphy, 3);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 2);
+			tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 3);
 		}
 
 		writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
 	}
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	reset_control_deassert(tcphy->uphy_rst);
 
@@ -990,7 +1035,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
 
 	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A2, 1000,
+				 val, val & DP_MODE_A2_ACK, 1000,
 				 PHY_MODE_SET_TIMEOUT);
 	if (ret < 0) {
 		dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
@@ -999,13 +1044,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 
 	tcphy_dp_aux_calibration(tcphy);
 
-	writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
+	/* enter A0 mode */
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A0;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A0, 1000,
+				 val, val & DP_MODE_A0_ACK, 1000,
 				 PHY_MODE_SET_TIMEOUT);
 	if (ret < 0) {
-		writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+		val &= ~DP_MODE_MASK;
+		val |= DP_MODE_ENTER_A2;
+		writel(val, tcphy->base + DP_MODE_CTL);
 		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
 		goto power_on_finish;
 	}
@@ -1023,6 +1074,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 static int rockchip_dp_phy_power_off(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	u32 val;
 
 	mutex_lock(&tcphy->lock);
 
@@ -1031,7 +1083,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
 
 	tcphy->mode &= ~MODE_DFP_DP;
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A2;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	if (tcphy->mode == MODE_DISCONNECT)
 		tcphy_phy_deinit(tcphy);
@@ -1047,6 +1102,30 @@ static const struct phy_ops rockchip_dp_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
+static int type_c_dp_phy_config(struct phy *phy, int link_rate,
+			 int lanes, u8 swing, u8 pre_emp)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	u8 i;
+
+	tcphy_cfg_dp_pll(tcphy, link_rate);
+
+	if (tcphy->mode == MODE_DFP_DP) {
+		for (i = 0; i < 4; i++)
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
+	} else {
+		if (tcphy->flip) {
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
+		} else {
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
+		}
+	}
+
+	return 0;
+}
+
 static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 			  struct device *dev)
 {
@@ -1087,6 +1166,14 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 		return PTR_ERR(tcphy->tcphy_rst);
 	}
 
+	/*
+	 * check if phy_config pass from dts, if yes,
+	 * need to use this phy config to do software training later
+	 */
+	if (!of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
+		(u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32)))
+		tcphy->need_software_training = 1;
+
 	return 0;
 }
 
@@ -1171,6 +1258,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 		}
 	}
 
+	tcphy->typec_phy_config = type_c_dp_phy_config;
 	pm_runtime_enable(dev);
 
 	for_each_available_child_of_node(np, child_np) {
diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
new file mode 100644
index 0000000..e25840e
--- /dev/null
+++ b/include/soc/rockchip/rockchip_phy_typec.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
+#define __SOC_ROCKCHIP_PHY_TYPEC_H
+
+
+struct usb3phy_reg {
+	u32 offset;
+	u32 enable_bit;
+	u32 write_enable;
+};
+
+/**
+ * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
+ * @reg: the base address for usb3-phy config.
+ * @typec_conn_dir: the register of type-c connector direction.
+ * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
+ * @external_psm: the register of type-c phy external psm clock.
+ * @pipe_status: the register of type-c phy pipe status.
+ * @usb3_host_disable: the register of type-c usb3 host disable.
+ * @usb3_host_port: the register of type-c usb3 host port.
+ * @uphy_dp_sel: the register of type-c phy DP select control.
+ */
+struct rockchip_usb3phy_port_cfg {
+	unsigned int reg;
+	struct usb3phy_reg typec_conn_dir;
+	struct usb3phy_reg usb3tousb2_en;
+	struct usb3phy_reg external_psm;
+	struct usb3phy_reg pipe_status;
+	struct usb3phy_reg usb3_host_disable;
+	struct usb3phy_reg usb3_host_port;
+	struct usb3phy_reg uphy_dp_sel;
+};
+
+struct phy_config {
+	int swing;
+	int pe;
+};
+
+struct rockchip_typec_phy {
+	struct device *dev;
+	void __iomem *base;
+	struct extcon_dev *extcon;
+	struct regmap *grf_regs;
+	struct clk *clk_core;
+	struct clk *clk_ref;
+	struct reset_control *uphy_rst;
+	struct reset_control *pipe_rst;
+	struct reset_control *tcphy_rst;
+	struct rockchip_usb3phy_port_cfg *port_cfgs;
+	/* mutex to protect access to individual PHYs */
+	struct mutex lock;
+	struct phy_config config[3][4];
+	u8 need_software_training;
+	bool flip;
+	u8 mode;
+	int (*typec_phy_config)(struct phy *phy, int link_rate,
+				int lanes, u8 swing, u8 pre_emp);
+};
+
+#endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 3/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
  2018-05-04  8:08 ` Lin Huang
@ 2018-05-04  8:08   ` Lin Huang
  -1 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: seanpaul, airlied, zyw
  Cc: dianders, briannorris, linux-rockchip, heiko, daniel.vetter,
	jani.nikula, dri-devel, linux-arm-kernel, linux-kernel,
	Lin Huang

If want to do training outside DP Firmware, need phy voltage swing
and pre_emphasis value.

Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
index 960da7f..eda26dd 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -17,7 +17,9 @@ Required properties:
 
 Optional properties:
  - extcon : extcon specifier for the Power Delivery
-
+ - rockchip,phy_config : That's phy voltage swing and pre_emphasis
+			 setting, if want to do dp training outside
+			 dp firmware, need to add these value.
 Required nodes : a sub-node is required for each port the phy provides.
 		 The sub-node name is used to identify dp or usb3 port,
 		 and shall be the following entries:
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 3/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
@ 2018-05-04  8:08   ` Lin Huang
  0 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: linux-arm-kernel

If want to do training outside DP Firmware, need phy voltage swing
and pre_emphasis value.

Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
index 960da7f..eda26dd 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -17,7 +17,9 @@ Required properties:
 
 Optional properties:
  - extcon : extcon specifier for the Power Delivery
-
+ - rockchip,phy_config : That's phy voltage swing and pre_emphasis
+			 setting, if want to do dp training outside
+			 dp firmware, need to add these value.
 Required nodes : a sub-node is required for each port the phy provides.
 		 The sub-node name is used to identify dp or usb3 port,
 		 and shall be the following entries:
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 4/4] drm/rockchip: support dp training outside dp firmware
  2018-05-04  8:08 ` Lin Huang
@ 2018-05-04  8:08   ` Lin Huang
  -1 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: seanpaul, airlied, zyw
  Cc: dianders, briannorris, linux-rockchip, heiko, daniel.vetter,
	jani.nikula, dri-devel, linux-arm-kernel, linux-kernel,
	Lin Huang

DP firware use fix phy config value to do training, but some
board need to adjust these value to fit for their hardware design,
so we use new phy config to do training outside firmware to meet
this situation, if there have new phy config pass from dts, it will
use training outside firmware.

Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 drivers/gpu/drm/rockchip/Makefile               |   3 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.c          |  23 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
 drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 ++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  33 +-
 drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
 6 files changed, 480 insertions(+), 17 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a314e21..b932f62 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
-rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
+rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
+					cdn-dp-link-training.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 268c190..a2a4208 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
 			goto out;
 		}
 	}
-
-	ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
-		goto out;
+	if (dp->sw_training_success == false) {
+		ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev,
+				      "Failed to idle video %d\n", ret);
+			goto out;
+		}
 	}
 
 	ret = cdn_dp_config_video(dp);
@@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
 		goto out;
 	}
 
-	ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
-		goto out;
+	if (dp->sw_training_success == false) {
+		ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
+			goto out;
+		}
 	}
+
 out:
 	mutex_unlock(&dp->lock);
 }
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 46159b2..c6050ab 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -84,6 +84,7 @@ struct cdn_dp_device {
 	bool connected;
 	bool active;
 	bool suspended;
+	bool sw_training_success;
 
 	const struct firmware *fw;	/* cdn dp firmware */
 	unsigned int fw_version;	/* cdn fw version */
@@ -106,6 +107,7 @@ struct cdn_dp_device {
 	u8 ports;
 	u8 lanes;
 	int active_port;
+	u8 train_set[4];
 
 	u8 dpcd[DP_RECEIVER_CAP_SIZE];
 	bool sink_has_audio;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
new file mode 100644
index 0000000..558c945
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
@@ -0,0 +1,398 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
+{
+	struct cdn_dp_port *port = dp->port[dp->active_port];
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
+
+	int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+	u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
+		   DP_TRAIN_VOLTAGE_SWING_SHIFT;
+	u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
+			  >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+	tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
+				swing, pre_emphasis);
+}
+
+static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
+{
+	u32 phy_config, global_config;
+	int ret;
+	uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
+
+	global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
+			GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
+
+	phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
+		     DP_TX_PHY_SKEW_BYPASS(0) |
+		     DP_TX_PHY_DISPARITY_RST(0) |
+		     DP_TX_PHY_LANE0_SKEW(0) |
+		     DP_TX_PHY_LANE1_SKEW(1) |
+		     DP_TX_PHY_LANE2_SKEW(2) |
+		     DP_TX_PHY_LANE3_SKEW(3) |
+		     DP_TX_PHY_10BIT_ENABLE(0);
+
+	if (pattern != DP_TRAINING_PATTERN_DISABLE) {
+		global_config |= NO_VIDEO;
+		phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
+			      DP_TX_PHY_SCRAMBLER_BYPASS(1) |
+			      DP_TX_PHY_TRAINING_PATTERN(pattern);
+	}
+
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
+	if (ret) {
+		DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
+			  ret);
+		return ret;
+	}
+
+	ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
+	if (ret) {
+		DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
+			  ret);
+		return ret;
+	}
+
+	ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
+	if (ret) {
+		DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
+		return ret;
+	}
+
+	if (drm_dp_enhanced_frame_cap(dp->dpcd))
+		ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
+	else
+		ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
+	if (ret)
+		DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
+
+	return ret;
+}
+
+static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
+{
+	switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+	case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+		return DP_TRAIN_PRE_EMPH_LEVEL_3;
+	case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+		return DP_TRAIN_PRE_EMPH_LEVEL_2;
+	case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+		return DP_TRAIN_PRE_EMPH_LEVEL_1;
+	default:
+		return DP_TRAIN_PRE_EMPH_LEVEL_0;
+	}
+}
+
+static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
+				    uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	int i;
+	uint8_t v = 0, p = 0;
+	uint8_t preemph_max;
+
+	for (i = 0; i < dp->link.num_lanes; i++) {
+		v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
+		p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
+								  i));
+	}
+
+	if (v >= VOLTAGE_LEVEL_2)
+		v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
+
+	preemph_max = cdn_dp_pre_emphasis_max(v);
+	if (p >= preemph_max)
+		p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+	for (i = 0; i < dp->link.num_lanes; i++)
+		dp->train_set[i] = v | p;
+}
+
+/*
+ * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
+ * or 1.2 devices that support it, Training Pattern 2 otherwise.
+ */
+static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
+{
+	u32 training_pattern = DP_TRAINING_PATTERN_2;
+
+	/*
+	 * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
+	 * for downstream devices that support HBR2. However, not all sinks
+	 * follow the spec.
+	 */
+	if (drm_dp_tps3_supported(dp->dpcd))
+		training_pattern = DP_TRAINING_PATTERN_3;
+	else
+		DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
+
+	return training_pattern;
+}
+
+
+static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
+{
+	int lane;
+
+	for (lane = 0; lane < dp->link.num_lanes; lane++)
+		if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+			return false;
+
+	return true;
+}
+
+static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
+{
+	int ret;
+
+	cdn_dp_set_signal_levels(dp);
+
+	ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
+				dp->train_set, dp->link.num_lanes);
+	if (ret != dp->link.num_lanes)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
+				  uint8_t dp_train_pat)
+{
+	uint8_t buf[sizeof(dp->train_set) + 1];
+	int ret, len;
+
+	buf[0] = dp_train_pat;
+	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
+	    DP_TRAINING_PATTERN_DISABLE) {
+		/* don't write DP_TRAINING_LANEx_SET on disable */
+		len = 1;
+	} else {
+		/* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
+		memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
+		len = dp->link.num_lanes + 1;
+	}
+
+	ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
+				buf, len);
+	if (ret != len)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
+				    uint8_t dp_train_pat)
+{
+	int ret;
+
+	memset(dp->train_set, 0, sizeof(dp->train_set));
+
+	cdn_dp_set_signal_levels(dp);
+
+	ret = cdn_dp_set_pattern(dp, dp_train_pat);
+	if (ret)
+		return ret;
+
+	return cdn_dp_set_link_train(dp, dp_train_pat);
+}
+
+/* Enable corresponding port and start training pattern 1 */
+static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
+{
+	u8 voltage, link_config[2];
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	u32 voltage_tries, max_vswing_tries;
+	u32 rate, sink_max, source_max;
+	int ret;
+
+	source_max = dp->lanes;
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	dp->link.num_lanes = min(source_max, sink_max);
+
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	rate = min(source_max, sink_max);
+	dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
+
+	/* Write the link configuration data */
+	link_config[0] = dp->link.rate;
+	link_config[1] = dp->link.num_lanes;
+	if (drm_dp_enhanced_frame_cap(dp->dpcd))
+		link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+	drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
+
+	link_config[0] = 0;
+	link_config[1] = 0;
+	if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
+		link_config[1] = DP_SET_ANSI_8B10B;
+
+	drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
+
+	/* clock recovery */
+	ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
+					  DP_LINK_SCRAMBLING_DISABLE);
+	if (ret) {
+		DRM_ERROR("failed to start link train\n");
+		return ret;
+	}
+
+	voltage_tries = 1;
+	max_vswing_tries = 0;
+	for (;;) {
+		drm_dp_link_train_clock_recovery_delay(dp->dpcd);
+		if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+		    DP_LINK_STATUS_SIZE) {
+			DRM_ERROR("failed to get link status\n");
+			return -EINVAL;
+		}
+
+		if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
+			DRM_DEBUG_KMS("clock recovery OK\n");
+			return 0;
+		}
+
+		if (voltage_tries >= 5) {
+			DRM_DEBUG_KMS("Same voltage tried 5 times\n");
+			return -EINVAL;
+		}
+
+		if (max_vswing_tries >= 1) {
+			DRM_DEBUG_KMS("Max Voltage Swing reached\n");
+			return -EINVAL;
+		}
+
+		voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+		/* Update training set as requested by target */
+		cdn_dp_get_adjust_train(dp, link_status);
+		if (cdn_dp_update_link_train(dp)) {
+			DRM_ERROR("failed to update link training\n");
+			return -EINVAL;
+		}
+
+		if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
+		    voltage)
+			++voltage_tries;
+		else
+			voltage_tries = 1;
+
+		if (cdn_dp_link_max_vswing_reached(dp))
+			++max_vswing_tries;
+	}
+}
+
+static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
+{
+	int tries, ret;
+	u32 training_pattern;
+	uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+	training_pattern = cdn_dp_select_chaneq_pattern(dp);
+	training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
+
+	ret = cdn_dp_set_pattern(dp, training_pattern);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_set_link_train(dp, training_pattern);
+	if (ret) {
+		DRM_ERROR("failed to start channel equalization\n");
+		return ret;
+	}
+
+	for (tries = 0; tries < 5; tries++) {
+		drm_dp_link_train_channel_eq_delay(dp->dpcd);
+		if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+		    DP_LINK_STATUS_SIZE) {
+			DRM_ERROR("failed to get link status\n");
+			break;
+		}
+
+		/* Make sure clock is still ok */
+		if (!drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
+			DRM_DEBUG_KMS("Clock recovery check failed, cannot "
+				      "continue channel equalization\n");
+			break;
+		}
+
+		if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
+			DRM_DEBUG_KMS("Channel EQ done. DP Training "
+				      "successful\n");
+			return 0;
+		}
+
+		/* Update training set as requested by target */
+		cdn_dp_get_adjust_train(dp, link_status);
+		if (cdn_dp_update_link_train(dp)) {
+			DRM_ERROR("failed to update link training\n");
+			break;
+		}
+	}
+
+	/* Try 5 times, else fail and try at lower BW */
+	if (tries == 5)
+		DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
+
+	return -EINVAL;
+
+}
+
+static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
+{
+	int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
+
+	if (ret)
+		return ret;
+
+	return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
+}
+
+int cdn_dp_software_train_link(struct cdn_dp_device *dp)
+{
+	int ret, stop_err;
+
+	ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
+			       sizeof(dp->dpcd));
+	if (ret < 0) {
+		DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
+		return ret;
+	}
+
+	ret = cdn_dp_link_training_clock_recovery(dp);
+	if (ret) {
+		DRM_ERROR("training clock recovery fail, error: %d\n", ret);
+		goto out;
+	}
+
+	ret = cdn_dp_link_training_channel_equalization(dp);
+	if (ret) {
+		DRM_ERROR("training channel equalization fail, error: %d\n",
+			  ret);
+		goto out;
+	}
+out:
+	stop_err = cdn_dp_stop_link_train(dp);
+	if (stop_err) {
+		DRM_ERROR("stop training fail, error: %d\n", stop_err);
+		return stop_err;
+	}
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index b2f532a..72780f1 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -17,7 +17,9 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
+#include <linux/phy/phy.h>
 #include <linux/reset.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
 
 #include "cdn-dp-core.h"
 #include "cdn-dp-reg.h"
@@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
 	return 0;
 }
 
-static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
 {
 	u8 msg[6];
 
@@ -605,22 +607,41 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
 int cdn_dp_train_link(struct cdn_dp_device *dp)
 {
 	int ret;
+	struct cdn_dp_port *port = dp->port[dp->active_port];
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
 
+	/*
+	 * DP firmware uses fixed phy config values to do training, but some
+	 * boards need to adjust these values to fit for their unique hardware
+	 * design. So if the phy is using custom config values, do software
+	 * link training instead of relying on firmware, if software training
+	 * fail, keep firmware training as a fallback if sw training fails.
+	 */
+	if (tcphy->need_software_training) {
+		ret = cdn_dp_software_train_link(dp);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev,
+				"Failed to do software training %d\n", ret);
+			goto do_fw_training;
+		}
+		cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
+		dp->sw_training_success = true;
+		return 0;
+	}
+
+do_fw_training:
+	dp->sw_training_success = false;
 	ret = cdn_dp_training_start(dp);
 	if (ret) {
 		DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
 		return ret;
 	}
-
 	ret = cdn_dp_get_training_status(dp);
 	if (ret) {
 		DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
 		return ret;
 	}
-
-	DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
-			  dp->link.num_lanes);
-	return ret;
+	return 0;
 }
 
 int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index aedf2dc..b60a6b2 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -137,7 +137,7 @@
 #define HPD_EVENT_MASK			0x211c
 #define HPD_EVENT_DET			0x2120
 
-/* dpyx framer addr */
+/* dptx framer addr */
 #define DP_FRAMER_GLOBAL_CONFIG		0x2200
 #define DP_SW_RESET			0x2204
 #define DP_FRAMER_TU			0x2208
@@ -431,6 +431,40 @@
 /* Reference cycles when using lane clock as reference */
 #define LANE_REF_CYC				0x8000
 
+/* register CM_VID_CTRL */
+#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
+#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
+
+/* register DP_TX_PHY_CONFIG_REG */
+#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
+#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
+#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
+#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
+#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
+#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
+#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
+#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
+#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
+#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
+#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
+#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
+
+/* register DP_FRAMER_GLOBAL_CONFIG */
+#define NUM_LANES(x)           ((x) & 3)
+#define SST_MODE               (0 << 2)
+#define GLOBAL_EN              (1 << 3)
+#define RG_EN                  (0 << 4)
+#define NO_VIDEO               (1 << 5)
+#define ENC_RST_DIS            (1 << 6)
+#define WR_VHSYNC_FALL         (1 << 7)
+
 enum voltage_swing_level {
 	VOLTAGE_LEVEL_0,
 	VOLTAGE_LEVEL_1,
@@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
 int cdn_dp_event_config(struct cdn_dp_device *dp);
 u32 cdn_dp_get_event(struct cdn_dp_device *dp);
 int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
 ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
 			  u8 *data, u16 len);
 ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
@@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
 int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
 int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
 int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_software_train_link(struct cdn_dp_device *dp);
 #endif /* _CDN_DP_REG_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 4/4] drm/rockchip: support dp training outside dp firmware
@ 2018-05-04  8:08   ` Lin Huang
  0 siblings, 0 replies; 26+ messages in thread
From: Lin Huang @ 2018-05-04  8:08 UTC (permalink / raw)
  To: linux-arm-kernel

DP firware use fix phy config value to do training, but some
board need to adjust these value to fit for their hardware design,
so we use new phy config to do training outside firmware to meet
this situation, if there have new phy config pass from dts, it will
use training outside firmware.

Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
 drivers/gpu/drm/rockchip/Makefile               |   3 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.c          |  23 +-
 drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
 drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 ++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  33 +-
 drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
 6 files changed, 480 insertions(+), 17 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a314e21..b932f62 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
-rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
+rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
+					cdn-dp-link-training.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 268c190..a2a4208 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
 			goto out;
 		}
 	}
-
-	ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
-		goto out;
+	if (dp->sw_training_success == false) {
+		ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev,
+				      "Failed to idle video %d\n", ret);
+			goto out;
+		}
 	}
 
 	ret = cdn_dp_config_video(dp);
@@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
 		goto out;
 	}
 
-	ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
-		goto out;
+	if (dp->sw_training_success == false) {
+		ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
+			goto out;
+		}
 	}
+
 out:
 	mutex_unlock(&dp->lock);
 }
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 46159b2..c6050ab 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -84,6 +84,7 @@ struct cdn_dp_device {
 	bool connected;
 	bool active;
 	bool suspended;
+	bool sw_training_success;
 
 	const struct firmware *fw;	/* cdn dp firmware */
 	unsigned int fw_version;	/* cdn fw version */
@@ -106,6 +107,7 @@ struct cdn_dp_device {
 	u8 ports;
 	u8 lanes;
 	int active_port;
+	u8 train_set[4];
 
 	u8 dpcd[DP_RECEIVER_CAP_SIZE];
 	bool sink_has_audio;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
new file mode 100644
index 0000000..558c945
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
@@ -0,0 +1,398 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
+{
+	struct cdn_dp_port *port = dp->port[dp->active_port];
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
+
+	int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+	u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
+		   DP_TRAIN_VOLTAGE_SWING_SHIFT;
+	u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
+			  >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+	tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
+				swing, pre_emphasis);
+}
+
+static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
+{
+	u32 phy_config, global_config;
+	int ret;
+	uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
+
+	global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
+			GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
+
+	phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
+		     DP_TX_PHY_SKEW_BYPASS(0) |
+		     DP_TX_PHY_DISPARITY_RST(0) |
+		     DP_TX_PHY_LANE0_SKEW(0) |
+		     DP_TX_PHY_LANE1_SKEW(1) |
+		     DP_TX_PHY_LANE2_SKEW(2) |
+		     DP_TX_PHY_LANE3_SKEW(3) |
+		     DP_TX_PHY_10BIT_ENABLE(0);
+
+	if (pattern != DP_TRAINING_PATTERN_DISABLE) {
+		global_config |= NO_VIDEO;
+		phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
+			      DP_TX_PHY_SCRAMBLER_BYPASS(1) |
+			      DP_TX_PHY_TRAINING_PATTERN(pattern);
+	}
+
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
+	if (ret) {
+		DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
+			  ret);
+		return ret;
+	}
+
+	ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
+	if (ret) {
+		DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
+			  ret);
+		return ret;
+	}
+
+	ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
+	if (ret) {
+		DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
+		return ret;
+	}
+
+	if (drm_dp_enhanced_frame_cap(dp->dpcd))
+		ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
+	else
+		ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
+	if (ret)
+		DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
+
+	return ret;
+}
+
+static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
+{
+	switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+	case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+		return DP_TRAIN_PRE_EMPH_LEVEL_3;
+	case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+		return DP_TRAIN_PRE_EMPH_LEVEL_2;
+	case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+		return DP_TRAIN_PRE_EMPH_LEVEL_1;
+	default:
+		return DP_TRAIN_PRE_EMPH_LEVEL_0;
+	}
+}
+
+static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
+				    uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	int i;
+	uint8_t v = 0, p = 0;
+	uint8_t preemph_max;
+
+	for (i = 0; i < dp->link.num_lanes; i++) {
+		v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
+		p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
+								  i));
+	}
+
+	if (v >= VOLTAGE_LEVEL_2)
+		v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
+
+	preemph_max = cdn_dp_pre_emphasis_max(v);
+	if (p >= preemph_max)
+		p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+	for (i = 0; i < dp->link.num_lanes; i++)
+		dp->train_set[i] = v | p;
+}
+
+/*
+ * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
+ * or 1.2 devices that support it, Training Pattern 2 otherwise.
+ */
+static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
+{
+	u32 training_pattern = DP_TRAINING_PATTERN_2;
+
+	/*
+	 * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
+	 * for downstream devices that support HBR2. However, not all sinks
+	 * follow the spec.
+	 */
+	if (drm_dp_tps3_supported(dp->dpcd))
+		training_pattern = DP_TRAINING_PATTERN_3;
+	else
+		DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
+
+	return training_pattern;
+}
+
+
+static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
+{
+	int lane;
+
+	for (lane = 0; lane < dp->link.num_lanes; lane++)
+		if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+			return false;
+
+	return true;
+}
+
+static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
+{
+	int ret;
+
+	cdn_dp_set_signal_levels(dp);
+
+	ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
+				dp->train_set, dp->link.num_lanes);
+	if (ret != dp->link.num_lanes)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
+				  uint8_t dp_train_pat)
+{
+	uint8_t buf[sizeof(dp->train_set) + 1];
+	int ret, len;
+
+	buf[0] = dp_train_pat;
+	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
+	    DP_TRAINING_PATTERN_DISABLE) {
+		/* don't write DP_TRAINING_LANEx_SET on disable */
+		len = 1;
+	} else {
+		/* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
+		memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
+		len = dp->link.num_lanes + 1;
+	}
+
+	ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
+				buf, len);
+	if (ret != len)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
+				    uint8_t dp_train_pat)
+{
+	int ret;
+
+	memset(dp->train_set, 0, sizeof(dp->train_set));
+
+	cdn_dp_set_signal_levels(dp);
+
+	ret = cdn_dp_set_pattern(dp, dp_train_pat);
+	if (ret)
+		return ret;
+
+	return cdn_dp_set_link_train(dp, dp_train_pat);
+}
+
+/* Enable corresponding port and start training pattern 1 */
+static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
+{
+	u8 voltage, link_config[2];
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	u32 voltage_tries, max_vswing_tries;
+	u32 rate, sink_max, source_max;
+	int ret;
+
+	source_max = dp->lanes;
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	dp->link.num_lanes = min(source_max, sink_max);
+
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	rate = min(source_max, sink_max);
+	dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
+
+	/* Write the link configuration data */
+	link_config[0] = dp->link.rate;
+	link_config[1] = dp->link.num_lanes;
+	if (drm_dp_enhanced_frame_cap(dp->dpcd))
+		link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+	drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
+
+	link_config[0] = 0;
+	link_config[1] = 0;
+	if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
+		link_config[1] = DP_SET_ANSI_8B10B;
+
+	drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
+
+	/* clock recovery */
+	ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
+					  DP_LINK_SCRAMBLING_DISABLE);
+	if (ret) {
+		DRM_ERROR("failed to start link train\n");
+		return ret;
+	}
+
+	voltage_tries = 1;
+	max_vswing_tries = 0;
+	for (;;) {
+		drm_dp_link_train_clock_recovery_delay(dp->dpcd);
+		if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+		    DP_LINK_STATUS_SIZE) {
+			DRM_ERROR("failed to get link status\n");
+			return -EINVAL;
+		}
+
+		if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
+			DRM_DEBUG_KMS("clock recovery OK\n");
+			return 0;
+		}
+
+		if (voltage_tries >= 5) {
+			DRM_DEBUG_KMS("Same voltage tried 5 times\n");
+			return -EINVAL;
+		}
+
+		if (max_vswing_tries >= 1) {
+			DRM_DEBUG_KMS("Max Voltage Swing reached\n");
+			return -EINVAL;
+		}
+
+		voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+		/* Update training set as requested by target */
+		cdn_dp_get_adjust_train(dp, link_status);
+		if (cdn_dp_update_link_train(dp)) {
+			DRM_ERROR("failed to update link training\n");
+			return -EINVAL;
+		}
+
+		if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
+		    voltage)
+			++voltage_tries;
+		else
+			voltage_tries = 1;
+
+		if (cdn_dp_link_max_vswing_reached(dp))
+			++max_vswing_tries;
+	}
+}
+
+static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
+{
+	int tries, ret;
+	u32 training_pattern;
+	uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+	training_pattern = cdn_dp_select_chaneq_pattern(dp);
+	training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
+
+	ret = cdn_dp_set_pattern(dp, training_pattern);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_set_link_train(dp, training_pattern);
+	if (ret) {
+		DRM_ERROR("failed to start channel equalization\n");
+		return ret;
+	}
+
+	for (tries = 0; tries < 5; tries++) {
+		drm_dp_link_train_channel_eq_delay(dp->dpcd);
+		if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+		    DP_LINK_STATUS_SIZE) {
+			DRM_ERROR("failed to get link status\n");
+			break;
+		}
+
+		/* Make sure clock is still ok */
+		if (!drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
+			DRM_DEBUG_KMS("Clock recovery check failed, cannot "
+				      "continue channel equalization\n");
+			break;
+		}
+
+		if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
+			DRM_DEBUG_KMS("Channel EQ done. DP Training "
+				      "successful\n");
+			return 0;
+		}
+
+		/* Update training set as requested by target */
+		cdn_dp_get_adjust_train(dp, link_status);
+		if (cdn_dp_update_link_train(dp)) {
+			DRM_ERROR("failed to update link training\n");
+			break;
+		}
+	}
+
+	/* Try 5 times, else fail and try at lower BW */
+	if (tries == 5)
+		DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
+
+	return -EINVAL;
+
+}
+
+static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
+{
+	int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
+
+	if (ret)
+		return ret;
+
+	return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
+}
+
+int cdn_dp_software_train_link(struct cdn_dp_device *dp)
+{
+	int ret, stop_err;
+
+	ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
+			       sizeof(dp->dpcd));
+	if (ret < 0) {
+		DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
+		return ret;
+	}
+
+	ret = cdn_dp_link_training_clock_recovery(dp);
+	if (ret) {
+		DRM_ERROR("training clock recovery fail, error: %d\n", ret);
+		goto out;
+	}
+
+	ret = cdn_dp_link_training_channel_equalization(dp);
+	if (ret) {
+		DRM_ERROR("training channel equalization fail, error: %d\n",
+			  ret);
+		goto out;
+	}
+out:
+	stop_err = cdn_dp_stop_link_train(dp);
+	if (stop_err) {
+		DRM_ERROR("stop training fail, error: %d\n", stop_err);
+		return stop_err;
+	}
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index b2f532a..72780f1 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -17,7 +17,9 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
+#include <linux/phy/phy.h>
 #include <linux/reset.h>
+#include <soc/rockchip/rockchip_phy_typec.h>
 
 #include "cdn-dp-core.h"
 #include "cdn-dp-reg.h"
@@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
 	return 0;
 }
 
-static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
 {
 	u8 msg[6];
 
@@ -605,22 +607,41 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
 int cdn_dp_train_link(struct cdn_dp_device *dp)
 {
 	int ret;
+	struct cdn_dp_port *port = dp->port[dp->active_port];
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
 
+	/*
+	 * DP firmware uses fixed phy config values to do training, but some
+	 * boards need to adjust these values to fit for their unique hardware
+	 * design. So if the phy is using custom config values, do software
+	 * link training instead of relying on firmware, if software training
+	 * fail, keep firmware training as a fallback if sw training fails.
+	 */
+	if (tcphy->need_software_training) {
+		ret = cdn_dp_software_train_link(dp);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev,
+				"Failed to do software training %d\n", ret);
+			goto do_fw_training;
+		}
+		cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
+		dp->sw_training_success = true;
+		return 0;
+	}
+
+do_fw_training:
+	dp->sw_training_success = false;
 	ret = cdn_dp_training_start(dp);
 	if (ret) {
 		DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
 		return ret;
 	}
-
 	ret = cdn_dp_get_training_status(dp);
 	if (ret) {
 		DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
 		return ret;
 	}
-
-	DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
-			  dp->link.num_lanes);
-	return ret;
+	return 0;
 }
 
 int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index aedf2dc..b60a6b2 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -137,7 +137,7 @@
 #define HPD_EVENT_MASK			0x211c
 #define HPD_EVENT_DET			0x2120
 
-/* dpyx framer addr */
+/* dptx framer addr */
 #define DP_FRAMER_GLOBAL_CONFIG		0x2200
 #define DP_SW_RESET			0x2204
 #define DP_FRAMER_TU			0x2208
@@ -431,6 +431,40 @@
 /* Reference cycles when using lane clock as reference */
 #define LANE_REF_CYC				0x8000
 
+/* register CM_VID_CTRL */
+#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
+#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
+
+/* register DP_TX_PHY_CONFIG_REG */
+#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
+#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
+#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
+#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
+#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
+#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
+#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
+#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
+#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
+#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
+#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
+#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
+
+/* register DP_FRAMER_GLOBAL_CONFIG */
+#define NUM_LANES(x)           ((x) & 3)
+#define SST_MODE               (0 << 2)
+#define GLOBAL_EN              (1 << 3)
+#define RG_EN                  (0 << 4)
+#define NO_VIDEO               (1 << 5)
+#define ENC_RST_DIS            (1 << 6)
+#define WR_VHSYNC_FALL         (1 << 7)
+
 enum voltage_swing_level {
 	VOLTAGE_LEVEL_0,
 	VOLTAGE_LEVEL_1,
@@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
 int cdn_dp_event_config(struct cdn_dp_device *dp);
 u32 cdn_dp_get_event(struct cdn_dp_device *dp);
 int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
 ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
 			  u8 *data, u16 len);
 ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
@@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
 int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
 int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
 int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_software_train_link(struct cdn_dp_device *dp);
 #endif /* _CDN_DP_REG_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/4] phy: rockchip-typec: support variable phy config value
  2018-05-04  8:08   ` Lin Huang
  (?)
@ 2018-05-04 17:51     ` kbuild test robot
  -1 siblings, 0 replies; 26+ messages in thread
From: kbuild test robot @ 2018-05-04 17:51 UTC (permalink / raw)
  To: Lin Huang
  Cc: kbuild-all, seanpaul, airlied, zyw, dianders, briannorris,
	linux-rockchip, heiko, daniel.vetter, jani.nikula, dri-devel,
	linux-arm-kernel, linux-kernel, Lin Huang

[-- Attachment #1: Type: text/plain, Size: 13989 bytes --]

Hi Lin,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on rockchip/for-next]
[also build test WARNING on v4.17-rc3 next-20180504]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Lin-Huang/drm-rockchip-add-transfer-function-for-cdn-dp/20180504-235745
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
config: arm64-defconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm64 

All warnings (new ones prefixed by >>):

   drivers/phy/rockchip/phy-rockchip-typec.c: In function 'rockchip_typec_phy_probe':
>> drivers/phy/rockchip/phy-rockchip-typec.c:1227:21: warning: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
       tcphy->port_cfgs = &phy_cfgs[index];
                        ^
   drivers/phy/rockchip/phy-rockchip-typec.c: In function 'tcphy_cfg_dp_pll.isra.2':
>> drivers/phy/rockchip/phy-rockchip-typec.c:512:6: warning: 'clk_ctrl' may be used uninitialized in this function [-Wmaybe-uninitialized]
     u32 clk_ctrl;
         ^~~~~~~~
>> drivers/phy/rockchip/phy-rockchip-typec.c:511:18: warning: 'phy_cfg' may be used uninitialized in this function [-Wmaybe-uninitialized]
     struct phy_reg *phy_cfg;
                     ^~~~~~~

vim +/const +1227 drivers/phy/rockchip/phy-rockchip-typec.c

e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1194  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1195  static int rockchip_typec_phy_probe(struct platform_device *pdev)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1196  {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1197  	struct device *dev = &pdev->dev;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1198  	struct device_node *np = dev->of_node;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1199  	struct device_node *child_np;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1200  	struct rockchip_typec_phy *tcphy;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1201  	struct phy_provider *phy_provider;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1202  	struct resource *res;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1203  	const struct rockchip_usb3phy_port_cfg *phy_cfgs;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1204  	const struct of_device_id *match;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1205  	int index, ret;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1206  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1207  	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1208  	if (!tcphy)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1209  		return -ENOMEM;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1210  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1211  	match = of_match_device(dev->driver->of_match_table, dev);
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1212  	if (!match || !match->data) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1213  		dev_err(dev, "phy configs are not assigned!\n");
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1214  		return -EINVAL;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1215  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1216  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1217  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1218  	tcphy->base = devm_ioremap_resource(dev, res);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1219  	if (IS_ERR(tcphy->base))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1220  		return PTR_ERR(tcphy->base);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1221  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1222  	phy_cfgs = match->data;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1223  	/* find out a proper config which can be matched with dt. */
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1224  	index = 0;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1225  	while (phy_cfgs[index].reg) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1226  		if (phy_cfgs[index].reg == res->start) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16 @1227  			tcphy->port_cfgs = &phy_cfgs[index];
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1228  			break;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1229  		}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1230  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1231  		++index;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1232  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1233  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1234  	if (!tcphy->port_cfgs) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1235  		dev_err(dev, "no phy-config can be matched with %s node\n",
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1236  			np->name);
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1237  		return -EINVAL;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1238  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1239  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1240  	ret = tcphy_parse_dt(tcphy, dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1241  	if (ret)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1242  		return ret;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1243  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1244  	tcphy->dev = dev;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1245  	platform_set_drvdata(pdev, tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1246  	mutex_init(&tcphy->lock);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1247  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1248  	typec_phy_pre_init(tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1249  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1250  	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1251  	if (IS_ERR(tcphy->extcon)) {
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1252  		if (PTR_ERR(tcphy->extcon) == -ENODEV) {
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1253  			tcphy->extcon = NULL;
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1254  		} else {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1255  			if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1256  				dev_err(dev, "Invalid or missing extcon\n");
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1257  			return PTR_ERR(tcphy->extcon);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1258  		}
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1259  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1260  
cdb051be drivers/phy/rockchip/phy-rockchip-typec.c Lin Huang              2018-05-04  1261  	tcphy->typec_phy_config = type_c_dp_phy_config;
2a4d5962 drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-09-07  1262  	pm_runtime_enable(dev);
2a4d5962 drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-09-07  1263  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1264  	for_each_available_child_of_node(np, child_np) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1265  		struct phy *phy;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1266  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1267  		if (!of_node_cmp(child_np->name, "dp-port"))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1268  			phy = devm_phy_create(dev, child_np,
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1269  					      &rockchip_dp_phy_ops);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1270  		else if (!of_node_cmp(child_np->name, "usb3-port"))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1271  			phy = devm_phy_create(dev, child_np,
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1272  					      &rockchip_usb3_phy_ops);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1273  		else
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1274  			continue;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1275  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1276  		if (IS_ERR(phy)) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1277  			dev_err(dev, "failed to create phy: %s\n",
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1278  				child_np->name);
3cb0ab6e drivers/phy/rockchip/phy-rockchip-typec.c Chris Zhong            2016-09-08  1279  			pm_runtime_disable(dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1280  			return PTR_ERR(phy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1281  		}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1282  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1283  		phy_set_drvdata(phy, tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1284  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1285  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1286  	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1287  	if (IS_ERR(phy_provider)) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1288  		dev_err(dev, "Failed to register phy provider\n");
3cb0ab6e drivers/phy/rockchip/phy-rockchip-typec.c Chris Zhong            2016-09-08  1289  		pm_runtime_disable(dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1290  		return PTR_ERR(phy_provider);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1291  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1292  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1293  	return 0;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1294  }
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1295  

:::::: The code at line 1227 was first introduced by commit
:::::: 0fbc47d9e426a934668dbfeb0db26da6f0b7f35c phy: rockchip-typec: deprecate some DT properties for various register fields.

:::::: TO: Enric Balletbo i Serra <enric.balletbo@collabora.com>
:::::: CC: Kishon Vijay Abraham I <kishon@ti.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 38203 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/4] phy: rockchip-typec: support variable phy config value
@ 2018-05-04 17:51     ` kbuild test robot
  0 siblings, 0 replies; 26+ messages in thread
From: kbuild test robot @ 2018-05-04 17:51 UTC (permalink / raw)
  Cc: airlied, dri-devel, briannorris, dianders, linux-kernel,
	linux-rockchip, kbuild-all, zyw, daniel.vetter, linux-arm-kernel,
	Lin Huang

[-- Attachment #1: Type: text/plain, Size: 13989 bytes --]

Hi Lin,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on rockchip/for-next]
[also build test WARNING on v4.17-rc3 next-20180504]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Lin-Huang/drm-rockchip-add-transfer-function-for-cdn-dp/20180504-235745
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
config: arm64-defconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm64 

All warnings (new ones prefixed by >>):

   drivers/phy/rockchip/phy-rockchip-typec.c: In function 'rockchip_typec_phy_probe':
>> drivers/phy/rockchip/phy-rockchip-typec.c:1227:21: warning: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
       tcphy->port_cfgs = &phy_cfgs[index];
                        ^
   drivers/phy/rockchip/phy-rockchip-typec.c: In function 'tcphy_cfg_dp_pll.isra.2':
>> drivers/phy/rockchip/phy-rockchip-typec.c:512:6: warning: 'clk_ctrl' may be used uninitialized in this function [-Wmaybe-uninitialized]
     u32 clk_ctrl;
         ^~~~~~~~
>> drivers/phy/rockchip/phy-rockchip-typec.c:511:18: warning: 'phy_cfg' may be used uninitialized in this function [-Wmaybe-uninitialized]
     struct phy_reg *phy_cfg;
                     ^~~~~~~

vim +/const +1227 drivers/phy/rockchip/phy-rockchip-typec.c

e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1194  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1195  static int rockchip_typec_phy_probe(struct platform_device *pdev)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1196  {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1197  	struct device *dev = &pdev->dev;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1198  	struct device_node *np = dev->of_node;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1199  	struct device_node *child_np;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1200  	struct rockchip_typec_phy *tcphy;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1201  	struct phy_provider *phy_provider;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1202  	struct resource *res;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1203  	const struct rockchip_usb3phy_port_cfg *phy_cfgs;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1204  	const struct of_device_id *match;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1205  	int index, ret;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1206  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1207  	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1208  	if (!tcphy)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1209  		return -ENOMEM;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1210  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1211  	match = of_match_device(dev->driver->of_match_table, dev);
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1212  	if (!match || !match->data) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1213  		dev_err(dev, "phy configs are not assigned!\n");
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1214  		return -EINVAL;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1215  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1216  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1217  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1218  	tcphy->base = devm_ioremap_resource(dev, res);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1219  	if (IS_ERR(tcphy->base))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1220  		return PTR_ERR(tcphy->base);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1221  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1222  	phy_cfgs = match->data;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1223  	/* find out a proper config which can be matched with dt. */
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1224  	index = 0;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1225  	while (phy_cfgs[index].reg) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1226  		if (phy_cfgs[index].reg == res->start) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16 @1227  			tcphy->port_cfgs = &phy_cfgs[index];
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1228  			break;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1229  		}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1230  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1231  		++index;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1232  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1233  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1234  	if (!tcphy->port_cfgs) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1235  		dev_err(dev, "no phy-config can be matched with %s node\n",
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1236  			np->name);
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1237  		return -EINVAL;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1238  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1239  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1240  	ret = tcphy_parse_dt(tcphy, dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1241  	if (ret)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1242  		return ret;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1243  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1244  	tcphy->dev = dev;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1245  	platform_set_drvdata(pdev, tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1246  	mutex_init(&tcphy->lock);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1247  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1248  	typec_phy_pre_init(tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1249  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1250  	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1251  	if (IS_ERR(tcphy->extcon)) {
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1252  		if (PTR_ERR(tcphy->extcon) == -ENODEV) {
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1253  			tcphy->extcon = NULL;
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1254  		} else {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1255  			if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1256  				dev_err(dev, "Invalid or missing extcon\n");
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1257  			return PTR_ERR(tcphy->extcon);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1258  		}
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1259  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1260  
cdb051be drivers/phy/rockchip/phy-rockchip-typec.c Lin Huang              2018-05-04  1261  	tcphy->typec_phy_config = type_c_dp_phy_config;
2a4d5962 drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-09-07  1262  	pm_runtime_enable(dev);
2a4d5962 drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-09-07  1263  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1264  	for_each_available_child_of_node(np, child_np) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1265  		struct phy *phy;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1266  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1267  		if (!of_node_cmp(child_np->name, "dp-port"))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1268  			phy = devm_phy_create(dev, child_np,
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1269  					      &rockchip_dp_phy_ops);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1270  		else if (!of_node_cmp(child_np->name, "usb3-port"))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1271  			phy = devm_phy_create(dev, child_np,
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1272  					      &rockchip_usb3_phy_ops);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1273  		else
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1274  			continue;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1275  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1276  		if (IS_ERR(phy)) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1277  			dev_err(dev, "failed to create phy: %s\n",
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1278  				child_np->name);
3cb0ab6e drivers/phy/rockchip/phy-rockchip-typec.c Chris Zhong            2016-09-08  1279  			pm_runtime_disable(dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1280  			return PTR_ERR(phy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1281  		}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1282  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1283  		phy_set_drvdata(phy, tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1284  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1285  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1286  	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1287  	if (IS_ERR(phy_provider)) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1288  		dev_err(dev, "Failed to register phy provider\n");
3cb0ab6e drivers/phy/rockchip/phy-rockchip-typec.c Chris Zhong            2016-09-08  1289  		pm_runtime_disable(dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1290  		return PTR_ERR(phy_provider);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1291  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1292  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1293  	return 0;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1294  }
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1295  

:::::: The code at line 1227 was first introduced by commit
:::::: 0fbc47d9e426a934668dbfeb0db26da6f0b7f35c phy: rockchip-typec: deprecate some DT properties for various register fields.

:::::: TO: Enric Balletbo i Serra <enric.balletbo@collabora.com>
:::::: CC: Kishon Vijay Abraham I <kishon@ti.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 38203 bytes --]

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 2/4] phy: rockchip-typec: support variable phy config value
@ 2018-05-04 17:51     ` kbuild test robot
  0 siblings, 0 replies; 26+ messages in thread
From: kbuild test robot @ 2018-05-04 17:51 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lin,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on rockchip/for-next]
[also build test WARNING on v4.17-rc3 next-20180504]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Lin-Huang/drm-rockchip-add-transfer-function-for-cdn-dp/20180504-235745
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
config: arm64-defconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm64 

All warnings (new ones prefixed by >>):

   drivers/phy/rockchip/phy-rockchip-typec.c: In function 'rockchip_typec_phy_probe':
>> drivers/phy/rockchip/phy-rockchip-typec.c:1227:21: warning: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
       tcphy->port_cfgs = &phy_cfgs[index];
                        ^
   drivers/phy/rockchip/phy-rockchip-typec.c: In function 'tcphy_cfg_dp_pll.isra.2':
>> drivers/phy/rockchip/phy-rockchip-typec.c:512:6: warning: 'clk_ctrl' may be used uninitialized in this function [-Wmaybe-uninitialized]
     u32 clk_ctrl;
         ^~~~~~~~
>> drivers/phy/rockchip/phy-rockchip-typec.c:511:18: warning: 'phy_cfg' may be used uninitialized in this function [-Wmaybe-uninitialized]
     struct phy_reg *phy_cfg;
                     ^~~~~~~

vim +/const +1227 drivers/phy/rockchip/phy-rockchip-typec.c

e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1194  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1195  static int rockchip_typec_phy_probe(struct platform_device *pdev)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1196  {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1197  	struct device *dev = &pdev->dev;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1198  	struct device_node *np = dev->of_node;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1199  	struct device_node *child_np;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1200  	struct rockchip_typec_phy *tcphy;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1201  	struct phy_provider *phy_provider;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1202  	struct resource *res;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1203  	const struct rockchip_usb3phy_port_cfg *phy_cfgs;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1204  	const struct of_device_id *match;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1205  	int index, ret;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1206  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1207  	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1208  	if (!tcphy)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1209  		return -ENOMEM;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1210  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1211  	match = of_match_device(dev->driver->of_match_table, dev);
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1212  	if (!match || !match->data) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1213  		dev_err(dev, "phy configs are not assigned!\n");
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1214  		return -EINVAL;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1215  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1216  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1217  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1218  	tcphy->base = devm_ioremap_resource(dev, res);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1219  	if (IS_ERR(tcphy->base))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1220  		return PTR_ERR(tcphy->base);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1221  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1222  	phy_cfgs = match->data;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1223  	/* find out a proper config which can be matched with dt. */
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1224  	index = 0;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1225  	while (phy_cfgs[index].reg) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1226  		if (phy_cfgs[index].reg == res->start) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16 @1227  			tcphy->port_cfgs = &phy_cfgs[index];
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1228  			break;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1229  		}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1230  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1231  		++index;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1232  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1233  
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1234  	if (!tcphy->port_cfgs) {
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1235  		dev_err(dev, "no phy-config can be matched with %s node\n",
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1236  			np->name);
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1237  		return -EINVAL;
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1238  	}
0fbc47d9 drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-02-16  1239  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1240  	ret = tcphy_parse_dt(tcphy, dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1241  	if (ret)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1242  		return ret;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1243  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1244  	tcphy->dev = dev;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1245  	platform_set_drvdata(pdev, tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1246  	mutex_init(&tcphy->lock);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1247  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1248  	typec_phy_pre_init(tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1249  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1250  	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1251  	if (IS_ERR(tcphy->extcon)) {
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1252  		if (PTR_ERR(tcphy->extcon) == -ENODEV) {
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1253  			tcphy->extcon = NULL;
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1254  		} else {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1255  			if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1256  				dev_err(dev, "Invalid or missing extcon\n");
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1257  			return PTR_ERR(tcphy->extcon);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1258  		}
ec1fcd7b drivers/phy/rockchip/phy-rockchip-typec.c Enric Balletbo i Serra 2018-03-01  1259  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1260  
cdb051be drivers/phy/rockchip/phy-rockchip-typec.c Lin Huang              2018-05-04  1261  	tcphy->typec_phy_config = type_c_dp_phy_config;
2a4d5962 drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-09-07  1262  	pm_runtime_enable(dev);
2a4d5962 drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-09-07  1263  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1264  	for_each_available_child_of_node(np, child_np) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1265  		struct phy *phy;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1266  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1267  		if (!of_node_cmp(child_np->name, "dp-port"))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1268  			phy = devm_phy_create(dev, child_np,
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1269  					      &rockchip_dp_phy_ops);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1270  		else if (!of_node_cmp(child_np->name, "usb3-port"))
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1271  			phy = devm_phy_create(dev, child_np,
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1272  					      &rockchip_usb3_phy_ops);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1273  		else
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1274  			continue;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1275  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1276  		if (IS_ERR(phy)) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1277  			dev_err(dev, "failed to create phy: %s\n",
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1278  				child_np->name);
3cb0ab6e drivers/phy/rockchip/phy-rockchip-typec.c Chris Zhong            2016-09-08  1279  			pm_runtime_disable(dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1280  			return PTR_ERR(phy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1281  		}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1282  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1283  		phy_set_drvdata(phy, tcphy);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1284  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1285  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1286  	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1287  	if (IS_ERR(phy_provider)) {
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1288  		dev_err(dev, "Failed to register phy provider\n");
3cb0ab6e drivers/phy/rockchip/phy-rockchip-typec.c Chris Zhong            2016-09-08  1289  		pm_runtime_disable(dev);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1290  		return PTR_ERR(phy_provider);
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1291  	}
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1292  
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1293  	return 0;
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1294  }
e96be45c drivers/phy/phy-rockchip-typec.c          Chris Zhong            2016-08-23  1295  

:::::: The code at line 1227 was first introduced by commit
:::::: 0fbc47d9e426a934668dbfeb0db26da6f0b7f35c phy: rockchip-typec: deprecate some DT properties for various register fields.

:::::: TO: Enric Balletbo i Serra <enric.balletbo@collabora.com>
:::::: CC: Kishon Vijay Abraham I <kishon@ti.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 38203 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180505/e57a34fa/attachment-0001.gz>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
  2018-05-04  8:08 ` Lin Huang
  (?)
@ 2018-05-07 11:27   ` Enric Balletbo Serra
  -1 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 11:27 UTC (permalink / raw)
  To: Lin Huang
  Cc: Sean Paul, David Airlie, Chris Zhong, Heiko Stübner,
	Brian Norris, Doug Anderson, jani.nikula, linux-kernel,
	open list:ARM/Rockchip SoC...,
	dri-devel, daniel.vetter, Linux ARM

Hi Lin,

I am interested in these patches, could you cc me on newer versions? Thanks.

Some comments below.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> From: Chris Zhong <zyw@rock-chips.com>
>
> We may support training outside firmware, so we need support
> dpcd read/write to get the message or do some setting with
> display.
>
> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 ++++++++++++++++++++++++----
>  drivers/gpu/drm/rockchip/cdn-dp-core.h |  1 +
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c  | 66 +++++++++++++++++++++++++++++-----
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h  | 14 ++++++--
>  4 files changed, 119 insertions(+), 17 deletions(-)
>

In general, for this patch and all the other patches in the series I
saw that checkpatch spits out some warnings, could you fix these and
ideally run checkpatch with the --strict --subjective option?

> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> index c6fbdcd..268c190 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
>         u8 value;
>
>         *sink_count = 0;
> -       ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
> -       if (ret)
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
> +       if (ret < 0)
>                 return ret;
>
>         *sink_count = DP_GET_SINK_COUNT(value);
> @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
>         if (!cdn_dp_check_sink_connection(dp))
>                 return -ENODEV;
>
> -       ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
> -                              DP_RECEIVER_CAP_SIZE);
> -       if (ret) {
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
> +                              sizeof(dp->dpcd));
> +       if (ret < 0) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>                 return ret;
>         }
> @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
>         if (!port || !dp->link.rate || !dp->link.num_lanes)
>                 return false;
>
> -       if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
> -                            DP_LINK_STATUS_SIZE)) {
> +       if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +           DP_LINK_STATUS_SIZE) {
>                 DRM_ERROR("Failed to get link status\n");
>                 return false;
>         }
> @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
>         return NOTIFY_DONE;
>  }
>
> +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
> +       int ret;
> +       u8 status;
> +
> +       switch (msg->request & ~DP_AUX_I2C_MOT) {
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +               ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
> +                                       msg->size);
> +               break;
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ:
> +               ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
> +                                      msg->size);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       status = cdn_dp_get_aux_status(dp);
> +       if (status == AUX_STAUS_ACK)
> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
> +       else if (status == AUX_STAUS_NACK)
> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK;
> +       else if (status == AUX_STAUS_DEFER)
> +               msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
> +

I think that you would mean STATUS instead of STAUS on these defines.

What happens if the status is AUX_STATUS_SINK_ERROR or AUX_STATUS_BUS_ERROR?

> +       return ret;
> +}
> +
>  static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct cdn_dp_device *dp = dev_get_drvdata(dev);
> @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>         dp->active = false;
>         dp->active_port = -1;
>         dp->fw_loaded = false;
> +       dp->aux.name = "DP-AUX";
> +       dp->aux.transfer = cdn_dp_aux_transfer;
> +       dp->aux.dev = dev;
> +
> +       ret = drm_dp_aux_register(&dp->aux);
> +       if (ret)
> +               return ret;
>
>         INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
>
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> index f57e296..46159b2 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -78,6 +78,7 @@ struct cdn_dp_device {
>         struct platform_device *audio_pdev;
>         struct work_struct event_work;
>         struct edid *edid;
> +       struct drm_dp_aux aux;
>
>         struct mutex lock;
>         bool connected;
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> index eb3042c..b2f532a 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
>                                    sizeof(field), field);
>  }
>
> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
> +/*
> + * Returns the number of bytes transferred on success, or a negative error
> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
> + */

Returns the number of bytes or -ETIMEDOUT, no other negative errors
can be returned, right?

I am not English native but the last phrase sounds incorrect to me,
I'd rephrase it: (open to suggestions)

 -ETIMEDOUT if fails to receive the mailbox message.

> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>  {
>         u8 msg[5], reg[5];
>         int ret;
> @@ -247,24 +251,40 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>                 goto err_dpcd_read;
>
>         ret = cdn_dp_mailbox_read_receive(dp, data, len);
> +       if (!ret)
> +               return len;
>
>  err_dpcd_read:
> +       DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
>         return ret;
>  }
>
> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
> +#define CDN_AUX_HEADER_SIZE    5
> +#define CDN_AUX_MSG_SIZE       20
> +/*
> + * Returns the number of bytes transferred on success, or a negative error
> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;

Same as above. Sounds incorrect to me.

> + * -EINVAL is return if get the wrong data size after message send.

Same here.

> + */
> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>  {
> -       u8 msg[6], reg[5];
> +       u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
> +       u8 reg[CDN_AUX_HEADER_SIZE];
>         int ret;
>
> -       msg[0] = 0;
> -       msg[1] = 1;
> +       if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
> +               return -EINVAL;
> +
> +       msg[0] = (len >> 8) & 0xff;
> +       msg[1] = len & 0xff;
>         msg[2] = (addr >> 16) & 0xff;
>         msg[3] = (addr >> 8) & 0xff;
>         msg[4] = addr & 0xff;
> -       msg[5] = value;
> +
> +       memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
> +
>         ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
> -                                 sizeof(msg), msg);
> +                                 CDN_AUX_HEADER_SIZE + len, msg);
>         if (ret)
>                 goto err_dpcd_write;
>
> @@ -277,8 +297,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>         if (ret)
>                 goto err_dpcd_write;
>
> -       if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
> +       if ((len != (reg[0] << 8 | reg[1])) ||
> +           (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
>                 ret = -EINVAL;
> +       } else {
> +               return len;
> +       }
>
>  err_dpcd_write:
>         if (ret)
> @@ -286,6 +310,32 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>         return ret;
>  }
>
> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
> +{
> +       u8 status;
> +       int ret;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_LAST_AUX_STAUS,
> +                                 0, NULL);
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_GET_LAST_AUX_STAUS, sizeof(status));
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       return status;
> +
> +err_get_hpd:
> +       DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
> +       return ret;
> +}
> +
>  int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>                          u32 i_size, const u32 *d_mem, u32 d_size)
>  {
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> index c4bbb4a83..aedf2dc 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -328,6 +328,13 @@
>  #define GENERAL_BUS_SETTINGS            0x03
>  #define GENERAL_TEST_ACCESS             0x04
>
> +/* AUX status*/
> +#define AUX_STAUS_ACK                  0
> +#define AUX_STAUS_NACK                 1
> +#define AUX_STAUS_DEFER                        2
> +#define AUX_STAUS_SINK_ERROR           3
> +#define AUX_STAUS_BUS_ERROR            4
> +

For the five defines, s/STAUS/STATUS/

>  #define DPTX_SET_POWER_MNG                     0x00
>  #define DPTX_SET_HOST_CAPABILITIES             0x01
>  #define DPTX_GET_EDID                          0x02
> @@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>  int cdn_dp_event_config(struct cdn_dp_device *dp);
>  u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>  int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
> +                         u8 *data, u16 len);
> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
> +                        u8 *data, u16 len);
> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
>  int cdn_dp_get_edid_block(void *dp, u8 *edid,
>                           unsigned int block, size_t length);
>  int cdn_dp_train_link(struct cdn_dp_device *dp);
> --
> 2.7.4
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
@ 2018-05-07 11:27   ` Enric Balletbo Serra
  0 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 11:27 UTC (permalink / raw)
  To: Lin Huang
  Cc: David Airlie, Brian Norris, Doug Anderson, linux-kernel,
	open list:ARM/Rockchip SoC...,
	dri-devel, Chris Zhong, daniel.vetter, Linux ARM

Hi Lin,

I am interested in these patches, could you cc me on newer versions? Thanks.

Some comments below.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> From: Chris Zhong <zyw@rock-chips.com>
>
> We may support training outside firmware, so we need support
> dpcd read/write to get the message or do some setting with
> display.
>
> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 ++++++++++++++++++++++++----
>  drivers/gpu/drm/rockchip/cdn-dp-core.h |  1 +
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c  | 66 +++++++++++++++++++++++++++++-----
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h  | 14 ++++++--
>  4 files changed, 119 insertions(+), 17 deletions(-)
>

In general, for this patch and all the other patches in the series I
saw that checkpatch spits out some warnings, could you fix these and
ideally run checkpatch with the --strict --subjective option?

> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> index c6fbdcd..268c190 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
>         u8 value;
>
>         *sink_count = 0;
> -       ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
> -       if (ret)
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
> +       if (ret < 0)
>                 return ret;
>
>         *sink_count = DP_GET_SINK_COUNT(value);
> @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
>         if (!cdn_dp_check_sink_connection(dp))
>                 return -ENODEV;
>
> -       ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
> -                              DP_RECEIVER_CAP_SIZE);
> -       if (ret) {
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
> +                              sizeof(dp->dpcd));
> +       if (ret < 0) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>                 return ret;
>         }
> @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
>         if (!port || !dp->link.rate || !dp->link.num_lanes)
>                 return false;
>
> -       if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
> -                            DP_LINK_STATUS_SIZE)) {
> +       if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +           DP_LINK_STATUS_SIZE) {
>                 DRM_ERROR("Failed to get link status\n");
>                 return false;
>         }
> @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
>         return NOTIFY_DONE;
>  }
>
> +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
> +       int ret;
> +       u8 status;
> +
> +       switch (msg->request & ~DP_AUX_I2C_MOT) {
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +               ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
> +                                       msg->size);
> +               break;
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ:
> +               ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
> +                                      msg->size);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       status = cdn_dp_get_aux_status(dp);
> +       if (status == AUX_STAUS_ACK)
> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
> +       else if (status == AUX_STAUS_NACK)
> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK;
> +       else if (status == AUX_STAUS_DEFER)
> +               msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
> +

I think that you would mean STATUS instead of STAUS on these defines.

What happens if the status is AUX_STATUS_SINK_ERROR or AUX_STATUS_BUS_ERROR?

> +       return ret;
> +}
> +
>  static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct cdn_dp_device *dp = dev_get_drvdata(dev);
> @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>         dp->active = false;
>         dp->active_port = -1;
>         dp->fw_loaded = false;
> +       dp->aux.name = "DP-AUX";
> +       dp->aux.transfer = cdn_dp_aux_transfer;
> +       dp->aux.dev = dev;
> +
> +       ret = drm_dp_aux_register(&dp->aux);
> +       if (ret)
> +               return ret;
>
>         INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
>
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> index f57e296..46159b2 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -78,6 +78,7 @@ struct cdn_dp_device {
>         struct platform_device *audio_pdev;
>         struct work_struct event_work;
>         struct edid *edid;
> +       struct drm_dp_aux aux;
>
>         struct mutex lock;
>         bool connected;
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> index eb3042c..b2f532a 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
>                                    sizeof(field), field);
>  }
>
> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
> +/*
> + * Returns the number of bytes transferred on success, or a negative error
> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
> + */

Returns the number of bytes or -ETIMEDOUT, no other negative errors
can be returned, right?

I am not English native but the last phrase sounds incorrect to me,
I'd rephrase it: (open to suggestions)

 -ETIMEDOUT if fails to receive the mailbox message.

> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>  {
>         u8 msg[5], reg[5];
>         int ret;
> @@ -247,24 +251,40 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>                 goto err_dpcd_read;
>
>         ret = cdn_dp_mailbox_read_receive(dp, data, len);
> +       if (!ret)
> +               return len;
>
>  err_dpcd_read:
> +       DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
>         return ret;
>  }
>
> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
> +#define CDN_AUX_HEADER_SIZE    5
> +#define CDN_AUX_MSG_SIZE       20
> +/*
> + * Returns the number of bytes transferred on success, or a negative error
> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;

Same as above. Sounds incorrect to me.

> + * -EINVAL is return if get the wrong data size after message send.

Same here.

> + */
> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>  {
> -       u8 msg[6], reg[5];
> +       u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
> +       u8 reg[CDN_AUX_HEADER_SIZE];
>         int ret;
>
> -       msg[0] = 0;
> -       msg[1] = 1;
> +       if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
> +               return -EINVAL;
> +
> +       msg[0] = (len >> 8) & 0xff;
> +       msg[1] = len & 0xff;
>         msg[2] = (addr >> 16) & 0xff;
>         msg[3] = (addr >> 8) & 0xff;
>         msg[4] = addr & 0xff;
> -       msg[5] = value;
> +
> +       memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
> +
>         ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
> -                                 sizeof(msg), msg);
> +                                 CDN_AUX_HEADER_SIZE + len, msg);
>         if (ret)
>                 goto err_dpcd_write;
>
> @@ -277,8 +297,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>         if (ret)
>                 goto err_dpcd_write;
>
> -       if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
> +       if ((len != (reg[0] << 8 | reg[1])) ||
> +           (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
>                 ret = -EINVAL;
> +       } else {
> +               return len;
> +       }
>
>  err_dpcd_write:
>         if (ret)
> @@ -286,6 +310,32 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>         return ret;
>  }
>
> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
> +{
> +       u8 status;
> +       int ret;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_LAST_AUX_STAUS,
> +                                 0, NULL);
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_GET_LAST_AUX_STAUS, sizeof(status));
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       return status;
> +
> +err_get_hpd:
> +       DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
> +       return ret;
> +}
> +
>  int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>                          u32 i_size, const u32 *d_mem, u32 d_size)
>  {
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> index c4bbb4a83..aedf2dc 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -328,6 +328,13 @@
>  #define GENERAL_BUS_SETTINGS            0x03
>  #define GENERAL_TEST_ACCESS             0x04
>
> +/* AUX status*/
> +#define AUX_STAUS_ACK                  0
> +#define AUX_STAUS_NACK                 1
> +#define AUX_STAUS_DEFER                        2
> +#define AUX_STAUS_SINK_ERROR           3
> +#define AUX_STAUS_BUS_ERROR            4
> +

For the five defines, s/STAUS/STATUS/

>  #define DPTX_SET_POWER_MNG                     0x00
>  #define DPTX_SET_HOST_CAPABILITIES             0x01
>  #define DPTX_GET_EDID                          0x02
> @@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>  int cdn_dp_event_config(struct cdn_dp_device *dp);
>  u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>  int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
> +                         u8 *data, u16 len);
> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
> +                        u8 *data, u16 len);
> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
>  int cdn_dp_get_edid_block(void *dp, u8 *edid,
>                           unsigned int block, size_t length);
>  int cdn_dp_train_link(struct cdn_dp_device *dp);
> --
> 2.7.4
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
@ 2018-05-07 11:27   ` Enric Balletbo Serra
  0 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 11:27 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lin,

I am interested in these patches, could you cc me on newer versions? Thanks.

Some comments below.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> From: Chris Zhong <zyw@rock-chips.com>
>
> We may support training outside firmware, so we need support
> dpcd read/write to get the message or do some setting with
> display.
>
> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 ++++++++++++++++++++++++----
>  drivers/gpu/drm/rockchip/cdn-dp-core.h |  1 +
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c  | 66 +++++++++++++++++++++++++++++-----
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h  | 14 ++++++--
>  4 files changed, 119 insertions(+), 17 deletions(-)
>

In general, for this patch and all the other patches in the series I
saw that checkpatch spits out some warnings, could you fix these and
ideally run checkpatch with the --strict --subjective option?

> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> index c6fbdcd..268c190 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
>         u8 value;
>
>         *sink_count = 0;
> -       ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
> -       if (ret)
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
> +       if (ret < 0)
>                 return ret;
>
>         *sink_count = DP_GET_SINK_COUNT(value);
> @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
>         if (!cdn_dp_check_sink_connection(dp))
>                 return -ENODEV;
>
> -       ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
> -                              DP_RECEIVER_CAP_SIZE);
> -       if (ret) {
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
> +                              sizeof(dp->dpcd));
> +       if (ret < 0) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>                 return ret;
>         }
> @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
>         if (!port || !dp->link.rate || !dp->link.num_lanes)
>                 return false;
>
> -       if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
> -                            DP_LINK_STATUS_SIZE)) {
> +       if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +           DP_LINK_STATUS_SIZE) {
>                 DRM_ERROR("Failed to get link status\n");
>                 return false;
>         }
> @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
>         return NOTIFY_DONE;
>  }
>
> +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
> +                                  struct drm_dp_aux_msg *msg)
> +{
> +       struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
> +       int ret;
> +       u8 status;
> +
> +       switch (msg->request & ~DP_AUX_I2C_MOT) {
> +       case DP_AUX_NATIVE_WRITE:
> +       case DP_AUX_I2C_WRITE:
> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
> +               ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
> +                                       msg->size);
> +               break;
> +       case DP_AUX_NATIVE_READ:
> +       case DP_AUX_I2C_READ:
> +               ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
> +                                      msg->size);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       status = cdn_dp_get_aux_status(dp);
> +       if (status == AUX_STAUS_ACK)
> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
> +       else if (status == AUX_STAUS_NACK)
> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK;
> +       else if (status == AUX_STAUS_DEFER)
> +               msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
> +

I think that you would mean STATUS instead of STAUS on these defines.

What happens if the status is AUX_STATUS_SINK_ERROR or AUX_STATUS_BUS_ERROR?

> +       return ret;
> +}
> +
>  static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct cdn_dp_device *dp = dev_get_drvdata(dev);
> @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>         dp->active = false;
>         dp->active_port = -1;
>         dp->fw_loaded = false;
> +       dp->aux.name = "DP-AUX";
> +       dp->aux.transfer = cdn_dp_aux_transfer;
> +       dp->aux.dev = dev;
> +
> +       ret = drm_dp_aux_register(&dp->aux);
> +       if (ret)
> +               return ret;
>
>         INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
>
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> index f57e296..46159b2 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -78,6 +78,7 @@ struct cdn_dp_device {
>         struct platform_device *audio_pdev;
>         struct work_struct event_work;
>         struct edid *edid;
> +       struct drm_dp_aux aux;
>
>         struct mutex lock;
>         bool connected;
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> index eb3042c..b2f532a 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
>                                    sizeof(field), field);
>  }
>
> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
> +/*
> + * Returns the number of bytes transferred on success, or a negative error
> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
> + */

Returns the number of bytes or -ETIMEDOUT, no other negative errors
can be returned, right?

I am not English native but the last phrase sounds incorrect to me,
I'd rephrase it: (open to suggestions)

 -ETIMEDOUT if fails to receive the mailbox message.

> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>  {
>         u8 msg[5], reg[5];
>         int ret;
> @@ -247,24 +251,40 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>                 goto err_dpcd_read;
>
>         ret = cdn_dp_mailbox_read_receive(dp, data, len);
> +       if (!ret)
> +               return len;
>
>  err_dpcd_read:
> +       DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
>         return ret;
>  }
>
> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
> +#define CDN_AUX_HEADER_SIZE    5
> +#define CDN_AUX_MSG_SIZE       20
> +/*
> + * Returns the number of bytes transferred on success, or a negative error
> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;

Same as above. Sounds incorrect to me.

> + * -EINVAL is return if get the wrong data size after message send.

Same here.

> + */
> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>  {
> -       u8 msg[6], reg[5];
> +       u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
> +       u8 reg[CDN_AUX_HEADER_SIZE];
>         int ret;
>
> -       msg[0] = 0;
> -       msg[1] = 1;
> +       if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
> +               return -EINVAL;
> +
> +       msg[0] = (len >> 8) & 0xff;
> +       msg[1] = len & 0xff;
>         msg[2] = (addr >> 16) & 0xff;
>         msg[3] = (addr >> 8) & 0xff;
>         msg[4] = addr & 0xff;
> -       msg[5] = value;
> +
> +       memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
> +
>         ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
> -                                 sizeof(msg), msg);
> +                                 CDN_AUX_HEADER_SIZE + len, msg);
>         if (ret)
>                 goto err_dpcd_write;
>
> @@ -277,8 +297,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>         if (ret)
>                 goto err_dpcd_write;
>
> -       if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
> +       if ((len != (reg[0] << 8 | reg[1])) ||
> +           (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
>                 ret = -EINVAL;
> +       } else {
> +               return len;
> +       }
>
>  err_dpcd_write:
>         if (ret)
> @@ -286,6 +310,32 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>         return ret;
>  }
>
> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
> +{
> +       u8 status;
> +       int ret;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_LAST_AUX_STAUS,
> +                                 0, NULL);
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_GET_LAST_AUX_STAUS, sizeof(status));
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
> +       if (ret)
> +               goto err_get_hpd;
> +
> +       return status;
> +
> +err_get_hpd:
> +       DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
> +       return ret;
> +}
> +
>  int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>                          u32 i_size, const u32 *d_mem, u32 d_size)
>  {
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> index c4bbb4a83..aedf2dc 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -328,6 +328,13 @@
>  #define GENERAL_BUS_SETTINGS            0x03
>  #define GENERAL_TEST_ACCESS             0x04
>
> +/* AUX status*/
> +#define AUX_STAUS_ACK                  0
> +#define AUX_STAUS_NACK                 1
> +#define AUX_STAUS_DEFER                        2
> +#define AUX_STAUS_SINK_ERROR           3
> +#define AUX_STAUS_BUS_ERROR            4
> +

For the five defines, s/STAUS/STATUS/

>  #define DPTX_SET_POWER_MNG                     0x00
>  #define DPTX_SET_HOST_CAPABILITIES             0x01
>  #define DPTX_GET_EDID                          0x02
> @@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>  int cdn_dp_event_config(struct cdn_dp_device *dp);
>  u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>  int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
> +                         u8 *data, u16 len);
> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
> +                        u8 *data, u16 len);
> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
>  int cdn_dp_get_edid_block(void *dp, u8 *edid,
>                           unsigned int block, size_t length);
>  int cdn_dp_train_link(struct cdn_dp_device *dp);
> --
> 2.7.4
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 4/4] drm/rockchip: support dp training outside dp firmware
  2018-05-04  8:08   ` Lin Huang
  (?)
@ 2018-05-07 11:29     ` Enric Balletbo Serra
  -1 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 11:29 UTC (permalink / raw)
  To: Lin Huang
  Cc: Sean Paul, David Airlie, Chris Zhong, Doug Anderson,
	Brian Norris, open list:ARM/Rockchip SoC...,
	Heiko Stübner, daniel.vetter, jani.nikula, dri-devel,
	Linux ARM, linux-kernel

Hi Lin,

Thanks for the patch.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> DP firware use fix phy config value to do training, but some

s/fiware/firmware/

> board need to adjust these value to fit for their hardware design,
> so we use new phy config to do training outside firmware to meet
> this situation, if there have new phy config pass from dts, it will
> use training outside firmware.
>

maybe you can rewrite all this in a better way.

ooi, which boards needs this?


> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/gpu/drm/rockchip/Makefile               |   3 +-
>  drivers/gpu/drm/rockchip/cdn-dp-core.c          |  23 +-
>  drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
>  drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 ++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  33 +-
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
>  6 files changed, 480 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index a314e21..b932f62 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>
>  rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
> +                                       cdn-dp-link-training.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> index 268c190..a2a4208 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>                         goto out;
>                 }
>         }
> -
> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> -       if (ret) {
> -               DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
> -               goto out;
> +       if (dp->sw_training_success == false) {
> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev,
> +                                     "Failed to idle video %d\n", ret);
> +                       goto out;
> +               }
>         }
>
>         ret = cdn_dp_config_video(dp);
> @@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>                 goto out;
>         }
>
> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
> -       if (ret) {
> -               DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
> -               goto out;
> +       if (dp->sw_training_success == false) {
> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
> +                       goto out;
> +               }
>         }
> +
>  out:
>         mutex_unlock(&dp->lock);
>  }
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> index 46159b2..c6050ab 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -84,6 +84,7 @@ struct cdn_dp_device {
>         bool connected;
>         bool active;
>         bool suspended;
> +       bool sw_training_success;
>
>         const struct firmware *fw;      /* cdn dp firmware */
>         unsigned int fw_version;        /* cdn fw version */
> @@ -106,6 +107,7 @@ struct cdn_dp_device {
>         u8 ports;
>         u8 lanes;
>         int active_port;
> +       u8 train_set[4];
>
>         u8 dpcd[DP_RECEIVER_CAP_SIZE];
>         bool sink_has_audio;
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
> new file mode 100644
> index 0000000..558c945
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
> @@ -0,0 +1,398 @@
> +/* SPDX-License-Identifier: GPL-2.0 */

For a C source file the format is:
(https://www.kernel.org/doc/html/latest/process/license-rules.html)

// SPDX-License-Identifier: <SPDX License Expression>

> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw@rock-chips.com>
> + */
> +
> +#include <linux/arm-smccc.h>

Why you need this include?

> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/phy/phy.h>
> +#include <linux/reset.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
> +

In fact, I think that there are other includes that can be removed,
please review.

> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +
> +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
> +{
> +       struct cdn_dp_port *port = dp->port[dp->active_port];
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
> +
> +       int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
> +       u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
> +                  DP_TRAIN_VOLTAGE_SWING_SHIFT;
> +       u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
> +                         >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +       tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
> +                               swing, pre_emphasis);
> +}
> +
> +static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
> +{
> +       u32 phy_config, global_config;
> +       int ret;
> +       uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
> +
> +       global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
> +                       GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
> +
> +       phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
> +                    DP_TX_PHY_SKEW_BYPASS(0) |
> +                    DP_TX_PHY_DISPARITY_RST(0) |
> +                    DP_TX_PHY_LANE0_SKEW(0) |
> +                    DP_TX_PHY_LANE1_SKEW(1) |
> +                    DP_TX_PHY_LANE2_SKEW(2) |
> +                    DP_TX_PHY_LANE3_SKEW(3) |
> +                    DP_TX_PHY_10BIT_ENABLE(0);
> +
> +       if (pattern != DP_TRAINING_PATTERN_DISABLE) {
> +               global_config |= NO_VIDEO;
> +               phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
> +                             DP_TX_PHY_SCRAMBLER_BYPASS(1) |
> +                             DP_TX_PHY_TRAINING_PATTERN(pattern);
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
> +       if (ret) {
> +               DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
> +                         ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
> +       if (ret) {
> +               DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
> +                         ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
> +       if (ret) {
> +               DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
> +               return ret;
> +       }
> +
> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
> +       else
> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
> +       if (ret)
> +               DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
> +
> +       return ret;
> +}
> +
> +static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
> +{
> +       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_3;
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +       default:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +       }
> +}
> +
> +static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
> +                                   uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +       int i;
> +       uint8_t v = 0, p = 0;
> +       uint8_t preemph_max;
> +
> +       for (i = 0; i < dp->link.num_lanes; i++) {
> +               v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
> +               p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
> +                                                                 i));
> +       }
> +
> +       if (v >= VOLTAGE_LEVEL_2)
> +               v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
> +
> +       preemph_max = cdn_dp_pre_emphasis_max(v);
> +       if (p >= preemph_max)
> +               p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +       for (i = 0; i < dp->link.num_lanes; i++)
> +               dp->train_set[i] = v | p;
> +}
> +
> +/*
> + * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
> + * or 1.2 devices that support it, Training Pattern 2 otherwise.
> + */
> +static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
> +{
> +       u32 training_pattern = DP_TRAINING_PATTERN_2;
> +
> +       /*
> +        * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
> +        * for downstream devices that support HBR2. However, not all sinks
> +        * follow the spec.
> +        */
> +       if (drm_dp_tps3_supported(dp->dpcd))
> +               training_pattern = DP_TRAINING_PATTERN_3;
> +       else
> +               DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
> +
> +       return training_pattern;
> +}
> +
> +
> +static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
> +{
> +       int lane;
> +
> +       for (lane = 0; lane < dp->link.num_lanes; lane++)
> +               if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
> +                       return false;
> +
> +       return true;
> +}
> +
> +static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
> +{
> +       int ret;
> +
> +       cdn_dp_set_signal_levels(dp);
> +
> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
> +                               dp->train_set, dp->link.num_lanes);
> +       if (ret != dp->link.num_lanes)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
> +                                 uint8_t dp_train_pat)
> +{
> +       uint8_t buf[sizeof(dp->train_set) + 1];
> +       int ret, len;
> +
> +       buf[0] = dp_train_pat;
> +       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
> +           DP_TRAINING_PATTERN_DISABLE) {
> +               /* don't write DP_TRAINING_LANEx_SET on disable */
> +               len = 1;
> +       } else {
> +               /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
> +               memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
> +               len = dp->link.num_lanes + 1;
> +       }
> +
> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
> +                               buf, len);
> +       if (ret != len)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
> +                                   uint8_t dp_train_pat)
> +{
> +       int ret;
> +
> +       memset(dp->train_set, 0, sizeof(dp->train_set));
> +
> +       cdn_dp_set_signal_levels(dp);
> +
> +       ret = cdn_dp_set_pattern(dp, dp_train_pat);
> +       if (ret)
> +               return ret;
> +
> +       return cdn_dp_set_link_train(dp, dp_train_pat);
> +}
> +
> +/* Enable corresponding port and start training pattern 1 */
> +static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
> +{
> +       u8 voltage, link_config[2];
> +       u8 link_status[DP_LINK_STATUS_SIZE];
> +       u32 voltage_tries, max_vswing_tries;
> +       u32 rate, sink_max, source_max;
> +       int ret;
> +
> +       source_max = dp->lanes;
> +       sink_max = drm_dp_max_lane_count(dp->dpcd);
> +       dp->link.num_lanes = min(source_max, sink_max);
> +
> +       source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
> +       sink_max = drm_dp_max_link_rate(dp->dpcd);
> +       rate = min(source_max, sink_max);
> +       dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
> +
> +       /* Write the link configuration data */
> +       link_config[0] = dp->link.rate;
> +       link_config[1] = dp->link.num_lanes;
> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
> +               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
> +       drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
> +
> +       link_config[0] = 0;
> +       link_config[1] = 0;
> +       if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
> +               link_config[1] = DP_SET_ANSI_8B10B;
> +
> +       drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
> +
> +       /* clock recovery */
> +       ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
> +                                         DP_LINK_SCRAMBLING_DISABLE);
> +       if (ret) {
> +               DRM_ERROR("failed to start link train\n");
> +               return ret;
> +       }
> +
> +       voltage_tries = 1;
> +       max_vswing_tries = 0;
> +       for (;;) {
> +               drm_dp_link_train_clock_recovery_delay(dp->dpcd);
> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +                   DP_LINK_STATUS_SIZE) {
> +                       DRM_ERROR("failed to get link status\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("clock recovery OK\n");
> +                       return 0;
> +               }
> +
> +               if (voltage_tries >= 5) {
> +                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (max_vswing_tries >= 1) {
> +                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
> +                       return -EINVAL;
> +               }
> +
> +               voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
> +
> +               /* Update training set as requested by target */
> +               cdn_dp_get_adjust_train(dp, link_status);
> +               if (cdn_dp_update_link_train(dp)) {
> +                       DRM_ERROR("failed to update link training\n");
> +                       return -EINVAL;
> +               }
> +
> +               if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
> +                   voltage)
> +                       ++voltage_tries;
> +               else
> +                       voltage_tries = 1;
> +
> +               if (cdn_dp_link_max_vswing_reached(dp))
> +                       ++max_vswing_tries;
> +       }
> +}
> +
> +static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
> +{
> +       int tries, ret;
> +       u32 training_pattern;
> +       uint8_t link_status[DP_LINK_STATUS_SIZE];
> +
> +       training_pattern = cdn_dp_select_chaneq_pattern(dp);
> +       training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
> +
> +       ret = cdn_dp_set_pattern(dp, training_pattern);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_set_link_train(dp, training_pattern);
> +       if (ret) {
> +               DRM_ERROR("failed to start channel equalization\n");
> +               return ret;
> +       }
> +
> +       for (tries = 0; tries < 5; tries++) {
> +               drm_dp_link_train_channel_eq_delay(dp->dpcd);
> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +                   DP_LINK_STATUS_SIZE) {
> +                       DRM_ERROR("failed to get link status\n");
> +                       break;
> +               }
> +
> +               /* Make sure clock is still ok */
> +               if (!drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("Clock recovery check failed, cannot "
> +                                     "continue channel equalization\n");
> +                       break;
> +               }
> +
> +               if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("Channel EQ done. DP Training "
> +                                     "successful\n");
> +                       return 0;
> +               }
> +
> +               /* Update training set as requested by target */
> +               cdn_dp_get_adjust_train(dp, link_status);
> +               if (cdn_dp_update_link_train(dp)) {
> +                       DRM_ERROR("failed to update link training\n");
> +                       break;
> +               }
> +       }
> +
> +       /* Try 5 times, else fail and try at lower BW */
> +       if (tries == 5)
> +               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
> +
> +       return -EINVAL;
> +
> +}
> +
> +static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
> +{
> +       int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
> +
> +       if (ret)
> +               return ret;
> +
> +       return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
> +}
> +
> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
> +{
> +       int ret, stop_err;
> +
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
> +                              sizeof(dp->dpcd));
> +       if (ret < 0) {
> +               DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_link_training_clock_recovery(dp);
> +       if (ret) {
> +               DRM_ERROR("training clock recovery fail, error: %d\n", ret);
> +               goto out;
> +       }
> +
> +       ret = cdn_dp_link_training_channel_equalization(dp);
> +       if (ret) {
> +               DRM_ERROR("training channel equalization fail, error: %d\n",
> +                         ret);
> +               goto out;
> +       }
> +out:
> +       stop_err = cdn_dp_stop_link_train(dp);
> +       if (stop_err) {
> +               DRM_ERROR("stop training fail, error: %d\n", stop_err);
> +               return stop_err;
> +       }
> +
> +       return ret;
> +}
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> index b2f532a..72780f1 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -17,7 +17,9 @@
>  #include <linux/delay.h>
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
> +#include <linux/phy/phy.h>
>  #include <linux/reset.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
>
>  #include "cdn-dp-core.h"
>  #include "cdn-dp-reg.h"
> @@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
>         return 0;
>  }
>
> -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>  {
>         u8 msg[6];
>
> @@ -605,22 +607,41 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
>  int cdn_dp_train_link(struct cdn_dp_device *dp)
>  {
>         int ret;
> +       struct cdn_dp_port *port = dp->port[dp->active_port];
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>
> +       /*
> +        * DP firmware uses fixed phy config values to do training, but some
> +        * boards need to adjust these values to fit for their unique hardware
> +        * design. So if the phy is using custom config values, do software
> +        * link training instead of relying on firmware, if software training
> +        * fail, keep firmware training as a fallback if sw training fails.
> +        */
> +       if (tcphy->need_software_training) {
> +               ret = cdn_dp_software_train_link(dp);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev,
> +                               "Failed to do software training %d\n", ret);
> +                       goto do_fw_training;
> +               }
> +               cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);

Check for an error here and act accordingly? Or doesn't matter?

> +               dp->sw_training_success = true;
> +               return 0;
> +       }
> +

nit: Personally I don't like this goto. Maybe you can refactor this.

       if (tcphy->need_software_training) {
               ret = cdn_dp_software_train_link(dp);
               if (!ret) {
                       cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
                       dp->sw_training_success = true;
                       return 0;
               }
               DRM_DEV_ERROR(dp->dev, "Failed to do software training
%d\n", ret);
       }

> +do_fw_training:

Then remove the goto label.

> +       dp->sw_training_success = false;
>         ret = cdn_dp_training_start(dp);
>         if (ret) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
>                 return ret;
>         }
> -

Do not remove the new line.

>         ret = cdn_dp_get_training_status(dp);
>         if (ret) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
>                 return ret;
>         }
> -
> -       DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
> -                         dp->link.num_lanes);

Why you remove this debug message?

> -       return ret;
> +       return 0;
>  }
>
>  int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> index aedf2dc..b60a6b2 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -137,7 +137,7 @@
>  #define HPD_EVENT_MASK                 0x211c
>  #define HPD_EVENT_DET                  0x2120
>
> -/* dpyx framer addr */
> +/* dptx framer addr */
>  #define DP_FRAMER_GLOBAL_CONFIG                0x2200
>  #define DP_SW_RESET                    0x2204
>  #define DP_FRAMER_TU                   0x2208
> @@ -431,6 +431,40 @@
>  /* Reference cycles when using lane clock as reference */
>  #define LANE_REF_CYC                           0x8000
>
> +/* register CM_VID_CTRL */
> +#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
> +#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
> +
> +/* register DP_TX_PHY_CONFIG_REG */
> +#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
> +#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
> +#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
> +#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
> +#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
> +#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
> +#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
> +#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
> +#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
> +#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
> +#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
> +#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
> +
> +/* register DP_FRAMER_GLOBAL_CONFIG */
> +#define NUM_LANES(x)           ((x) & 3)
> +#define SST_MODE               (0 << 2)
> +#define GLOBAL_EN              (1 << 3)
> +#define RG_EN                  (0 << 4)
> +#define NO_VIDEO               (1 << 5)
> +#define ENC_RST_DIS            (1 << 6)
> +#define WR_VHSYNC_FALL         (1 << 7)
> +

Use the BIT() macros for these.

>  enum voltage_swing_level {
>         VOLTAGE_LEVEL_0,
>         VOLTAGE_LEVEL_1,
> @@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>  int cdn_dp_event_config(struct cdn_dp_device *dp);
>  u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>  int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
>  ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>                           u8 *data, u16 len);
>  ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
> @@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
>  int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
>  int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>  int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
> +int cdn_dp_software_train_link(struct cdn_dp_device *dp);
>  #endif /* _CDN_DP_REG_H */
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 4/4] drm/rockchip: support dp training outside dp firmware
@ 2018-05-07 11:29     ` Enric Balletbo Serra
  0 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 11:29 UTC (permalink / raw)
  To: Lin Huang
  Cc: David Airlie, Brian Norris, Doug Anderson, linux-kernel,
	open list:ARM/Rockchip SoC...,
	dri-devel, Chris Zhong, daniel.vetter, Linux ARM

Hi Lin,

Thanks for the patch.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> DP firware use fix phy config value to do training, but some

s/fiware/firmware/

> board need to adjust these value to fit for their hardware design,
> so we use new phy config to do training outside firmware to meet
> this situation, if there have new phy config pass from dts, it will
> use training outside firmware.
>

maybe you can rewrite all this in a better way.

ooi, which boards needs this?


> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/gpu/drm/rockchip/Makefile               |   3 +-
>  drivers/gpu/drm/rockchip/cdn-dp-core.c          |  23 +-
>  drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
>  drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 ++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  33 +-
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
>  6 files changed, 480 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index a314e21..b932f62 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>
>  rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
> +                                       cdn-dp-link-training.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> index 268c190..a2a4208 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>                         goto out;
>                 }
>         }
> -
> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> -       if (ret) {
> -               DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
> -               goto out;
> +       if (dp->sw_training_success == false) {
> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev,
> +                                     "Failed to idle video %d\n", ret);
> +                       goto out;
> +               }
>         }
>
>         ret = cdn_dp_config_video(dp);
> @@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>                 goto out;
>         }
>
> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
> -       if (ret) {
> -               DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
> -               goto out;
> +       if (dp->sw_training_success == false) {
> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
> +                       goto out;
> +               }
>         }
> +
>  out:
>         mutex_unlock(&dp->lock);
>  }
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> index 46159b2..c6050ab 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -84,6 +84,7 @@ struct cdn_dp_device {
>         bool connected;
>         bool active;
>         bool suspended;
> +       bool sw_training_success;
>
>         const struct firmware *fw;      /* cdn dp firmware */
>         unsigned int fw_version;        /* cdn fw version */
> @@ -106,6 +107,7 @@ struct cdn_dp_device {
>         u8 ports;
>         u8 lanes;
>         int active_port;
> +       u8 train_set[4];
>
>         u8 dpcd[DP_RECEIVER_CAP_SIZE];
>         bool sink_has_audio;
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
> new file mode 100644
> index 0000000..558c945
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
> @@ -0,0 +1,398 @@
> +/* SPDX-License-Identifier: GPL-2.0 */

For a C source file the format is:
(https://www.kernel.org/doc/html/latest/process/license-rules.html)

// SPDX-License-Identifier: <SPDX License Expression>

> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw@rock-chips.com>
> + */
> +
> +#include <linux/arm-smccc.h>

Why you need this include?

> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/phy/phy.h>
> +#include <linux/reset.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
> +

In fact, I think that there are other includes that can be removed,
please review.

> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +
> +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
> +{
> +       struct cdn_dp_port *port = dp->port[dp->active_port];
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
> +
> +       int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
> +       u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
> +                  DP_TRAIN_VOLTAGE_SWING_SHIFT;
> +       u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
> +                         >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +       tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
> +                               swing, pre_emphasis);
> +}
> +
> +static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
> +{
> +       u32 phy_config, global_config;
> +       int ret;
> +       uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
> +
> +       global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
> +                       GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
> +
> +       phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
> +                    DP_TX_PHY_SKEW_BYPASS(0) |
> +                    DP_TX_PHY_DISPARITY_RST(0) |
> +                    DP_TX_PHY_LANE0_SKEW(0) |
> +                    DP_TX_PHY_LANE1_SKEW(1) |
> +                    DP_TX_PHY_LANE2_SKEW(2) |
> +                    DP_TX_PHY_LANE3_SKEW(3) |
> +                    DP_TX_PHY_10BIT_ENABLE(0);
> +
> +       if (pattern != DP_TRAINING_PATTERN_DISABLE) {
> +               global_config |= NO_VIDEO;
> +               phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
> +                             DP_TX_PHY_SCRAMBLER_BYPASS(1) |
> +                             DP_TX_PHY_TRAINING_PATTERN(pattern);
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
> +       if (ret) {
> +               DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
> +                         ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
> +       if (ret) {
> +               DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
> +                         ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
> +       if (ret) {
> +               DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
> +               return ret;
> +       }
> +
> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
> +       else
> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
> +       if (ret)
> +               DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
> +
> +       return ret;
> +}
> +
> +static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
> +{
> +       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_3;
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +       default:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +       }
> +}
> +
> +static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
> +                                   uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +       int i;
> +       uint8_t v = 0, p = 0;
> +       uint8_t preemph_max;
> +
> +       for (i = 0; i < dp->link.num_lanes; i++) {
> +               v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
> +               p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
> +                                                                 i));
> +       }
> +
> +       if (v >= VOLTAGE_LEVEL_2)
> +               v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
> +
> +       preemph_max = cdn_dp_pre_emphasis_max(v);
> +       if (p >= preemph_max)
> +               p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +       for (i = 0; i < dp->link.num_lanes; i++)
> +               dp->train_set[i] = v | p;
> +}
> +
> +/*
> + * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
> + * or 1.2 devices that support it, Training Pattern 2 otherwise.
> + */
> +static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
> +{
> +       u32 training_pattern = DP_TRAINING_PATTERN_2;
> +
> +       /*
> +        * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
> +        * for downstream devices that support HBR2. However, not all sinks
> +        * follow the spec.
> +        */
> +       if (drm_dp_tps3_supported(dp->dpcd))
> +               training_pattern = DP_TRAINING_PATTERN_3;
> +       else
> +               DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
> +
> +       return training_pattern;
> +}
> +
> +
> +static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
> +{
> +       int lane;
> +
> +       for (lane = 0; lane < dp->link.num_lanes; lane++)
> +               if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
> +                       return false;
> +
> +       return true;
> +}
> +
> +static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
> +{
> +       int ret;
> +
> +       cdn_dp_set_signal_levels(dp);
> +
> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
> +                               dp->train_set, dp->link.num_lanes);
> +       if (ret != dp->link.num_lanes)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
> +                                 uint8_t dp_train_pat)
> +{
> +       uint8_t buf[sizeof(dp->train_set) + 1];
> +       int ret, len;
> +
> +       buf[0] = dp_train_pat;
> +       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
> +           DP_TRAINING_PATTERN_DISABLE) {
> +               /* don't write DP_TRAINING_LANEx_SET on disable */
> +               len = 1;
> +       } else {
> +               /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
> +               memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
> +               len = dp->link.num_lanes + 1;
> +       }
> +
> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
> +                               buf, len);
> +       if (ret != len)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
> +                                   uint8_t dp_train_pat)
> +{
> +       int ret;
> +
> +       memset(dp->train_set, 0, sizeof(dp->train_set));
> +
> +       cdn_dp_set_signal_levels(dp);
> +
> +       ret = cdn_dp_set_pattern(dp, dp_train_pat);
> +       if (ret)
> +               return ret;
> +
> +       return cdn_dp_set_link_train(dp, dp_train_pat);
> +}
> +
> +/* Enable corresponding port and start training pattern 1 */
> +static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
> +{
> +       u8 voltage, link_config[2];
> +       u8 link_status[DP_LINK_STATUS_SIZE];
> +       u32 voltage_tries, max_vswing_tries;
> +       u32 rate, sink_max, source_max;
> +       int ret;
> +
> +       source_max = dp->lanes;
> +       sink_max = drm_dp_max_lane_count(dp->dpcd);
> +       dp->link.num_lanes = min(source_max, sink_max);
> +
> +       source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
> +       sink_max = drm_dp_max_link_rate(dp->dpcd);
> +       rate = min(source_max, sink_max);
> +       dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
> +
> +       /* Write the link configuration data */
> +       link_config[0] = dp->link.rate;
> +       link_config[1] = dp->link.num_lanes;
> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
> +               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
> +       drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
> +
> +       link_config[0] = 0;
> +       link_config[1] = 0;
> +       if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
> +               link_config[1] = DP_SET_ANSI_8B10B;
> +
> +       drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
> +
> +       /* clock recovery */
> +       ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
> +                                         DP_LINK_SCRAMBLING_DISABLE);
> +       if (ret) {
> +               DRM_ERROR("failed to start link train\n");
> +               return ret;
> +       }
> +
> +       voltage_tries = 1;
> +       max_vswing_tries = 0;
> +       for (;;) {
> +               drm_dp_link_train_clock_recovery_delay(dp->dpcd);
> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +                   DP_LINK_STATUS_SIZE) {
> +                       DRM_ERROR("failed to get link status\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("clock recovery OK\n");
> +                       return 0;
> +               }
> +
> +               if (voltage_tries >= 5) {
> +                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (max_vswing_tries >= 1) {
> +                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
> +                       return -EINVAL;
> +               }
> +
> +               voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
> +
> +               /* Update training set as requested by target */
> +               cdn_dp_get_adjust_train(dp, link_status);
> +               if (cdn_dp_update_link_train(dp)) {
> +                       DRM_ERROR("failed to update link training\n");
> +                       return -EINVAL;
> +               }
> +
> +               if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
> +                   voltage)
> +                       ++voltage_tries;
> +               else
> +                       voltage_tries = 1;
> +
> +               if (cdn_dp_link_max_vswing_reached(dp))
> +                       ++max_vswing_tries;
> +       }
> +}
> +
> +static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
> +{
> +       int tries, ret;
> +       u32 training_pattern;
> +       uint8_t link_status[DP_LINK_STATUS_SIZE];
> +
> +       training_pattern = cdn_dp_select_chaneq_pattern(dp);
> +       training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
> +
> +       ret = cdn_dp_set_pattern(dp, training_pattern);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_set_link_train(dp, training_pattern);
> +       if (ret) {
> +               DRM_ERROR("failed to start channel equalization\n");
> +               return ret;
> +       }
> +
> +       for (tries = 0; tries < 5; tries++) {
> +               drm_dp_link_train_channel_eq_delay(dp->dpcd);
> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +                   DP_LINK_STATUS_SIZE) {
> +                       DRM_ERROR("failed to get link status\n");
> +                       break;
> +               }
> +
> +               /* Make sure clock is still ok */
> +               if (!drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("Clock recovery check failed, cannot "
> +                                     "continue channel equalization\n");
> +                       break;
> +               }
> +
> +               if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("Channel EQ done. DP Training "
> +                                     "successful\n");
> +                       return 0;
> +               }
> +
> +               /* Update training set as requested by target */
> +               cdn_dp_get_adjust_train(dp, link_status);
> +               if (cdn_dp_update_link_train(dp)) {
> +                       DRM_ERROR("failed to update link training\n");
> +                       break;
> +               }
> +       }
> +
> +       /* Try 5 times, else fail and try at lower BW */
> +       if (tries == 5)
> +               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
> +
> +       return -EINVAL;
> +
> +}
> +
> +static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
> +{
> +       int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
> +
> +       if (ret)
> +               return ret;
> +
> +       return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
> +}
> +
> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
> +{
> +       int ret, stop_err;
> +
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
> +                              sizeof(dp->dpcd));
> +       if (ret < 0) {
> +               DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_link_training_clock_recovery(dp);
> +       if (ret) {
> +               DRM_ERROR("training clock recovery fail, error: %d\n", ret);
> +               goto out;
> +       }
> +
> +       ret = cdn_dp_link_training_channel_equalization(dp);
> +       if (ret) {
> +               DRM_ERROR("training channel equalization fail, error: %d\n",
> +                         ret);
> +               goto out;
> +       }
> +out:
> +       stop_err = cdn_dp_stop_link_train(dp);
> +       if (stop_err) {
> +               DRM_ERROR("stop training fail, error: %d\n", stop_err);
> +               return stop_err;
> +       }
> +
> +       return ret;
> +}
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> index b2f532a..72780f1 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -17,7 +17,9 @@
>  #include <linux/delay.h>
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
> +#include <linux/phy/phy.h>
>  #include <linux/reset.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
>
>  #include "cdn-dp-core.h"
>  #include "cdn-dp-reg.h"
> @@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
>         return 0;
>  }
>
> -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>  {
>         u8 msg[6];
>
> @@ -605,22 +607,41 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
>  int cdn_dp_train_link(struct cdn_dp_device *dp)
>  {
>         int ret;
> +       struct cdn_dp_port *port = dp->port[dp->active_port];
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>
> +       /*
> +        * DP firmware uses fixed phy config values to do training, but some
> +        * boards need to adjust these values to fit for their unique hardware
> +        * design. So if the phy is using custom config values, do software
> +        * link training instead of relying on firmware, if software training
> +        * fail, keep firmware training as a fallback if sw training fails.
> +        */
> +       if (tcphy->need_software_training) {
> +               ret = cdn_dp_software_train_link(dp);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev,
> +                               "Failed to do software training %d\n", ret);
> +                       goto do_fw_training;
> +               }
> +               cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);

Check for an error here and act accordingly? Or doesn't matter?

> +               dp->sw_training_success = true;
> +               return 0;
> +       }
> +

nit: Personally I don't like this goto. Maybe you can refactor this.

       if (tcphy->need_software_training) {
               ret = cdn_dp_software_train_link(dp);
               if (!ret) {
                       cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
                       dp->sw_training_success = true;
                       return 0;
               }
               DRM_DEV_ERROR(dp->dev, "Failed to do software training
%d\n", ret);
       }

> +do_fw_training:

Then remove the goto label.

> +       dp->sw_training_success = false;
>         ret = cdn_dp_training_start(dp);
>         if (ret) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
>                 return ret;
>         }
> -

Do not remove the new line.

>         ret = cdn_dp_get_training_status(dp);
>         if (ret) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
>                 return ret;
>         }
> -
> -       DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
> -                         dp->link.num_lanes);

Why you remove this debug message?

> -       return ret;
> +       return 0;
>  }
>
>  int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> index aedf2dc..b60a6b2 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -137,7 +137,7 @@
>  #define HPD_EVENT_MASK                 0x211c
>  #define HPD_EVENT_DET                  0x2120
>
> -/* dpyx framer addr */
> +/* dptx framer addr */
>  #define DP_FRAMER_GLOBAL_CONFIG                0x2200
>  #define DP_SW_RESET                    0x2204
>  #define DP_FRAMER_TU                   0x2208
> @@ -431,6 +431,40 @@
>  /* Reference cycles when using lane clock as reference */
>  #define LANE_REF_CYC                           0x8000
>
> +/* register CM_VID_CTRL */
> +#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
> +#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
> +
> +/* register DP_TX_PHY_CONFIG_REG */
> +#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
> +#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
> +#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
> +#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
> +#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
> +#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
> +#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
> +#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
> +#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
> +#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
> +#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
> +#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
> +
> +/* register DP_FRAMER_GLOBAL_CONFIG */
> +#define NUM_LANES(x)           ((x) & 3)
> +#define SST_MODE               (0 << 2)
> +#define GLOBAL_EN              (1 << 3)
> +#define RG_EN                  (0 << 4)
> +#define NO_VIDEO               (1 << 5)
> +#define ENC_RST_DIS            (1 << 6)
> +#define WR_VHSYNC_FALL         (1 << 7)
> +

Use the BIT() macros for these.

>  enum voltage_swing_level {
>         VOLTAGE_LEVEL_0,
>         VOLTAGE_LEVEL_1,
> @@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>  int cdn_dp_event_config(struct cdn_dp_device *dp);
>  u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>  int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
>  ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>                           u8 *data, u16 len);
>  ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
> @@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
>  int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
>  int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>  int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
> +int cdn_dp_software_train_link(struct cdn_dp_device *dp);
>  #endif /* _CDN_DP_REG_H */
> --
> 2.7.4
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 4/4] drm/rockchip: support dp training outside dp firmware
@ 2018-05-07 11:29     ` Enric Balletbo Serra
  0 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 11:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lin,

Thanks for the patch.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> DP firware use fix phy config value to do training, but some

s/fiware/firmware/

> board need to adjust these value to fit for their hardware design,
> so we use new phy config to do training outside firmware to meet
> this situation, if there have new phy config pass from dts, it will
> use training outside firmware.
>

maybe you can rewrite all this in a better way.

ooi, which boards needs this?


> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/gpu/drm/rockchip/Makefile               |   3 +-
>  drivers/gpu/drm/rockchip/cdn-dp-core.c          |  23 +-
>  drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
>  drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 ++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  33 +-
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
>  6 files changed, 480 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index a314e21..b932f62 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>
>  rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
> +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
> +                                       cdn-dp-link-training.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> index 268c190..a2a4208 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>                         goto out;
>                 }
>         }
> -
> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> -       if (ret) {
> -               DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
> -               goto out;
> +       if (dp->sw_training_success == false) {
> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev,
> +                                     "Failed to idle video %d\n", ret);
> +                       goto out;
> +               }
>         }
>
>         ret = cdn_dp_config_video(dp);
> @@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>                 goto out;
>         }
>
> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
> -       if (ret) {
> -               DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
> -               goto out;
> +       if (dp->sw_training_success == false) {
> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
> +                       goto out;
> +               }
>         }
> +
>  out:
>         mutex_unlock(&dp->lock);
>  }
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> index 46159b2..c6050ab 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -84,6 +84,7 @@ struct cdn_dp_device {
>         bool connected;
>         bool active;
>         bool suspended;
> +       bool sw_training_success;
>
>         const struct firmware *fw;      /* cdn dp firmware */
>         unsigned int fw_version;        /* cdn fw version */
> @@ -106,6 +107,7 @@ struct cdn_dp_device {
>         u8 ports;
>         u8 lanes;
>         int active_port;
> +       u8 train_set[4];
>
>         u8 dpcd[DP_RECEIVER_CAP_SIZE];
>         bool sink_has_audio;
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
> new file mode 100644
> index 0000000..558c945
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
> @@ -0,0 +1,398 @@
> +/* SPDX-License-Identifier: GPL-2.0 */

For a C source file the format is:
(https://www.kernel.org/doc/html/latest/process/license-rules.html)

// SPDX-License-Identifier: <SPDX License Expression>

> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw@rock-chips.com>
> + */
> +
> +#include <linux/arm-smccc.h>

Why you need this include?

> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/phy/phy.h>
> +#include <linux/reset.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
> +

In fact, I think that there are other includes that can be removed,
please review.

> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +
> +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
> +{
> +       struct cdn_dp_port *port = dp->port[dp->active_port];
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
> +
> +       int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
> +       u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
> +                  DP_TRAIN_VOLTAGE_SWING_SHIFT;
> +       u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
> +                         >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +       tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
> +                               swing, pre_emphasis);
> +}
> +
> +static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
> +{
> +       u32 phy_config, global_config;
> +       int ret;
> +       uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
> +
> +       global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
> +                       GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
> +
> +       phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
> +                    DP_TX_PHY_SKEW_BYPASS(0) |
> +                    DP_TX_PHY_DISPARITY_RST(0) |
> +                    DP_TX_PHY_LANE0_SKEW(0) |
> +                    DP_TX_PHY_LANE1_SKEW(1) |
> +                    DP_TX_PHY_LANE2_SKEW(2) |
> +                    DP_TX_PHY_LANE3_SKEW(3) |
> +                    DP_TX_PHY_10BIT_ENABLE(0);
> +
> +       if (pattern != DP_TRAINING_PATTERN_DISABLE) {
> +               global_config |= NO_VIDEO;
> +               phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
> +                             DP_TX_PHY_SCRAMBLER_BYPASS(1) |
> +                             DP_TX_PHY_TRAINING_PATTERN(pattern);
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
> +       if (ret) {
> +               DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
> +                         ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
> +       if (ret) {
> +               DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
> +                         ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
> +       if (ret) {
> +               DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
> +               return ret;
> +       }
> +
> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
> +       else
> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
> +       if (ret)
> +               DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
> +
> +       return ret;
> +}
> +
> +static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
> +{
> +       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_3;
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +       default:
> +               return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +       }
> +}
> +
> +static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
> +                                   uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +       int i;
> +       uint8_t v = 0, p = 0;
> +       uint8_t preemph_max;
> +
> +       for (i = 0; i < dp->link.num_lanes; i++) {
> +               v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
> +               p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
> +                                                                 i));
> +       }
> +
> +       if (v >= VOLTAGE_LEVEL_2)
> +               v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
> +
> +       preemph_max = cdn_dp_pre_emphasis_max(v);
> +       if (p >= preemph_max)
> +               p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +
> +       for (i = 0; i < dp->link.num_lanes; i++)
> +               dp->train_set[i] = v | p;
> +}
> +
> +/*
> + * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
> + * or 1.2 devices that support it, Training Pattern 2 otherwise.
> + */
> +static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
> +{
> +       u32 training_pattern = DP_TRAINING_PATTERN_2;
> +
> +       /*
> +        * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
> +        * for downstream devices that support HBR2. However, not all sinks
> +        * follow the spec.
> +        */
> +       if (drm_dp_tps3_supported(dp->dpcd))
> +               training_pattern = DP_TRAINING_PATTERN_3;
> +       else
> +               DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
> +
> +       return training_pattern;
> +}
> +
> +
> +static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
> +{
> +       int lane;
> +
> +       for (lane = 0; lane < dp->link.num_lanes; lane++)
> +               if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
> +                       return false;
> +
> +       return true;
> +}
> +
> +static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
> +{
> +       int ret;
> +
> +       cdn_dp_set_signal_levels(dp);
> +
> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
> +                               dp->train_set, dp->link.num_lanes);
> +       if (ret != dp->link.num_lanes)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
> +                                 uint8_t dp_train_pat)
> +{
> +       uint8_t buf[sizeof(dp->train_set) + 1];
> +       int ret, len;
> +
> +       buf[0] = dp_train_pat;
> +       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
> +           DP_TRAINING_PATTERN_DISABLE) {
> +               /* don't write DP_TRAINING_LANEx_SET on disable */
> +               len = 1;
> +       } else {
> +               /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
> +               memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
> +               len = dp->link.num_lanes + 1;
> +       }
> +
> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
> +                               buf, len);
> +       if (ret != len)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
> +                                   uint8_t dp_train_pat)
> +{
> +       int ret;
> +
> +       memset(dp->train_set, 0, sizeof(dp->train_set));
> +
> +       cdn_dp_set_signal_levels(dp);
> +
> +       ret = cdn_dp_set_pattern(dp, dp_train_pat);
> +       if (ret)
> +               return ret;
> +
> +       return cdn_dp_set_link_train(dp, dp_train_pat);
> +}
> +
> +/* Enable corresponding port and start training pattern 1 */
> +static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
> +{
> +       u8 voltage, link_config[2];
> +       u8 link_status[DP_LINK_STATUS_SIZE];
> +       u32 voltage_tries, max_vswing_tries;
> +       u32 rate, sink_max, source_max;
> +       int ret;
> +
> +       source_max = dp->lanes;
> +       sink_max = drm_dp_max_lane_count(dp->dpcd);
> +       dp->link.num_lanes = min(source_max, sink_max);
> +
> +       source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
> +       sink_max = drm_dp_max_link_rate(dp->dpcd);
> +       rate = min(source_max, sink_max);
> +       dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
> +
> +       /* Write the link configuration data */
> +       link_config[0] = dp->link.rate;
> +       link_config[1] = dp->link.num_lanes;
> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
> +               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
> +       drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
> +
> +       link_config[0] = 0;
> +       link_config[1] = 0;
> +       if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
> +               link_config[1] = DP_SET_ANSI_8B10B;
> +
> +       drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
> +
> +       /* clock recovery */
> +       ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
> +                                         DP_LINK_SCRAMBLING_DISABLE);
> +       if (ret) {
> +               DRM_ERROR("failed to start link train\n");
> +               return ret;
> +       }
> +
> +       voltage_tries = 1;
> +       max_vswing_tries = 0;
> +       for (;;) {
> +               drm_dp_link_train_clock_recovery_delay(dp->dpcd);
> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +                   DP_LINK_STATUS_SIZE) {
> +                       DRM_ERROR("failed to get link status\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("clock recovery OK\n");
> +                       return 0;
> +               }
> +
> +               if (voltage_tries >= 5) {
> +                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (max_vswing_tries >= 1) {
> +                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
> +                       return -EINVAL;
> +               }
> +
> +               voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
> +
> +               /* Update training set as requested by target */
> +               cdn_dp_get_adjust_train(dp, link_status);
> +               if (cdn_dp_update_link_train(dp)) {
> +                       DRM_ERROR("failed to update link training\n");
> +                       return -EINVAL;
> +               }
> +
> +               if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
> +                   voltage)
> +                       ++voltage_tries;
> +               else
> +                       voltage_tries = 1;
> +
> +               if (cdn_dp_link_max_vswing_reached(dp))
> +                       ++max_vswing_tries;
> +       }
> +}
> +
> +static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
> +{
> +       int tries, ret;
> +       u32 training_pattern;
> +       uint8_t link_status[DP_LINK_STATUS_SIZE];
> +
> +       training_pattern = cdn_dp_select_chaneq_pattern(dp);
> +       training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
> +
> +       ret = cdn_dp_set_pattern(dp, training_pattern);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_set_link_train(dp, training_pattern);
> +       if (ret) {
> +               DRM_ERROR("failed to start channel equalization\n");
> +               return ret;
> +       }
> +
> +       for (tries = 0; tries < 5; tries++) {
> +               drm_dp_link_train_channel_eq_delay(dp->dpcd);
> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
> +                   DP_LINK_STATUS_SIZE) {
> +                       DRM_ERROR("failed to get link status\n");
> +                       break;
> +               }
> +
> +               /* Make sure clock is still ok */
> +               if (!drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("Clock recovery check failed, cannot "
> +                                     "continue channel equalization\n");
> +                       break;
> +               }
> +
> +               if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
> +                       DRM_DEBUG_KMS("Channel EQ done. DP Training "
> +                                     "successful\n");
> +                       return 0;
> +               }
> +
> +               /* Update training set as requested by target */
> +               cdn_dp_get_adjust_train(dp, link_status);
> +               if (cdn_dp_update_link_train(dp)) {
> +                       DRM_ERROR("failed to update link training\n");
> +                       break;
> +               }
> +       }
> +
> +       /* Try 5 times, else fail and try at lower BW */
> +       if (tries == 5)
> +               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
> +
> +       return -EINVAL;
> +
> +}
> +
> +static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
> +{
> +       int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
> +
> +       if (ret)
> +               return ret;
> +
> +       return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
> +}
> +
> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
> +{
> +       int ret, stop_err;
> +
> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
> +                              sizeof(dp->dpcd));
> +       if (ret < 0) {
> +               DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = cdn_dp_link_training_clock_recovery(dp);
> +       if (ret) {
> +               DRM_ERROR("training clock recovery fail, error: %d\n", ret);
> +               goto out;
> +       }
> +
> +       ret = cdn_dp_link_training_channel_equalization(dp);
> +       if (ret) {
> +               DRM_ERROR("training channel equalization fail, error: %d\n",
> +                         ret);
> +               goto out;
> +       }
> +out:
> +       stop_err = cdn_dp_stop_link_train(dp);
> +       if (stop_err) {
> +               DRM_ERROR("stop training fail, error: %d\n", stop_err);
> +               return stop_err;
> +       }
> +
> +       return ret;
> +}
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> index b2f532a..72780f1 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -17,7 +17,9 @@
>  #include <linux/delay.h>
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
> +#include <linux/phy/phy.h>
>  #include <linux/reset.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
>
>  #include "cdn-dp-core.h"
>  #include "cdn-dp-reg.h"
> @@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
>         return 0;
>  }
>
> -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>  {
>         u8 msg[6];
>
> @@ -605,22 +607,41 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
>  int cdn_dp_train_link(struct cdn_dp_device *dp)
>  {
>         int ret;
> +       struct cdn_dp_port *port = dp->port[dp->active_port];
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>
> +       /*
> +        * DP firmware uses fixed phy config values to do training, but some
> +        * boards need to adjust these values to fit for their unique hardware
> +        * design. So if the phy is using custom config values, do software
> +        * link training instead of relying on firmware, if software training
> +        * fail, keep firmware training as a fallback if sw training fails.
> +        */
> +       if (tcphy->need_software_training) {
> +               ret = cdn_dp_software_train_link(dp);
> +               if (ret) {
> +                       DRM_DEV_ERROR(dp->dev,
> +                               "Failed to do software training %d\n", ret);
> +                       goto do_fw_training;
> +               }
> +               cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);

Check for an error here and act accordingly? Or doesn't matter?

> +               dp->sw_training_success = true;
> +               return 0;
> +       }
> +

nit: Personally I don't like this goto. Maybe you can refactor this.

       if (tcphy->need_software_training) {
               ret = cdn_dp_software_train_link(dp);
               if (!ret) {
                       cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
                       dp->sw_training_success = true;
                       return 0;
               }
               DRM_DEV_ERROR(dp->dev, "Failed to do software training
%d\n", ret);
       }

> +do_fw_training:

Then remove the goto label.

> +       dp->sw_training_success = false;
>         ret = cdn_dp_training_start(dp);
>         if (ret) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
>                 return ret;
>         }
> -

Do not remove the new line.

>         ret = cdn_dp_get_training_status(dp);
>         if (ret) {
>                 DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
>                 return ret;
>         }
> -
> -       DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
> -                         dp->link.num_lanes);

Why you remove this debug message?

> -       return ret;
> +       return 0;
>  }
>
>  int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> index aedf2dc..b60a6b2 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -137,7 +137,7 @@
>  #define HPD_EVENT_MASK                 0x211c
>  #define HPD_EVENT_DET                  0x2120
>
> -/* dpyx framer addr */
> +/* dptx framer addr */
>  #define DP_FRAMER_GLOBAL_CONFIG                0x2200
>  #define DP_SW_RESET                    0x2204
>  #define DP_FRAMER_TU                   0x2208
> @@ -431,6 +431,40 @@
>  /* Reference cycles when using lane clock as reference */
>  #define LANE_REF_CYC                           0x8000
>
> +/* register CM_VID_CTRL */
> +#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
> +#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
> +
> +/* register DP_TX_PHY_CONFIG_REG */
> +#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
> +#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
> +#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
> +#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
> +#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
> +#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
> +#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
> +#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
> +#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
> +#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
> +#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
> +#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
> +#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
> +
> +/* register DP_FRAMER_GLOBAL_CONFIG */
> +#define NUM_LANES(x)           ((x) & 3)
> +#define SST_MODE               (0 << 2)
> +#define GLOBAL_EN              (1 << 3)
> +#define RG_EN                  (0 << 4)
> +#define NO_VIDEO               (1 << 5)
> +#define ENC_RST_DIS            (1 << 6)
> +#define WR_VHSYNC_FALL         (1 << 7)
> +

Use the BIT() macros for these.

>  enum voltage_swing_level {
>         VOLTAGE_LEVEL_0,
>         VOLTAGE_LEVEL_1,
> @@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>  int cdn_dp_event_config(struct cdn_dp_device *dp);
>  u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>  int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
>  ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>                           u8 *data, u16 len);
>  ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
> @@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
>  int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
>  int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>  int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
> +int cdn_dp_software_train_link(struct cdn_dp_device *dp);
>  #endif /* _CDN_DP_REG_H */
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/4] phy: rockchip-typec: support variable phy config value
  2018-05-04  8:08   ` Lin Huang
  (?)
@ 2018-05-07 13:59     ` Enric Balletbo Serra
  -1 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 13:59 UTC (permalink / raw)
  To: Lin Huang
  Cc: Sean Paul, David Airlie, Chris Zhong, Heiko Stübner,
	Brian Norris, Doug Anderson, jani.nikula, linux-kernel,
	open list:ARM/Rockchip SoC...,
	dri-devel, daniel.vetter, Linux ARM

Hi Lin,

Thanks for the patch, apart from the new build warnings introduced
some more comments below.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> the phy config values used to fix in dp firmware, but some boards
> need change these values to do training and get the better eye diagram
> result. So support that in phy driver.
>
> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++++++++++++++++++-----------
>  include/soc/rockchip/rockchip_phy_typec.h |  72 ++++++++
>  2 files changed, 259 insertions(+), 99 deletions(-)
>  create mode 100644 include/soc/rockchip/rockchip_phy_typec.h
>
> diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
> index 76a4b58..831a93b 100644
> --- a/drivers/phy/rockchip/phy-rockchip-typec.c
> +++ b/drivers/phy/rockchip/phy-rockchip-typec.c
> @@ -63,6 +63,7 @@
>
>  #include <linux/mfd/syscon.h>
>  #include <linux/phy/phy.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
>
>  #define CMN_SSM_BANDGAP                        (0x21 << 2)
>  #define CMN_SSM_BIAS                   (0x22 << 2)
> @@ -323,23 +324,31 @@
>   * clock 0: PLL 0 div 1
>   * clock 1: PLL 1 div 2
>   */
> -#define CLK_PLL_CONFIG                 0X30
> +#define CLK_PLL1_DIV1                  0x20
> +#define CLK_PLL1_DIV2                  0x30
>  #define CLK_PLL_MASK                   0x33
>
>  #define CMN_READY                      BIT(0)
>
> +#define DP_PLL_CLOCK_ENABLE_ACK                BIT(3)
>  #define DP_PLL_CLOCK_ENABLE            BIT(2)
> +#define DP_PLL_ENABLE_ACK              BIT(1)
>  #define DP_PLL_ENABLE                  BIT(0)
>  #define DP_PLL_DATA_RATE_RBR           ((2 << 12) | (4 << 8))
>  #define DP_PLL_DATA_RATE_HBR           ((2 << 12) | (4 << 8))
>  #define DP_PLL_DATA_RATE_HBR2          ((1 << 12) | (2 << 8))
> +#define DP_PLL_DATA_RATE_MASK          0xff00
>
> -#define DP_MODE_A0                     BIT(4)
> -#define DP_MODE_A2                     BIT(6)
> -#define DP_MODE_ENTER_A0               0xc101
> -#define DP_MODE_ENTER_A2               0xc104
> +#define DP_MODE_MASK                   0xf
> +#define DP_MODE_ENTER_A0               BIT(0)
> +#define DP_MODE_ENTER_A2               BIT(2)
> +#define DP_MODE_ENTER_A3               BIT(3)
> +#define DP_MODE_A0_ACK                 BIT(4)
> +#define DP_MODE_A2_ACK                 BIT(6)
> +#define DP_MODE_A3_ACK                 BIT(7)
> +#define DP_LINK_RESET_DEASSERTED       BIT(8)
>
> -#define PHY_MODE_SET_TIMEOUT           100000
> +#define PHY_MODE_SET_TIMEOUT           1000000
>

Why do you need to increase this timeout? Is because the software link
training timed out using the old value?


>  #define PIN_ASSIGN_C_E                 0x51d9
>  #define PIN_ASSIGN_D_F                 0x5100
> @@ -349,51 +358,7 @@
>  #define MODE_DFP_USB                   BIT(1)
>  #define MODE_DFP_DP                    BIT(2)
>
> -struct usb3phy_reg {
> -       u32 offset;
> -       u32 enable_bit;
> -       u32 write_enable;
> -};
> -
> -/**
> - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
> - * @reg: the base address for usb3-phy config.
> - * @typec_conn_dir: the register of type-c connector direction.
> - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
> - * @external_psm: the register of type-c phy external psm clock.
> - * @pipe_status: the register of type-c phy pipe status.
> - * @usb3_host_disable: the register of type-c usb3 host disable.
> - * @usb3_host_port: the register of type-c usb3 host port.
> - * @uphy_dp_sel: the register of type-c phy DP select control.
> - */
> -struct rockchip_usb3phy_port_cfg {
> -       unsigned int reg;
> -       struct usb3phy_reg typec_conn_dir;
> -       struct usb3phy_reg usb3tousb2_en;
> -       struct usb3phy_reg external_psm;
> -       struct usb3phy_reg pipe_status;
> -       struct usb3phy_reg usb3_host_disable;
> -       struct usb3phy_reg usb3_host_port;
> -       struct usb3phy_reg uphy_dp_sel;
> -};
> -
> -struct rockchip_typec_phy {
> -       struct device *dev;
> -       void __iomem *base;
> -       struct extcon_dev *extcon;
> -       struct regmap *grf_regs;
> -       struct clk *clk_core;
> -       struct clk *clk_ref;
> -       struct reset_control *uphy_rst;
> -       struct reset_control *pipe_rst;
> -       struct reset_control *tcphy_rst;
> -       const struct rockchip_usb3phy_port_cfg *port_cfgs;
> -       /* mutex to protect access to individual PHYs */
> -       struct mutex lock;
> -
> -       bool flip;
> -       u8 mode;
> -};
> +#define DEFAULT_RATE                   162000
>

DEFAULT_RATE seems a very common name for me, maybe add a prefix?


>  struct phy_reg {
>         u16 value;
> @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
>         { 0x8,          CMN_DIAG_PLL0_LF_PROG },
>  };
>
> -struct phy_reg dp_pll_cfg[] = {
> +struct phy_reg dp_pll_rbr_cfg[] = {
>         { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>         { 0x18,         CMN_PLL1_VCOCAL_ITER },
>         { 0x30b9,       CMN_PLL1_VCOCAL_START },
> -       { 0x21c,        CMN_PLL1_INTDIV },
> +       { 0x87,         CMN_PLL1_INTDIV },
>         { 0,            CMN_PLL1_FRACDIV },
> -       { 0x5,          CMN_PLL1_HIGH_THR },
> -       { 0x35,         CMN_PLL1_SS_CTRL1 },
> -       { 0x7f1e,       CMN_PLL1_SS_CTRL2 },
> +       { 0x22,         CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
>         { 0x20,         CMN_PLL1_DSM_DIAG },
>         { 0,            CMN_PLLSM1_USER_DEF_CTRL },
>         { 0,            CMN_DIAG_PLL1_OVRD },
> @@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
>         { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>         { 0x100,        CMN_DIAG_PLL1_PTATIS_TUNE1 },
>         { 0x7,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> -       { 0x4,          CMN_DIAG_PLL1_INCLK_CTRL },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
> +};
> +
> +struct phy_reg dp_pll_hbr_cfg[] = {
> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
> +       { 0xe1,         CMN_PLL1_INTDIV },
> +       { 0,            CMN_PLL1_FRACDIV },
> +       { 0x5,          CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
> +       { 0x20,         CMN_PLL1_DSM_DIAG },
> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
> +       { 0,            CMN_DIAG_PLL1_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>  };
>
> +struct phy_reg dp_pll_hbr2_cfg[] = {
> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
> +       { 0xe1,         CMN_PLL1_INTDIV },
> +       { 0,            CMN_PLL1_FRACDIV },
> +       { 0x5,          CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
> +       { 0x20,         CMN_PLL1_DSM_DIAG },
> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
> +       { 0,            CMN_DIAG_PLL1_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
> +};
>  static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
>         {
>                 .reg = 0xff7c0000,
> @@ -484,7 +492,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
>
>         rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
>         rdata &= ~CLK_PLL_MASK;
> -       rdata |= CLK_PLL_CONFIG;
> +       rdata |= CLK_PLL1_DIV2;
>         writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
>  }
>
> @@ -498,17 +506,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
>                        tcphy->base + usb3_pll_cfg[i].addr);
>  }
>
> -static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
> +static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
>  {
> -       u32 i;
> +       struct phy_reg *phy_cfg;
> +       u32 clk_ctrl;
> +       u32 i, cfg_size, hsclk_sel;
> +
> +       hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
> +       hsclk_sel &= ~CLK_PLL_MASK;
> +
> +       switch (link_rate) {
> +       case 162000:
> +               clk_ctrl = DP_PLL_DATA_RATE_RBR;
> +               hsclk_sel |= CLK_PLL1_DIV2;
> +               phy_cfg = dp_pll_rbr_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
> +               break;
> +       case 270000:
> +               clk_ctrl = DP_PLL_DATA_RATE_HBR;
> +               hsclk_sel |= CLK_PLL1_DIV2;
> +               phy_cfg = dp_pll_hbr_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
> +               break;
> +       case 540000:
> +               clk_ctrl = DP_PLL_DATA_RATE_HBR2;
> +               hsclk_sel |= CLK_PLL1_DIV1;
> +               phy_cfg = dp_pll_hbr2_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
> +               break;
> +       }
> +
> +       clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
> +       writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
>
> -       /* set the default mode to RBR */
> -       writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
> -              tcphy->base + DP_CLK_CTL);
> +       writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
>
>         /* load the configuration of PLL1 */
> -       for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
> -               writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
> +       for (i = 0; i < cfg_size; i++)
> +               writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
>  }
>
>  static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
> @@ -535,9 +570,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>         writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
>  }
>
> -static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
> +static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
> +                             u8 swing, u8 pre_emp, u32 lane)
>  {
> -       u16 rdata;
> +       u16 val;

>From what I see you are only renaming rdata to val, there is any reason?

>
>         writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
>         writel(0x6799, tcphy->base + TX_PSC_A0(lane));
> @@ -545,25 +581,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>         writel(0x98, tcphy->base + TX_PSC_A2(lane));
>         writel(0x98, tcphy->base + TX_PSC_A3(lane));
>
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
> -
> -       writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> -       writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
> -
> -       rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> -       rdata = (rdata & 0x8fff) | 0x6000;
> -       writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +       writel(tcphy->config[swing][pre_emp].swing,
> +              tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> +       writel(tcphy->config[swing][pre_emp].pe,
> +              tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
> +
> +       if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
> +               writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
> +               writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> +       } else {
> +               writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> +               writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
> +       }
> +
> +       val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +       val = val & 0x8fff;
> +       switch (link_rate) {
> +       case 162000:
> +       case 270000:
> +               val |= (6 << 12);
> +               break;
> +       case 540000:
> +               val |= (4 << 12);
> +               break;
> +       }
> +       writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>  }
>
>  static inline int property_enable(struct rockchip_typec_phy *tcphy,
> @@ -754,30 +796,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
>         tcphy_cfg_24m(tcphy);
>
>         if (mode == MODE_DFP_DP) {
> -               tcphy_cfg_dp_pll(tcphy);
> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>                 for (i = 0; i < 4; i++)
> -                       tcphy_dp_cfg_lane(tcphy, i);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, i);
>
>                 writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
>         } else {
>                 tcphy_cfg_usb3_pll(tcphy);
> -               tcphy_cfg_dp_pll(tcphy);
> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>                 if (tcphy->flip) {
>                         tcphy_tx_usb3_cfg_lane(tcphy, 3);
>                         tcphy_rx_usb3_cfg_lane(tcphy, 2);
> -                       tcphy_dp_cfg_lane(tcphy, 0);
> -                       tcphy_dp_cfg_lane(tcphy, 1);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 0);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 1);
>                 } else {
>                         tcphy_tx_usb3_cfg_lane(tcphy, 0);
>                         tcphy_rx_usb3_cfg_lane(tcphy, 1);
> -                       tcphy_dp_cfg_lane(tcphy, 2);
> -                       tcphy_dp_cfg_lane(tcphy, 3);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 2);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 3);
>                 }
>
>                 writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
>         }
>
> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         reset_control_deassert(tcphy->uphy_rst);
>
> @@ -990,7 +1035,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>         property_enable(tcphy, &cfg->uphy_dp_sel, 1);
>
>         ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> -                                val, val & DP_MODE_A2, 1000,
> +                                val, val & DP_MODE_A2_ACK, 1000,
>                                  PHY_MODE_SET_TIMEOUT);
>         if (ret < 0) {
>                 dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
> @@ -999,13 +1044,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>
>         tcphy_dp_aux_calibration(tcphy);
>
> -       writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
> +       /* enter A0 mode */
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A0;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> -                                val, val & DP_MODE_A0, 1000,
> +                                val, val & DP_MODE_A0_ACK, 1000,
>                                  PHY_MODE_SET_TIMEOUT);
>         if (ret < 0) {
> -               writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +               val &= ~DP_MODE_MASK;
> +               val |= DP_MODE_ENTER_A2;
> +               writel(val, tcphy->base + DP_MODE_CTL);
>                 dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
>                 goto power_on_finish;
>         }
> @@ -1023,6 +1074,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>  static int rockchip_dp_phy_power_off(struct phy *phy)
>  {
>         struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +       u32 val;
>
>         mutex_lock(&tcphy->lock);
>
> @@ -1031,7 +1083,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
>
>         tcphy->mode &= ~MODE_DFP_DP;
>
> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A2;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         if (tcphy->mode == MODE_DISCONNECT)
>                 tcphy_phy_deinit(tcphy);
> @@ -1047,6 +1102,30 @@ static const struct phy_ops rockchip_dp_phy_ops = {
>         .owner          = THIS_MODULE,
>  };
>
> +static int type_c_dp_phy_config(struct phy *phy, int link_rate,

s/type_c/typec/ to be coherent with the rest of the code.

> +                        int lanes, u8 swing, u8 pre_emp)
> +{
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +       u8 i;
> +
> +       tcphy_cfg_dp_pll(tcphy, link_rate);
> +
> +       if (tcphy->mode == MODE_DFP_DP) {
> +               for (i = 0; i < 4; i++)
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
> +       } else {
> +               if (tcphy->flip) {
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
> +               } else {
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
> +               }
> +       }
> +
> +       return 0;
> +}
> +
>  static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>                           struct device *dev)
>  {
> @@ -1087,6 +1166,14 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>                 return PTR_ERR(tcphy->tcphy_rst);
>         }
>
> +       /*
> +        * check if phy_config pass from dts, if yes,
> +        * need to use this phy config to do software training later
> +        */
> +       if (!of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
> +               (u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32)))
> +               tcphy->need_software_training = 1;
> +
>         return 0;
>  }
>
> @@ -1171,6 +1258,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
>                 }
>         }
>
> +       tcphy->typec_phy_config = type_c_dp_phy_config;

type_c_dp_phy_config -> typec_dp_phy_config

>         pm_runtime_enable(dev);
>
>         for_each_available_child_of_node(np, child_np) {
> diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
> new file mode 100644
> index 0000000..e25840e
> --- /dev/null
> +++ b/include/soc/rockchip/rockchip_phy_typec.h
> @@ -0,0 +1,72 @@
> +/*

Add the SPDX License identifier ...

> + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Lin Huang <hl@rock-chips.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.

... and remove the above notice.

> + */
> +#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
> +#define __SOC_ROCKCHIP_PHY_TYPEC_H
> +
> +

Remove the extra new line.

> +struct usb3phy_reg {
> +       u32 offset;
> +       u32 enable_bit;
> +       u32 write_enable;
> +};
> +
> +/**
> + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
> + * @reg: the base address for usb3-phy config.
> + * @typec_conn_dir: the register of type-c connector direction.
> + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
> + * @external_psm: the register of type-c phy external psm clock.
> + * @pipe_status: the register of type-c phy pipe status.
> + * @usb3_host_disable: the register of type-c usb3 host disable.
> + * @usb3_host_port: the register of type-c usb3 host port.
> + * @uphy_dp_sel: the register of type-c phy DP select control.
> + */
> +struct rockchip_usb3phy_port_cfg {
> +       unsigned int reg;
> +       struct usb3phy_reg typec_conn_dir;
> +       struct usb3phy_reg usb3tousb2_en;
> +       struct usb3phy_reg external_psm;
> +       struct usb3phy_reg pipe_status;
> +       struct usb3phy_reg usb3_host_disable;
> +       struct usb3phy_reg usb3_host_port;
> +       struct usb3phy_reg uphy_dp_sel;
> +};
> +
> +struct phy_config {
> +       int swing;
> +       int pe;
> +};
> +
> +struct rockchip_typec_phy {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct extcon_dev *extcon;
> +       struct regmap *grf_regs;
> +       struct clk *clk_core;
> +       struct clk *clk_ref;
> +       struct reset_control *uphy_rst;
> +       struct reset_control *pipe_rst;
> +       struct reset_control *tcphy_rst;
> +       struct rockchip_usb3phy_port_cfg *port_cfgs;
> +       /* mutex to protect access to individual PHYs */
> +       struct mutex lock;
> +       struct phy_config config[3][4];
> +       u8 need_software_training;
> +       bool flip;
> +       u8 mode;
> +       int (*typec_phy_config)(struct phy *phy, int link_rate,
> +                               int lanes, u8 swing, u8 pre_emp);
> +};
> +
> +#endif

Best regards,
 Enric

> --
> 2.7.4
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/4] phy: rockchip-typec: support variable phy config value
@ 2018-05-07 13:59     ` Enric Balletbo Serra
  0 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 13:59 UTC (permalink / raw)
  To: Lin Huang
  Cc: David Airlie, Brian Norris, Doug Anderson, linux-kernel,
	open list:ARM/Rockchip SoC...,
	dri-devel, Chris Zhong, daniel.vetter, Linux ARM

Hi Lin,

Thanks for the patch, apart from the new build warnings introduced
some more comments below.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> the phy config values used to fix in dp firmware, but some boards
> need change these values to do training and get the better eye diagram
> result. So support that in phy driver.
>
> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++++++++++++++++++-----------
>  include/soc/rockchip/rockchip_phy_typec.h |  72 ++++++++
>  2 files changed, 259 insertions(+), 99 deletions(-)
>  create mode 100644 include/soc/rockchip/rockchip_phy_typec.h
>
> diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
> index 76a4b58..831a93b 100644
> --- a/drivers/phy/rockchip/phy-rockchip-typec.c
> +++ b/drivers/phy/rockchip/phy-rockchip-typec.c
> @@ -63,6 +63,7 @@
>
>  #include <linux/mfd/syscon.h>
>  #include <linux/phy/phy.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
>
>  #define CMN_SSM_BANDGAP                        (0x21 << 2)
>  #define CMN_SSM_BIAS                   (0x22 << 2)
> @@ -323,23 +324,31 @@
>   * clock 0: PLL 0 div 1
>   * clock 1: PLL 1 div 2
>   */
> -#define CLK_PLL_CONFIG                 0X30
> +#define CLK_PLL1_DIV1                  0x20
> +#define CLK_PLL1_DIV2                  0x30
>  #define CLK_PLL_MASK                   0x33
>
>  #define CMN_READY                      BIT(0)
>
> +#define DP_PLL_CLOCK_ENABLE_ACK                BIT(3)
>  #define DP_PLL_CLOCK_ENABLE            BIT(2)
> +#define DP_PLL_ENABLE_ACK              BIT(1)
>  #define DP_PLL_ENABLE                  BIT(0)
>  #define DP_PLL_DATA_RATE_RBR           ((2 << 12) | (4 << 8))
>  #define DP_PLL_DATA_RATE_HBR           ((2 << 12) | (4 << 8))
>  #define DP_PLL_DATA_RATE_HBR2          ((1 << 12) | (2 << 8))
> +#define DP_PLL_DATA_RATE_MASK          0xff00
>
> -#define DP_MODE_A0                     BIT(4)
> -#define DP_MODE_A2                     BIT(6)
> -#define DP_MODE_ENTER_A0               0xc101
> -#define DP_MODE_ENTER_A2               0xc104
> +#define DP_MODE_MASK                   0xf
> +#define DP_MODE_ENTER_A0               BIT(0)
> +#define DP_MODE_ENTER_A2               BIT(2)
> +#define DP_MODE_ENTER_A3               BIT(3)
> +#define DP_MODE_A0_ACK                 BIT(4)
> +#define DP_MODE_A2_ACK                 BIT(6)
> +#define DP_MODE_A3_ACK                 BIT(7)
> +#define DP_LINK_RESET_DEASSERTED       BIT(8)
>
> -#define PHY_MODE_SET_TIMEOUT           100000
> +#define PHY_MODE_SET_TIMEOUT           1000000
>

Why do you need to increase this timeout? Is because the software link
training timed out using the old value?


>  #define PIN_ASSIGN_C_E                 0x51d9
>  #define PIN_ASSIGN_D_F                 0x5100
> @@ -349,51 +358,7 @@
>  #define MODE_DFP_USB                   BIT(1)
>  #define MODE_DFP_DP                    BIT(2)
>
> -struct usb3phy_reg {
> -       u32 offset;
> -       u32 enable_bit;
> -       u32 write_enable;
> -};
> -
> -/**
> - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
> - * @reg: the base address for usb3-phy config.
> - * @typec_conn_dir: the register of type-c connector direction.
> - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
> - * @external_psm: the register of type-c phy external psm clock.
> - * @pipe_status: the register of type-c phy pipe status.
> - * @usb3_host_disable: the register of type-c usb3 host disable.
> - * @usb3_host_port: the register of type-c usb3 host port.
> - * @uphy_dp_sel: the register of type-c phy DP select control.
> - */
> -struct rockchip_usb3phy_port_cfg {
> -       unsigned int reg;
> -       struct usb3phy_reg typec_conn_dir;
> -       struct usb3phy_reg usb3tousb2_en;
> -       struct usb3phy_reg external_psm;
> -       struct usb3phy_reg pipe_status;
> -       struct usb3phy_reg usb3_host_disable;
> -       struct usb3phy_reg usb3_host_port;
> -       struct usb3phy_reg uphy_dp_sel;
> -};
> -
> -struct rockchip_typec_phy {
> -       struct device *dev;
> -       void __iomem *base;
> -       struct extcon_dev *extcon;
> -       struct regmap *grf_regs;
> -       struct clk *clk_core;
> -       struct clk *clk_ref;
> -       struct reset_control *uphy_rst;
> -       struct reset_control *pipe_rst;
> -       struct reset_control *tcphy_rst;
> -       const struct rockchip_usb3phy_port_cfg *port_cfgs;
> -       /* mutex to protect access to individual PHYs */
> -       struct mutex lock;
> -
> -       bool flip;
> -       u8 mode;
> -};
> +#define DEFAULT_RATE                   162000
>

DEFAULT_RATE seems a very common name for me, maybe add a prefix?


>  struct phy_reg {
>         u16 value;
> @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
>         { 0x8,          CMN_DIAG_PLL0_LF_PROG },
>  };
>
> -struct phy_reg dp_pll_cfg[] = {
> +struct phy_reg dp_pll_rbr_cfg[] = {
>         { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>         { 0x18,         CMN_PLL1_VCOCAL_ITER },
>         { 0x30b9,       CMN_PLL1_VCOCAL_START },
> -       { 0x21c,        CMN_PLL1_INTDIV },
> +       { 0x87,         CMN_PLL1_INTDIV },
>         { 0,            CMN_PLL1_FRACDIV },
> -       { 0x5,          CMN_PLL1_HIGH_THR },
> -       { 0x35,         CMN_PLL1_SS_CTRL1 },
> -       { 0x7f1e,       CMN_PLL1_SS_CTRL2 },
> +       { 0x22,         CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
>         { 0x20,         CMN_PLL1_DSM_DIAG },
>         { 0,            CMN_PLLSM1_USER_DEF_CTRL },
>         { 0,            CMN_DIAG_PLL1_OVRD },
> @@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
>         { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>         { 0x100,        CMN_DIAG_PLL1_PTATIS_TUNE1 },
>         { 0x7,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> -       { 0x4,          CMN_DIAG_PLL1_INCLK_CTRL },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
> +};
> +
> +struct phy_reg dp_pll_hbr_cfg[] = {
> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
> +       { 0xe1,         CMN_PLL1_INTDIV },
> +       { 0,            CMN_PLL1_FRACDIV },
> +       { 0x5,          CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
> +       { 0x20,         CMN_PLL1_DSM_DIAG },
> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
> +       { 0,            CMN_DIAG_PLL1_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>  };
>
> +struct phy_reg dp_pll_hbr2_cfg[] = {
> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
> +       { 0xe1,         CMN_PLL1_INTDIV },
> +       { 0,            CMN_PLL1_FRACDIV },
> +       { 0x5,          CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
> +       { 0x20,         CMN_PLL1_DSM_DIAG },
> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
> +       { 0,            CMN_DIAG_PLL1_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
> +};
>  static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
>         {
>                 .reg = 0xff7c0000,
> @@ -484,7 +492,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
>
>         rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
>         rdata &= ~CLK_PLL_MASK;
> -       rdata |= CLK_PLL_CONFIG;
> +       rdata |= CLK_PLL1_DIV2;
>         writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
>  }
>
> @@ -498,17 +506,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
>                        tcphy->base + usb3_pll_cfg[i].addr);
>  }
>
> -static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
> +static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
>  {
> -       u32 i;
> +       struct phy_reg *phy_cfg;
> +       u32 clk_ctrl;
> +       u32 i, cfg_size, hsclk_sel;
> +
> +       hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
> +       hsclk_sel &= ~CLK_PLL_MASK;
> +
> +       switch (link_rate) {
> +       case 162000:
> +               clk_ctrl = DP_PLL_DATA_RATE_RBR;
> +               hsclk_sel |= CLK_PLL1_DIV2;
> +               phy_cfg = dp_pll_rbr_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
> +               break;
> +       case 270000:
> +               clk_ctrl = DP_PLL_DATA_RATE_HBR;
> +               hsclk_sel |= CLK_PLL1_DIV2;
> +               phy_cfg = dp_pll_hbr_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
> +               break;
> +       case 540000:
> +               clk_ctrl = DP_PLL_DATA_RATE_HBR2;
> +               hsclk_sel |= CLK_PLL1_DIV1;
> +               phy_cfg = dp_pll_hbr2_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
> +               break;
> +       }
> +
> +       clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
> +       writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
>
> -       /* set the default mode to RBR */
> -       writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
> -              tcphy->base + DP_CLK_CTL);
> +       writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
>
>         /* load the configuration of PLL1 */
> -       for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
> -               writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
> +       for (i = 0; i < cfg_size; i++)
> +               writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
>  }
>
>  static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
> @@ -535,9 +570,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>         writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
>  }
>
> -static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
> +static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
> +                             u8 swing, u8 pre_emp, u32 lane)
>  {
> -       u16 rdata;
> +       u16 val;

From what I see you are only renaming rdata to val, there is any reason?

>
>         writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
>         writel(0x6799, tcphy->base + TX_PSC_A0(lane));
> @@ -545,25 +581,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>         writel(0x98, tcphy->base + TX_PSC_A2(lane));
>         writel(0x98, tcphy->base + TX_PSC_A3(lane));
>
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
> -
> -       writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> -       writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
> -
> -       rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> -       rdata = (rdata & 0x8fff) | 0x6000;
> -       writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +       writel(tcphy->config[swing][pre_emp].swing,
> +              tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> +       writel(tcphy->config[swing][pre_emp].pe,
> +              tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
> +
> +       if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
> +               writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
> +               writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> +       } else {
> +               writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> +               writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
> +       }
> +
> +       val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +       val = val & 0x8fff;
> +       switch (link_rate) {
> +       case 162000:
> +       case 270000:
> +               val |= (6 << 12);
> +               break;
> +       case 540000:
> +               val |= (4 << 12);
> +               break;
> +       }
> +       writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>  }
>
>  static inline int property_enable(struct rockchip_typec_phy *tcphy,
> @@ -754,30 +796,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
>         tcphy_cfg_24m(tcphy);
>
>         if (mode == MODE_DFP_DP) {
> -               tcphy_cfg_dp_pll(tcphy);
> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>                 for (i = 0; i < 4; i++)
> -                       tcphy_dp_cfg_lane(tcphy, i);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, i);
>
>                 writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
>         } else {
>                 tcphy_cfg_usb3_pll(tcphy);
> -               tcphy_cfg_dp_pll(tcphy);
> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>                 if (tcphy->flip) {
>                         tcphy_tx_usb3_cfg_lane(tcphy, 3);
>                         tcphy_rx_usb3_cfg_lane(tcphy, 2);
> -                       tcphy_dp_cfg_lane(tcphy, 0);
> -                       tcphy_dp_cfg_lane(tcphy, 1);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 0);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 1);
>                 } else {
>                         tcphy_tx_usb3_cfg_lane(tcphy, 0);
>                         tcphy_rx_usb3_cfg_lane(tcphy, 1);
> -                       tcphy_dp_cfg_lane(tcphy, 2);
> -                       tcphy_dp_cfg_lane(tcphy, 3);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 2);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 3);
>                 }
>
>                 writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
>         }
>
> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         reset_control_deassert(tcphy->uphy_rst);
>
> @@ -990,7 +1035,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>         property_enable(tcphy, &cfg->uphy_dp_sel, 1);
>
>         ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> -                                val, val & DP_MODE_A2, 1000,
> +                                val, val & DP_MODE_A2_ACK, 1000,
>                                  PHY_MODE_SET_TIMEOUT);
>         if (ret < 0) {
>                 dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
> @@ -999,13 +1044,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>
>         tcphy_dp_aux_calibration(tcphy);
>
> -       writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
> +       /* enter A0 mode */
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A0;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> -                                val, val & DP_MODE_A0, 1000,
> +                                val, val & DP_MODE_A0_ACK, 1000,
>                                  PHY_MODE_SET_TIMEOUT);
>         if (ret < 0) {
> -               writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +               val &= ~DP_MODE_MASK;
> +               val |= DP_MODE_ENTER_A2;
> +               writel(val, tcphy->base + DP_MODE_CTL);
>                 dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
>                 goto power_on_finish;
>         }
> @@ -1023,6 +1074,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>  static int rockchip_dp_phy_power_off(struct phy *phy)
>  {
>         struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +       u32 val;
>
>         mutex_lock(&tcphy->lock);
>
> @@ -1031,7 +1083,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
>
>         tcphy->mode &= ~MODE_DFP_DP;
>
> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A2;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         if (tcphy->mode == MODE_DISCONNECT)
>                 tcphy_phy_deinit(tcphy);
> @@ -1047,6 +1102,30 @@ static const struct phy_ops rockchip_dp_phy_ops = {
>         .owner          = THIS_MODULE,
>  };
>
> +static int type_c_dp_phy_config(struct phy *phy, int link_rate,

s/type_c/typec/ to be coherent with the rest of the code.

> +                        int lanes, u8 swing, u8 pre_emp)
> +{
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +       u8 i;
> +
> +       tcphy_cfg_dp_pll(tcphy, link_rate);
> +
> +       if (tcphy->mode == MODE_DFP_DP) {
> +               for (i = 0; i < 4; i++)
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
> +       } else {
> +               if (tcphy->flip) {
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
> +               } else {
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
> +               }
> +       }
> +
> +       return 0;
> +}
> +
>  static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>                           struct device *dev)
>  {
> @@ -1087,6 +1166,14 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>                 return PTR_ERR(tcphy->tcphy_rst);
>         }
>
> +       /*
> +        * check if phy_config pass from dts, if yes,
> +        * need to use this phy config to do software training later
> +        */
> +       if (!of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
> +               (u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32)))
> +               tcphy->need_software_training = 1;
> +
>         return 0;
>  }
>
> @@ -1171,6 +1258,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
>                 }
>         }
>
> +       tcphy->typec_phy_config = type_c_dp_phy_config;

type_c_dp_phy_config -> typec_dp_phy_config

>         pm_runtime_enable(dev);
>
>         for_each_available_child_of_node(np, child_np) {
> diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
> new file mode 100644
> index 0000000..e25840e
> --- /dev/null
> +++ b/include/soc/rockchip/rockchip_phy_typec.h
> @@ -0,0 +1,72 @@
> +/*

Add the SPDX License identifier ...

> + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Lin Huang <hl@rock-chips.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.

... and remove the above notice.

> + */
> +#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
> +#define __SOC_ROCKCHIP_PHY_TYPEC_H
> +
> +

Remove the extra new line.

> +struct usb3phy_reg {
> +       u32 offset;
> +       u32 enable_bit;
> +       u32 write_enable;
> +};
> +
> +/**
> + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
> + * @reg: the base address for usb3-phy config.
> + * @typec_conn_dir: the register of type-c connector direction.
> + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
> + * @external_psm: the register of type-c phy external psm clock.
> + * @pipe_status: the register of type-c phy pipe status.
> + * @usb3_host_disable: the register of type-c usb3 host disable.
> + * @usb3_host_port: the register of type-c usb3 host port.
> + * @uphy_dp_sel: the register of type-c phy DP select control.
> + */
> +struct rockchip_usb3phy_port_cfg {
> +       unsigned int reg;
> +       struct usb3phy_reg typec_conn_dir;
> +       struct usb3phy_reg usb3tousb2_en;
> +       struct usb3phy_reg external_psm;
> +       struct usb3phy_reg pipe_status;
> +       struct usb3phy_reg usb3_host_disable;
> +       struct usb3phy_reg usb3_host_port;
> +       struct usb3phy_reg uphy_dp_sel;
> +};
> +
> +struct phy_config {
> +       int swing;
> +       int pe;
> +};
> +
> +struct rockchip_typec_phy {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct extcon_dev *extcon;
> +       struct regmap *grf_regs;
> +       struct clk *clk_core;
> +       struct clk *clk_ref;
> +       struct reset_control *uphy_rst;
> +       struct reset_control *pipe_rst;
> +       struct reset_control *tcphy_rst;
> +       struct rockchip_usb3phy_port_cfg *port_cfgs;
> +       /* mutex to protect access to individual PHYs */
> +       struct mutex lock;
> +       struct phy_config config[3][4];
> +       u8 need_software_training;
> +       bool flip;
> +       u8 mode;
> +       int (*typec_phy_config)(struct phy *phy, int link_rate,
> +                               int lanes, u8 swing, u8 pre_emp);
> +};
> +
> +#endif

Best regards,
 Enric

> --
> 2.7.4
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 2/4] phy: rockchip-typec: support variable phy config value
@ 2018-05-07 13:59     ` Enric Balletbo Serra
  0 siblings, 0 replies; 26+ messages in thread
From: Enric Balletbo Serra @ 2018-05-07 13:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lin,

Thanks for the patch, apart from the new build warnings introduced
some more comments below.

2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
> the phy config values used to fix in dp firmware, but some boards
> need change these values to do training and get the better eye diagram
> result. So support that in phy driver.
>
> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
>  drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++++++++++++++++++-----------
>  include/soc/rockchip/rockchip_phy_typec.h |  72 ++++++++
>  2 files changed, 259 insertions(+), 99 deletions(-)
>  create mode 100644 include/soc/rockchip/rockchip_phy_typec.h
>
> diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
> index 76a4b58..831a93b 100644
> --- a/drivers/phy/rockchip/phy-rockchip-typec.c
> +++ b/drivers/phy/rockchip/phy-rockchip-typec.c
> @@ -63,6 +63,7 @@
>
>  #include <linux/mfd/syscon.h>
>  #include <linux/phy/phy.h>
> +#include <soc/rockchip/rockchip_phy_typec.h>
>
>  #define CMN_SSM_BANDGAP                        (0x21 << 2)
>  #define CMN_SSM_BIAS                   (0x22 << 2)
> @@ -323,23 +324,31 @@
>   * clock 0: PLL 0 div 1
>   * clock 1: PLL 1 div 2
>   */
> -#define CLK_PLL_CONFIG                 0X30
> +#define CLK_PLL1_DIV1                  0x20
> +#define CLK_PLL1_DIV2                  0x30
>  #define CLK_PLL_MASK                   0x33
>
>  #define CMN_READY                      BIT(0)
>
> +#define DP_PLL_CLOCK_ENABLE_ACK                BIT(3)
>  #define DP_PLL_CLOCK_ENABLE            BIT(2)
> +#define DP_PLL_ENABLE_ACK              BIT(1)
>  #define DP_PLL_ENABLE                  BIT(0)
>  #define DP_PLL_DATA_RATE_RBR           ((2 << 12) | (4 << 8))
>  #define DP_PLL_DATA_RATE_HBR           ((2 << 12) | (4 << 8))
>  #define DP_PLL_DATA_RATE_HBR2          ((1 << 12) | (2 << 8))
> +#define DP_PLL_DATA_RATE_MASK          0xff00
>
> -#define DP_MODE_A0                     BIT(4)
> -#define DP_MODE_A2                     BIT(6)
> -#define DP_MODE_ENTER_A0               0xc101
> -#define DP_MODE_ENTER_A2               0xc104
> +#define DP_MODE_MASK                   0xf
> +#define DP_MODE_ENTER_A0               BIT(0)
> +#define DP_MODE_ENTER_A2               BIT(2)
> +#define DP_MODE_ENTER_A3               BIT(3)
> +#define DP_MODE_A0_ACK                 BIT(4)
> +#define DP_MODE_A2_ACK                 BIT(6)
> +#define DP_MODE_A3_ACK                 BIT(7)
> +#define DP_LINK_RESET_DEASSERTED       BIT(8)
>
> -#define PHY_MODE_SET_TIMEOUT           100000
> +#define PHY_MODE_SET_TIMEOUT           1000000
>

Why do you need to increase this timeout? Is because the software link
training timed out using the old value?


>  #define PIN_ASSIGN_C_E                 0x51d9
>  #define PIN_ASSIGN_D_F                 0x5100
> @@ -349,51 +358,7 @@
>  #define MODE_DFP_USB                   BIT(1)
>  #define MODE_DFP_DP                    BIT(2)
>
> -struct usb3phy_reg {
> -       u32 offset;
> -       u32 enable_bit;
> -       u32 write_enable;
> -};
> -
> -/**
> - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
> - * @reg: the base address for usb3-phy config.
> - * @typec_conn_dir: the register of type-c connector direction.
> - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
> - * @external_psm: the register of type-c phy external psm clock.
> - * @pipe_status: the register of type-c phy pipe status.
> - * @usb3_host_disable: the register of type-c usb3 host disable.
> - * @usb3_host_port: the register of type-c usb3 host port.
> - * @uphy_dp_sel: the register of type-c phy DP select control.
> - */
> -struct rockchip_usb3phy_port_cfg {
> -       unsigned int reg;
> -       struct usb3phy_reg typec_conn_dir;
> -       struct usb3phy_reg usb3tousb2_en;
> -       struct usb3phy_reg external_psm;
> -       struct usb3phy_reg pipe_status;
> -       struct usb3phy_reg usb3_host_disable;
> -       struct usb3phy_reg usb3_host_port;
> -       struct usb3phy_reg uphy_dp_sel;
> -};
> -
> -struct rockchip_typec_phy {
> -       struct device *dev;
> -       void __iomem *base;
> -       struct extcon_dev *extcon;
> -       struct regmap *grf_regs;
> -       struct clk *clk_core;
> -       struct clk *clk_ref;
> -       struct reset_control *uphy_rst;
> -       struct reset_control *pipe_rst;
> -       struct reset_control *tcphy_rst;
> -       const struct rockchip_usb3phy_port_cfg *port_cfgs;
> -       /* mutex to protect access to individual PHYs */
> -       struct mutex lock;
> -
> -       bool flip;
> -       u8 mode;
> -};
> +#define DEFAULT_RATE                   162000
>

DEFAULT_RATE seems a very common name for me, maybe add a prefix?


>  struct phy_reg {
>         u16 value;
> @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
>         { 0x8,          CMN_DIAG_PLL0_LF_PROG },
>  };
>
> -struct phy_reg dp_pll_cfg[] = {
> +struct phy_reg dp_pll_rbr_cfg[] = {
>         { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>         { 0x18,         CMN_PLL1_VCOCAL_ITER },
>         { 0x30b9,       CMN_PLL1_VCOCAL_START },
> -       { 0x21c,        CMN_PLL1_INTDIV },
> +       { 0x87,         CMN_PLL1_INTDIV },
>         { 0,            CMN_PLL1_FRACDIV },
> -       { 0x5,          CMN_PLL1_HIGH_THR },
> -       { 0x35,         CMN_PLL1_SS_CTRL1 },
> -       { 0x7f1e,       CMN_PLL1_SS_CTRL2 },
> +       { 0x22,         CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
>         { 0x20,         CMN_PLL1_DSM_DIAG },
>         { 0,            CMN_PLLSM1_USER_DEF_CTRL },
>         { 0,            CMN_DIAG_PLL1_OVRD },
> @@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
>         { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>         { 0x100,        CMN_DIAG_PLL1_PTATIS_TUNE1 },
>         { 0x7,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> -       { 0x4,          CMN_DIAG_PLL1_INCLK_CTRL },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
> +};
> +
> +struct phy_reg dp_pll_hbr_cfg[] = {
> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
> +       { 0xe1,         CMN_PLL1_INTDIV },
> +       { 0,            CMN_PLL1_FRACDIV },
> +       { 0x5,          CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
> +       { 0x20,         CMN_PLL1_DSM_DIAG },
> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
> +       { 0,            CMN_DIAG_PLL1_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>  };
>
> +struct phy_reg dp_pll_hbr2_cfg[] = {
> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
> +       { 0xe1,         CMN_PLL1_INTDIV },
> +       { 0,            CMN_PLL1_FRACDIV },
> +       { 0x5,          CMN_PLL1_HIGH_THR },
> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
> +       { 0,            CMN_PLL1_SS_CTRL2 },
> +       { 0x20,         CMN_PLL1_DSM_DIAG },
> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
> +       { 0,            CMN_DIAG_PLL1_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
> +};
>  static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
>         {
>                 .reg = 0xff7c0000,
> @@ -484,7 +492,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
>
>         rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
>         rdata &= ~CLK_PLL_MASK;
> -       rdata |= CLK_PLL_CONFIG;
> +       rdata |= CLK_PLL1_DIV2;
>         writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
>  }
>
> @@ -498,17 +506,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
>                        tcphy->base + usb3_pll_cfg[i].addr);
>  }
>
> -static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
> +static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
>  {
> -       u32 i;
> +       struct phy_reg *phy_cfg;
> +       u32 clk_ctrl;
> +       u32 i, cfg_size, hsclk_sel;
> +
> +       hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
> +       hsclk_sel &= ~CLK_PLL_MASK;
> +
> +       switch (link_rate) {
> +       case 162000:
> +               clk_ctrl = DP_PLL_DATA_RATE_RBR;
> +               hsclk_sel |= CLK_PLL1_DIV2;
> +               phy_cfg = dp_pll_rbr_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
> +               break;
> +       case 270000:
> +               clk_ctrl = DP_PLL_DATA_RATE_HBR;
> +               hsclk_sel |= CLK_PLL1_DIV2;
> +               phy_cfg = dp_pll_hbr_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
> +               break;
> +       case 540000:
> +               clk_ctrl = DP_PLL_DATA_RATE_HBR2;
> +               hsclk_sel |= CLK_PLL1_DIV1;
> +               phy_cfg = dp_pll_hbr2_cfg;
> +               cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
> +               break;
> +       }
> +
> +       clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
> +       writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
>
> -       /* set the default mode to RBR */
> -       writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
> -              tcphy->base + DP_CLK_CTL);
> +       writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
>
>         /* load the configuration of PLL1 */
> -       for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
> -               writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
> +       for (i = 0; i < cfg_size; i++)
> +               writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
>  }
>
>  static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
> @@ -535,9 +570,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>         writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
>  }
>
> -static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
> +static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
> +                             u8 swing, u8 pre_emp, u32 lane)
>  {
> -       u16 rdata;
> +       u16 val;

>From what I see you are only renaming rdata to val, there is any reason?

>
>         writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
>         writel(0x6799, tcphy->base + TX_PSC_A0(lane));
> @@ -545,25 +581,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>         writel(0x98, tcphy->base + TX_PSC_A2(lane));
>         writel(0x98, tcphy->base + TX_PSC_A3(lane));
>
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
> -
> -       writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> -       writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
> -
> -       rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> -       rdata = (rdata & 0x8fff) | 0x6000;
> -       writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +       writel(tcphy->config[swing][pre_emp].swing,
> +              tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> +       writel(tcphy->config[swing][pre_emp].pe,
> +              tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
> +
> +       if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
> +               writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
> +               writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> +       } else {
> +               writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> +               writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
> +       }
> +
> +       val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +       val = val & 0x8fff;
> +       switch (link_rate) {
> +       case 162000:
> +       case 270000:
> +               val |= (6 << 12);
> +               break;
> +       case 540000:
> +               val |= (4 << 12);
> +               break;
> +       }
> +       writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>  }
>
>  static inline int property_enable(struct rockchip_typec_phy *tcphy,
> @@ -754,30 +796,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
>         tcphy_cfg_24m(tcphy);
>
>         if (mode == MODE_DFP_DP) {
> -               tcphy_cfg_dp_pll(tcphy);
> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>                 for (i = 0; i < 4; i++)
> -                       tcphy_dp_cfg_lane(tcphy, i);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, i);
>
>                 writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
>         } else {
>                 tcphy_cfg_usb3_pll(tcphy);
> -               tcphy_cfg_dp_pll(tcphy);
> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>                 if (tcphy->flip) {
>                         tcphy_tx_usb3_cfg_lane(tcphy, 3);
>                         tcphy_rx_usb3_cfg_lane(tcphy, 2);
> -                       tcphy_dp_cfg_lane(tcphy, 0);
> -                       tcphy_dp_cfg_lane(tcphy, 1);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 0);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 1);
>                 } else {
>                         tcphy_tx_usb3_cfg_lane(tcphy, 0);
>                         tcphy_rx_usb3_cfg_lane(tcphy, 1);
> -                       tcphy_dp_cfg_lane(tcphy, 2);
> -                       tcphy_dp_cfg_lane(tcphy, 3);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 2);
> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 3);
>                 }
>
>                 writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
>         }
>
> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         reset_control_deassert(tcphy->uphy_rst);
>
> @@ -990,7 +1035,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>         property_enable(tcphy, &cfg->uphy_dp_sel, 1);
>
>         ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> -                                val, val & DP_MODE_A2, 1000,
> +                                val, val & DP_MODE_A2_ACK, 1000,
>                                  PHY_MODE_SET_TIMEOUT);
>         if (ret < 0) {
>                 dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
> @@ -999,13 +1044,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>
>         tcphy_dp_aux_calibration(tcphy);
>
> -       writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
> +       /* enter A0 mode */
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A0;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> -                                val, val & DP_MODE_A0, 1000,
> +                                val, val & DP_MODE_A0_ACK, 1000,
>                                  PHY_MODE_SET_TIMEOUT);
>         if (ret < 0) {
> -               writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +               val &= ~DP_MODE_MASK;
> +               val |= DP_MODE_ENTER_A2;
> +               writel(val, tcphy->base + DP_MODE_CTL);
>                 dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
>                 goto power_on_finish;
>         }
> @@ -1023,6 +1074,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>  static int rockchip_dp_phy_power_off(struct phy *phy)
>  {
>         struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +       u32 val;
>
>         mutex_lock(&tcphy->lock);
>
> @@ -1031,7 +1083,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
>
>         tcphy->mode &= ~MODE_DFP_DP;
>
> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
> +       val = readl(tcphy->base + DP_MODE_CTL);
> +       val &= ~DP_MODE_MASK;
> +       val |= DP_MODE_ENTER_A2;
> +       writel(val, tcphy->base + DP_MODE_CTL);
>
>         if (tcphy->mode == MODE_DISCONNECT)
>                 tcphy_phy_deinit(tcphy);
> @@ -1047,6 +1102,30 @@ static const struct phy_ops rockchip_dp_phy_ops = {
>         .owner          = THIS_MODULE,
>  };
>
> +static int type_c_dp_phy_config(struct phy *phy, int link_rate,

s/type_c/typec/ to be coherent with the rest of the code.

> +                        int lanes, u8 swing, u8 pre_emp)
> +{
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +       u8 i;
> +
> +       tcphy_cfg_dp_pll(tcphy, link_rate);
> +
> +       if (tcphy->mode == MODE_DFP_DP) {
> +               for (i = 0; i < 4; i++)
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
> +       } else {
> +               if (tcphy->flip) {
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
> +               } else {
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
> +               }
> +       }
> +
> +       return 0;
> +}
> +
>  static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>                           struct device *dev)
>  {
> @@ -1087,6 +1166,14 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>                 return PTR_ERR(tcphy->tcphy_rst);
>         }
>
> +       /*
> +        * check if phy_config pass from dts, if yes,
> +        * need to use this phy config to do software training later
> +        */
> +       if (!of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
> +               (u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32)))
> +               tcphy->need_software_training = 1;
> +
>         return 0;
>  }
>
> @@ -1171,6 +1258,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
>                 }
>         }
>
> +       tcphy->typec_phy_config = type_c_dp_phy_config;

type_c_dp_phy_config -> typec_dp_phy_config

>         pm_runtime_enable(dev);
>
>         for_each_available_child_of_node(np, child_np) {
> diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
> new file mode 100644
> index 0000000..e25840e
> --- /dev/null
> +++ b/include/soc/rockchip/rockchip_phy_typec.h
> @@ -0,0 +1,72 @@
> +/*

Add the SPDX License identifier ...

> + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Lin Huang <hl@rock-chips.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.

... and remove the above notice.

> + */
> +#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
> +#define __SOC_ROCKCHIP_PHY_TYPEC_H
> +
> +

Remove the extra new line.

> +struct usb3phy_reg {
> +       u32 offset;
> +       u32 enable_bit;
> +       u32 write_enable;
> +};
> +
> +/**
> + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
> + * @reg: the base address for usb3-phy config.
> + * @typec_conn_dir: the register of type-c connector direction.
> + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
> + * @external_psm: the register of type-c phy external psm clock.
> + * @pipe_status: the register of type-c phy pipe status.
> + * @usb3_host_disable: the register of type-c usb3 host disable.
> + * @usb3_host_port: the register of type-c usb3 host port.
> + * @uphy_dp_sel: the register of type-c phy DP select control.
> + */
> +struct rockchip_usb3phy_port_cfg {
> +       unsigned int reg;
> +       struct usb3phy_reg typec_conn_dir;
> +       struct usb3phy_reg usb3tousb2_en;
> +       struct usb3phy_reg external_psm;
> +       struct usb3phy_reg pipe_status;
> +       struct usb3phy_reg usb3_host_disable;
> +       struct usb3phy_reg usb3_host_port;
> +       struct usb3phy_reg uphy_dp_sel;
> +};
> +
> +struct phy_config {
> +       int swing;
> +       int pe;
> +};
> +
> +struct rockchip_typec_phy {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct extcon_dev *extcon;
> +       struct regmap *grf_regs;
> +       struct clk *clk_core;
> +       struct clk *clk_ref;
> +       struct reset_control *uphy_rst;
> +       struct reset_control *pipe_rst;
> +       struct reset_control *tcphy_rst;
> +       struct rockchip_usb3phy_port_cfg *port_cfgs;
> +       /* mutex to protect access to individual PHYs */
> +       struct mutex lock;
> +       struct phy_config config[3][4];
> +       u8 need_software_training;
> +       bool flip;
> +       u8 mode;
> +       int (*typec_phy_config)(struct phy *phy, int link_rate,
> +                               int lanes, u8 swing, u8 pre_emp);
> +};
> +
> +#endif

Best regards,
 Enric

> --
> 2.7.4
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
  2018-05-07 11:27   ` Enric Balletbo Serra
@ 2018-05-09  2:59     ` hl
  -1 siblings, 0 replies; 26+ messages in thread
From: hl @ 2018-05-09  2:59 UTC (permalink / raw)
  To: Enric Balletbo Serra
  Cc: Heiko Stübner, David Airlie, Brian Norris, Doug Anderson,
	jani.nikula, linux-kernel, open list:ARM/Rockchip SoC...,
	Sean Paul, dri-devel, Chris Zhong, daniel.vetter, Linux ARM

Hi Enric,


On Monday, May 07, 2018 07:27 PM, Enric Balletbo Serra wrote:
> Hi Lin,
>
> I am interested in these patches, could you cc me on newer versions? Thanks.
>
> Some comments below.
Sure, will cc to you next version.
>
> 2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>> From: Chris Zhong <zyw@rock-chips.com>
>>
>> We may support training outside firmware, so we need support
>> dpcd read/write to get the message or do some setting with
>> display.
>>
>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>> ---
>>   drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 ++++++++++++++++++++++++----
>>   drivers/gpu/drm/rockchip/cdn-dp-core.h |  1 +
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.c  | 66 +++++++++++++++++++++++++++++-----
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.h  | 14 ++++++--
>>   4 files changed, 119 insertions(+), 17 deletions(-)
>>
> In general, for this patch and all the other patches in the series I
> saw that checkpatch spits out some warnings, could you fix these and
> ideally run checkpatch with the --strict --subjective option?
Okay.
>
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> index c6fbdcd..268c190 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
>>          u8 value;
>>
>>          *sink_count = 0;
>> -       ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
>> -       if (ret)
>> +       ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
>> +       if (ret < 0)
>>                  return ret;
>>
>>          *sink_count = DP_GET_SINK_COUNT(value);
>> @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
>>          if (!cdn_dp_check_sink_connection(dp))
>>                  return -ENODEV;
>>
>> -       ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
>> -                              DP_RECEIVER_CAP_SIZE);
>> -       if (ret) {
>> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
>> +                              sizeof(dp->dpcd));
>> +       if (ret < 0) {
>>                  DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>>                  return ret;
>>          }
>> @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
>>          if (!port || !dp->link.rate || !dp->link.num_lanes)
>>                  return false;
>>
>> -       if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
>> -                            DP_LINK_STATUS_SIZE)) {
>> +       if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
>> +           DP_LINK_STATUS_SIZE) {
>>                  DRM_ERROR("Failed to get link status\n");
>>                  return false;
>>          }
>> @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
>>          return NOTIFY_DONE;
>>   }
>>
>> +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
>> +                                  struct drm_dp_aux_msg *msg)
>> +{
>> +       struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
>> +       int ret;
>> +       u8 status;
>> +
>> +       switch (msg->request & ~DP_AUX_I2C_MOT) {
>> +       case DP_AUX_NATIVE_WRITE:
>> +       case DP_AUX_I2C_WRITE:
>> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
>> +               ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
>> +                                       msg->size);
>> +               break;
>> +       case DP_AUX_NATIVE_READ:
>> +       case DP_AUX_I2C_READ:
>> +               ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
>> +                                      msg->size);
>> +               break;
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +
>> +       status = cdn_dp_get_aux_status(dp);
>> +       if (status == AUX_STAUS_ACK)
>> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
>> +       else if (status == AUX_STAUS_NACK)
>> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK;
>> +       else if (status == AUX_STAUS_DEFER)
>> +               msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
>> +
> I think that you would mean STATUS instead of STAUS on these defines.
>
> What happens if the status is AUX_STATUS_SINK_ERROR or AUX_STATUS_BUS_ERROR?
drm_dp_i2c_do_msg() will mark it as invalid i2c relay and return error.
>
>> +       return ret;
>> +}
>> +
>>   static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>>   {
>>          struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>>          dp->active = false;
>>          dp->active_port = -1;
>>          dp->fw_loaded = false;
>> +       dp->aux.name = "DP-AUX";
>> +       dp->aux.transfer = cdn_dp_aux_transfer;
>> +       dp->aux.dev = dev;
>> +
>> +       ret = drm_dp_aux_register(&dp->aux);
>> +       if (ret)
>> +               return ret;
>>
>>          INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
>>
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> index f57e296..46159b2 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> @@ -78,6 +78,7 @@ struct cdn_dp_device {
>>          struct platform_device *audio_pdev;
>>          struct work_struct event_work;
>>          struct edid *edid;
>> +       struct drm_dp_aux aux;
>>
>>          struct mutex lock;
>>          bool connected;
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> index eb3042c..b2f532a 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> @@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
>>                                     sizeof(field), field);
>>   }
>>
>> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>> +/*
>> + * Returns the number of bytes transferred on success, or a negative error
>> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
>> + */
> Returns the number of bytes or -ETIMEDOUT, no other negative errors
> can be returned, right?
>
> I am not English native but the last phrase sounds incorrect to me,
> I'd rephrase it: (open to suggestions)
>
>   -ETIMEDOUT if fails to receive the mailbox message.
>
>> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>>   {
>>          u8 msg[5], reg[5];
>>          int ret;
>> @@ -247,24 +251,40 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>>                  goto err_dpcd_read;
>>
>>          ret = cdn_dp_mailbox_read_receive(dp, data, len);
>> +       if (!ret)
>> +               return len;
>>
>>   err_dpcd_read:
>> +       DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
>>          return ret;
>>   }
>>
>> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>> +#define CDN_AUX_HEADER_SIZE    5
>> +#define CDN_AUX_MSG_SIZE       20
>> +/*
>> + * Returns the number of bytes transferred on success, or a negative error
>> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
> Same as above. Sounds incorrect to me.
>
>> + * -EINVAL is return if get the wrong data size after message send.
> Same here.
>
>> + */
>> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>>   {
>> -       u8 msg[6], reg[5];
>> +       u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
>> +       u8 reg[CDN_AUX_HEADER_SIZE];
>>          int ret;
>>
>> -       msg[0] = 0;
>> -       msg[1] = 1;
>> +       if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
>> +               return -EINVAL;
>> +
>> +       msg[0] = (len >> 8) & 0xff;
>> +       msg[1] = len & 0xff;
>>          msg[2] = (addr >> 16) & 0xff;
>>          msg[3] = (addr >> 8) & 0xff;
>>          msg[4] = addr & 0xff;
>> -       msg[5] = value;
>> +
>> +       memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
>> +
>>          ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
>> -                                 sizeof(msg), msg);
>> +                                 CDN_AUX_HEADER_SIZE + len, msg);
>>          if (ret)
>>                  goto err_dpcd_write;
>>
>> @@ -277,8 +297,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>>          if (ret)
>>                  goto err_dpcd_write;
>>
>> -       if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
>> +       if ((len != (reg[0] << 8 | reg[1])) ||
>> +           (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
>>                  ret = -EINVAL;
>> +       } else {
>> +               return len;
>> +       }
>>
>>   err_dpcd_write:
>>          if (ret)
>> @@ -286,6 +310,32 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>>          return ret;
>>   }
>>
>> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
>> +{
>> +       u8 status;
>> +       int ret;
>> +
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_LAST_AUX_STAUS,
>> +                                 0, NULL);
>> +       if (ret)
>> +               goto err_get_hpd;
>> +
>> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
>> +                                             DPTX_GET_LAST_AUX_STAUS, sizeof(status));
>> +       if (ret)
>> +               goto err_get_hpd;
>> +
>> +       ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
>> +       if (ret)
>> +               goto err_get_hpd;
>> +
>> +       return status;
>> +
>> +err_get_hpd:
>> +       DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
>> +       return ret;
>> +}
>> +
>>   int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>>                           u32 i_size, const u32 *d_mem, u32 d_size)
>>   {
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> index c4bbb4a83..aedf2dc 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> @@ -328,6 +328,13 @@
>>   #define GENERAL_BUS_SETTINGS            0x03
>>   #define GENERAL_TEST_ACCESS             0x04
>>
>> +/* AUX status*/
>> +#define AUX_STAUS_ACK                  0
>> +#define AUX_STAUS_NACK                 1
>> +#define AUX_STAUS_DEFER                        2
>> +#define AUX_STAUS_SINK_ERROR           3
>> +#define AUX_STAUS_BUS_ERROR            4
>> +
> For the five defines, s/STAUS/STATUS/
will fix it.
>
>>   #define DPTX_SET_POWER_MNG                     0x00
>>   #define DPTX_SET_HOST_CAPABILITIES             0x01
>>   #define DPTX_GET_EDID                          0x02
>> @@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>>   int cdn_dp_event_config(struct cdn_dp_device *dp);
>>   u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>>   int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
>> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
>> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>> +                         u8 *data, u16 len);
>> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
>> +                        u8 *data, u16 len);
>> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
>>   int cdn_dp_get_edid_block(void *dp, u8 *edid,
>>                            unsigned int block, size_t length);
>>   int cdn_dp_train_link(struct cdn_dp_device *dp);
>> --
>> 2.7.4
>>
>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
@ 2018-05-09  2:59     ` hl
  0 siblings, 0 replies; 26+ messages in thread
From: hl @ 2018-05-09  2:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Enric,


On Monday, May 07, 2018 07:27 PM, Enric Balletbo Serra wrote:
> Hi Lin,
>
> I am interested in these patches, could you cc me on newer versions? Thanks.
>
> Some comments below.
Sure, will cc to you next version.
>
> 2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>> From: Chris Zhong <zyw@rock-chips.com>
>>
>> We may support training outside firmware, so we need support
>> dpcd read/write to get the message or do some setting with
>> display.
>>
>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>> ---
>>   drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 ++++++++++++++++++++++++----
>>   drivers/gpu/drm/rockchip/cdn-dp-core.h |  1 +
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.c  | 66 +++++++++++++++++++++++++++++-----
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.h  | 14 ++++++--
>>   4 files changed, 119 insertions(+), 17 deletions(-)
>>
> In general, for this patch and all the other patches in the series I
> saw that checkpatch spits out some warnings, could you fix these and
> ideally run checkpatch with the --strict --subjective option?
Okay.
>
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> index c6fbdcd..268c190 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
>>          u8 value;
>>
>>          *sink_count = 0;
>> -       ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
>> -       if (ret)
>> +       ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
>> +       if (ret < 0)
>>                  return ret;
>>
>>          *sink_count = DP_GET_SINK_COUNT(value);
>> @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
>>          if (!cdn_dp_check_sink_connection(dp))
>>                  return -ENODEV;
>>
>> -       ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
>> -                              DP_RECEIVER_CAP_SIZE);
>> -       if (ret) {
>> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
>> +                              sizeof(dp->dpcd));
>> +       if (ret < 0) {
>>                  DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>>                  return ret;
>>          }
>> @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
>>          if (!port || !dp->link.rate || !dp->link.num_lanes)
>>                  return false;
>>
>> -       if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
>> -                            DP_LINK_STATUS_SIZE)) {
>> +       if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
>> +           DP_LINK_STATUS_SIZE) {
>>                  DRM_ERROR("Failed to get link status\n");
>>                  return false;
>>          }
>> @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb,
>>          return NOTIFY_DONE;
>>   }
>>
>> +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
>> +                                  struct drm_dp_aux_msg *msg)
>> +{
>> +       struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
>> +       int ret;
>> +       u8 status;
>> +
>> +       switch (msg->request & ~DP_AUX_I2C_MOT) {
>> +       case DP_AUX_NATIVE_WRITE:
>> +       case DP_AUX_I2C_WRITE:
>> +       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
>> +               ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
>> +                                       msg->size);
>> +               break;
>> +       case DP_AUX_NATIVE_READ:
>> +       case DP_AUX_I2C_READ:
>> +               ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
>> +                                      msg->size);
>> +               break;
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +
>> +       status = cdn_dp_get_aux_status(dp);
>> +       if (status == AUX_STAUS_ACK)
>> +               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
>> +       else if (status == AUX_STAUS_NACK)
>> +               msg->reply = DP_AUX_NATIVE_REPLY_NACK;
>> +       else if (status == AUX_STAUS_DEFER)
>> +               msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
>> +
> I think that you would mean STATUS instead of STAUS on these defines.
>
> What happens if the status is AUX_STATUS_SINK_ERROR or AUX_STATUS_BUS_ERROR?
drm_dp_i2c_do_msg() will mark it as invalid i2c relay and return error.
>
>> +       return ret;
>> +}
>> +
>>   static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>>   {
>>          struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
>>          dp->active = false;
>>          dp->active_port = -1;
>>          dp->fw_loaded = false;
>> +       dp->aux.name = "DP-AUX";
>> +       dp->aux.transfer = cdn_dp_aux_transfer;
>> +       dp->aux.dev = dev;
>> +
>> +       ret = drm_dp_aux_register(&dp->aux);
>> +       if (ret)
>> +               return ret;
>>
>>          INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
>>
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> index f57e296..46159b2 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> @@ -78,6 +78,7 @@ struct cdn_dp_device {
>>          struct platform_device *audio_pdev;
>>          struct work_struct event_work;
>>          struct edid *edid;
>> +       struct drm_dp_aux aux;
>>
>>          struct mutex lock;
>>          bool connected;
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> index eb3042c..b2f532a 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> @@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
>>                                     sizeof(field), field);
>>   }
>>
>> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>> +/*
>> + * Returns the number of bytes transferred on success, or a negative error
>> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
>> + */
> Returns the number of bytes or -ETIMEDOUT, no other negative errors
> can be returned, right?
>
> I am not English native but the last phrase sounds incorrect to me,
> I'd rephrase it: (open to suggestions)
>
>   -ETIMEDOUT if fails to receive the mailbox message.
>
>> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>>   {
>>          u8 msg[5], reg[5];
>>          int ret;
>> @@ -247,24 +251,40 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>>                  goto err_dpcd_read;
>>
>>          ret = cdn_dp_mailbox_read_receive(dp, data, len);
>> +       if (!ret)
>> +               return len;
>>
>>   err_dpcd_read:
>> +       DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
>>          return ret;
>>   }
>>
>> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>> +#define CDN_AUX_HEADER_SIZE    5
>> +#define CDN_AUX_MSG_SIZE       20
>> +/*
>> + * Returns the number of bytes transferred on success, or a negative error
>> + * code on failure. -ETIMEDOUT is returned if mailbox message not send success;
> Same as above. Sounds incorrect to me.
>
>> + * -EINVAL is return if get the wrong data size after message send.
> Same here.
>
>> + */
>> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
>>   {
>> -       u8 msg[6], reg[5];
>> +       u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
>> +       u8 reg[CDN_AUX_HEADER_SIZE];
>>          int ret;
>>
>> -       msg[0] = 0;
>> -       msg[1] = 1;
>> +       if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
>> +               return -EINVAL;
>> +
>> +       msg[0] = (len >> 8) & 0xff;
>> +       msg[1] = len & 0xff;
>>          msg[2] = (addr >> 16) & 0xff;
>>          msg[3] = (addr >> 8) & 0xff;
>>          msg[4] = addr & 0xff;
>> -       msg[5] = value;
>> +
>> +       memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
>> +
>>          ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
>> -                                 sizeof(msg), msg);
>> +                                 CDN_AUX_HEADER_SIZE + len, msg);
>>          if (ret)
>>                  goto err_dpcd_write;
>>
>> @@ -277,8 +297,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>>          if (ret)
>>                  goto err_dpcd_write;
>>
>> -       if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
>> +       if ((len != (reg[0] << 8 | reg[1])) ||
>> +           (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
>>                  ret = -EINVAL;
>> +       } else {
>> +               return len;
>> +       }
>>
>>   err_dpcd_write:
>>          if (ret)
>> @@ -286,6 +310,32 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
>>          return ret;
>>   }
>>
>> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
>> +{
>> +       u8 status;
>> +       int ret;
>> +
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_LAST_AUX_STAUS,
>> +                                 0, NULL);
>> +       if (ret)
>> +               goto err_get_hpd;
>> +
>> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
>> +                                             DPTX_GET_LAST_AUX_STAUS, sizeof(status));
>> +       if (ret)
>> +               goto err_get_hpd;
>> +
>> +       ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
>> +       if (ret)
>> +               goto err_get_hpd;
>> +
>> +       return status;
>> +
>> +err_get_hpd:
>> +       DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
>> +       return ret;
>> +}
>> +
>>   int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>>                           u32 i_size, const u32 *d_mem, u32 d_size)
>>   {
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> index c4bbb4a83..aedf2dc 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> @@ -328,6 +328,13 @@
>>   #define GENERAL_BUS_SETTINGS            0x03
>>   #define GENERAL_TEST_ACCESS             0x04
>>
>> +/* AUX status*/
>> +#define AUX_STAUS_ACK                  0
>> +#define AUX_STAUS_NACK                 1
>> +#define AUX_STAUS_DEFER                        2
>> +#define AUX_STAUS_SINK_ERROR           3
>> +#define AUX_STAUS_BUS_ERROR            4
>> +
> For the five defines, s/STAUS/STATUS/
will fix it.
>
>>   #define DPTX_SET_POWER_MNG                     0x00
>>   #define DPTX_SET_HOST_CAPABILITIES             0x01
>>   #define DPTX_GET_EDID                          0x02
>> @@ -469,8 +476,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>>   int cdn_dp_event_config(struct cdn_dp_device *dp);
>>   u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>>   int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>> -int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
>> -int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
>> +ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>> +                         u8 *data, u16 len);
>> +ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
>> +                        u8 *data, u16 len);
>> +int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
>>   int cdn_dp_get_edid_block(void *dp, u8 *edid,
>>                            unsigned int block, size_t length);
>>   int cdn_dp_train_link(struct cdn_dp_device *dp);
>> --
>> 2.7.4
>>
>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/4] phy: rockchip-typec: support variable phy config value
  2018-05-07 13:59     ` Enric Balletbo Serra
@ 2018-05-09  3:39       ` hl
  -1 siblings, 0 replies; 26+ messages in thread
From: hl @ 2018-05-09  3:39 UTC (permalink / raw)
  To: Enric Balletbo Serra
  Cc: Heiko Stübner, David Airlie, Brian Norris, Doug Anderson,
	jani.nikula, linux-kernel, open list:ARM/Rockchip SoC...,
	Sean Paul, dri-devel, Chris Zhong, daniel.vetter, Linux ARM



On Monday, May 07, 2018 09:59 PM, Enric Balletbo Serra wrote:
> Hi Lin,
>
> Thanks for the patch, apart from the new build warnings introduced
> some more comments below.
>
> 2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>> the phy config values used to fix in dp firmware, but some boards
>> need change these values to do training and get the better eye diagram
>> result. So support that in phy driver.
>>
>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>> ---
>>   drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++++++++++++++++++-----------
>>   include/soc/rockchip/rockchip_phy_typec.h |  72 ++++++++
>>   2 files changed, 259 insertions(+), 99 deletions(-)
>>   create mode 100644 include/soc/rockchip/rockchip_phy_typec.h
>>
>> diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
>> index 76a4b58..831a93b 100644
>> --- a/drivers/phy/rockchip/phy-rockchip-typec.c
>> +++ b/drivers/phy/rockchip/phy-rockchip-typec.c
>> @@ -63,6 +63,7 @@
>>
>>   #include <linux/mfd/syscon.h>
>>   #include <linux/phy/phy.h>
>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>
>>   #define CMN_SSM_BANDGAP                        (0x21 << 2)
>>   #define CMN_SSM_BIAS                   (0x22 << 2)
>> @@ -323,23 +324,31 @@
>>    * clock 0: PLL 0 div 1
>>    * clock 1: PLL 1 div 2
>>    */
>> -#define CLK_PLL_CONFIG                 0X30
>> +#define CLK_PLL1_DIV1                  0x20
>> +#define CLK_PLL1_DIV2                  0x30
>>   #define CLK_PLL_MASK                   0x33
>>
>>   #define CMN_READY                      BIT(0)
>>
>> +#define DP_PLL_CLOCK_ENABLE_ACK                BIT(3)
>>   #define DP_PLL_CLOCK_ENABLE            BIT(2)
>> +#define DP_PLL_ENABLE_ACK              BIT(1)
>>   #define DP_PLL_ENABLE                  BIT(0)
>>   #define DP_PLL_DATA_RATE_RBR           ((2 << 12) | (4 << 8))
>>   #define DP_PLL_DATA_RATE_HBR           ((2 << 12) | (4 << 8))
>>   #define DP_PLL_DATA_RATE_HBR2          ((1 << 12) | (2 << 8))
>> +#define DP_PLL_DATA_RATE_MASK          0xff00
>>
>> -#define DP_MODE_A0                     BIT(4)
>> -#define DP_MODE_A2                     BIT(6)
>> -#define DP_MODE_ENTER_A0               0xc101
>> -#define DP_MODE_ENTER_A2               0xc104
>> +#define DP_MODE_MASK                   0xf
>> +#define DP_MODE_ENTER_A0               BIT(0)
>> +#define DP_MODE_ENTER_A2               BIT(2)
>> +#define DP_MODE_ENTER_A3               BIT(3)
>> +#define DP_MODE_A0_ACK                 BIT(4)
>> +#define DP_MODE_A2_ACK                 BIT(6)
>> +#define DP_MODE_A3_ACK                 BIT(7)
>> +#define DP_LINK_RESET_DEASSERTED       BIT(8)
>>
>> -#define PHY_MODE_SET_TIMEOUT           100000
>> +#define PHY_MODE_SET_TIMEOUT           1000000
>>
> Why do you need to increase this timeout? Is because the software link
> training timed out using the old value?
That for debug, will fix it in next version.
>
>
>>   #define PIN_ASSIGN_C_E                 0x51d9
>>   #define PIN_ASSIGN_D_F                 0x5100
>> @@ -349,51 +358,7 @@
>>   #define MODE_DFP_USB                   BIT(1)
>>   #define MODE_DFP_DP                    BIT(2)
>>
>> -struct usb3phy_reg {
>> -       u32 offset;
>> -       u32 enable_bit;
>> -       u32 write_enable;
>> -};
>> -
>> -/**
>> - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
>> - * @reg: the base address for usb3-phy config.
>> - * @typec_conn_dir: the register of type-c connector direction.
>> - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
>> - * @external_psm: the register of type-c phy external psm clock.
>> - * @pipe_status: the register of type-c phy pipe status.
>> - * @usb3_host_disable: the register of type-c usb3 host disable.
>> - * @usb3_host_port: the register of type-c usb3 host port.
>> - * @uphy_dp_sel: the register of type-c phy DP select control.
>> - */
>> -struct rockchip_usb3phy_port_cfg {
>> -       unsigned int reg;
>> -       struct usb3phy_reg typec_conn_dir;
>> -       struct usb3phy_reg usb3tousb2_en;
>> -       struct usb3phy_reg external_psm;
>> -       struct usb3phy_reg pipe_status;
>> -       struct usb3phy_reg usb3_host_disable;
>> -       struct usb3phy_reg usb3_host_port;
>> -       struct usb3phy_reg uphy_dp_sel;
>> -};
>> -
>> -struct rockchip_typec_phy {
>> -       struct device *dev;
>> -       void __iomem *base;
>> -       struct extcon_dev *extcon;
>> -       struct regmap *grf_regs;
>> -       struct clk *clk_core;
>> -       struct clk *clk_ref;
>> -       struct reset_control *uphy_rst;
>> -       struct reset_control *pipe_rst;
>> -       struct reset_control *tcphy_rst;
>> -       const struct rockchip_usb3phy_port_cfg *port_cfgs;
>> -       /* mutex to protect access to individual PHYs */
>> -       struct mutex lock;
>> -
>> -       bool flip;
>> -       u8 mode;
>> -};
>> +#define DEFAULT_RATE                   162000
>>
> DEFAULT_RATE seems a very common name for me, maybe add a prefix?
Okay, will fix it.
>
>
>>   struct phy_reg {
>>          u16 value;
>> @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
>>          { 0x8,          CMN_DIAG_PLL0_LF_PROG },
>>   };
>>
>> -struct phy_reg dp_pll_cfg[] = {
>> +struct phy_reg dp_pll_rbr_cfg[] = {
>>          { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>>          { 0x18,         CMN_PLL1_VCOCAL_ITER },
>>          { 0x30b9,       CMN_PLL1_VCOCAL_START },
>> -       { 0x21c,        CMN_PLL1_INTDIV },
>> +       { 0x87,         CMN_PLL1_INTDIV },
>>          { 0,            CMN_PLL1_FRACDIV },
>> -       { 0x5,          CMN_PLL1_HIGH_THR },
>> -       { 0x35,         CMN_PLL1_SS_CTRL1 },
>> -       { 0x7f1e,       CMN_PLL1_SS_CTRL2 },
>> +       { 0x22,         CMN_PLL1_HIGH_THR },
>> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
>> +       { 0,            CMN_PLL1_SS_CTRL2 },
>>          { 0x20,         CMN_PLL1_DSM_DIAG },
>>          { 0,            CMN_PLLSM1_USER_DEF_CTRL },
>>          { 0,            CMN_DIAG_PLL1_OVRD },
>> @@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
>>          { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>>          { 0x100,        CMN_DIAG_PLL1_PTATIS_TUNE1 },
>>          { 0x7,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
>> -       { 0x4,          CMN_DIAG_PLL1_INCLK_CTRL },
>> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>> +};
>> +
>> +struct phy_reg dp_pll_hbr_cfg[] = {
>> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
>> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
>> +       { 0xe1,         CMN_PLL1_INTDIV },
>> +       { 0,            CMN_PLL1_FRACDIV },
>> +       { 0x5,          CMN_PLL1_HIGH_THR },
>> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
>> +       { 0,            CMN_PLL1_SS_CTRL2 },
>> +       { 0x20,         CMN_PLL1_DSM_DIAG },
>> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
>> +       { 0,            CMN_DIAG_PLL1_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
>> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
>> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
>> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
>> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>>   };
>>
>> +struct phy_reg dp_pll_hbr2_cfg[] = {
>> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
>> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
>> +       { 0xe1,         CMN_PLL1_INTDIV },
>> +       { 0,            CMN_PLL1_FRACDIV },
>> +       { 0x5,          CMN_PLL1_HIGH_THR },
>> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
>> +       { 0,            CMN_PLL1_SS_CTRL2 },
>> +       { 0x20,         CMN_PLL1_DSM_DIAG },
>> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
>> +       { 0,            CMN_DIAG_PLL1_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
>> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
>> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
>> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
>> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>> +};
>>   static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
>>          {
>>                  .reg = 0xff7c0000,
>> @@ -484,7 +492,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
>>
>>          rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
>>          rdata &= ~CLK_PLL_MASK;
>> -       rdata |= CLK_PLL_CONFIG;
>> +       rdata |= CLK_PLL1_DIV2;
>>          writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
>>   }
>>
>> @@ -498,17 +506,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
>>                         tcphy->base + usb3_pll_cfg[i].addr);
>>   }
>>
>> -static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
>> +static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
>>   {
>> -       u32 i;
>> +       struct phy_reg *phy_cfg;
>> +       u32 clk_ctrl;
>> +       u32 i, cfg_size, hsclk_sel;
>> +
>> +       hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
>> +       hsclk_sel &= ~CLK_PLL_MASK;
>> +
>> +       switch (link_rate) {
>> +       case 162000:
>> +               clk_ctrl = DP_PLL_DATA_RATE_RBR;
>> +               hsclk_sel |= CLK_PLL1_DIV2;
>> +               phy_cfg = dp_pll_rbr_cfg;
>> +               cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
>> +               break;
>> +       case 270000:
>> +               clk_ctrl = DP_PLL_DATA_RATE_HBR;
>> +               hsclk_sel |= CLK_PLL1_DIV2;
>> +               phy_cfg = dp_pll_hbr_cfg;
>> +               cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
>> +               break;
>> +       case 540000:
>> +               clk_ctrl = DP_PLL_DATA_RATE_HBR2;
>> +               hsclk_sel |= CLK_PLL1_DIV1;
>> +               phy_cfg = dp_pll_hbr2_cfg;
>> +               cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
>> +               break;
>> +       }
>> +
>> +       clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
>> +       writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
>>
>> -       /* set the default mode to RBR */
>> -       writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
>> -              tcphy->base + DP_CLK_CTL);
>> +       writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
>>
>>          /* load the configuration of PLL1 */
>> -       for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
>> -               writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
>> +       for (i = 0; i < cfg_size; i++)
>> +               writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
>>   }
>>
>>   static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>> @@ -535,9 +570,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>>          writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
>>   }
>>
>> -static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>> +static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
>> +                             u8 swing, u8 pre_emp, u32 lane)
>>   {
>> -       u16 rdata;
>> +       u16 val;
> >From what I see you are only renaming rdata to val, there is any reason?
not reason, just rename a new variable :)
>
>>          writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
>>          writel(0x6799, tcphy->base + TX_PSC_A0(lane));
>> @@ -545,25 +581,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>>          writel(0x98, tcphy->base + TX_PSC_A2(lane));
>>          writel(0x98, tcphy->base + TX_PSC_A3(lane));
>>
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
>> -
>> -       writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
>> -       writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
>> -
>> -       rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>> -       rdata = (rdata & 0x8fff) | 0x6000;
>> -       writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>> +       writel(tcphy->config[swing][pre_emp].swing,
>> +              tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
>> +       writel(tcphy->config[swing][pre_emp].pe,
>> +              tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
>> +
>> +       if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
>> +               writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
>> +               writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
>> +       } else {
>> +               writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
>> +               writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
>> +       }
>> +
>> +       val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>> +       val = val & 0x8fff;
>> +       switch (link_rate) {
>> +       case 162000:
>> +       case 270000:
>> +               val |= (6 << 12);
>> +               break;
>> +       case 540000:
>> +               val |= (4 << 12);
>> +               break;
>> +       }
>> +       writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>>   }
>>
>>   static inline int property_enable(struct rockchip_typec_phy *tcphy,
>> @@ -754,30 +796,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
>>          tcphy_cfg_24m(tcphy);
>>
>>          if (mode == MODE_DFP_DP) {
>> -               tcphy_cfg_dp_pll(tcphy);
>> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>>                  for (i = 0; i < 4; i++)
>> -                       tcphy_dp_cfg_lane(tcphy, i);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, i);
>>
>>                  writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
>>          } else {
>>                  tcphy_cfg_usb3_pll(tcphy);
>> -               tcphy_cfg_dp_pll(tcphy);
>> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>>                  if (tcphy->flip) {
>>                          tcphy_tx_usb3_cfg_lane(tcphy, 3);
>>                          tcphy_rx_usb3_cfg_lane(tcphy, 2);
>> -                       tcphy_dp_cfg_lane(tcphy, 0);
>> -                       tcphy_dp_cfg_lane(tcphy, 1);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 0);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 1);
>>                  } else {
>>                          tcphy_tx_usb3_cfg_lane(tcphy, 0);
>>                          tcphy_rx_usb3_cfg_lane(tcphy, 1);
>> -                       tcphy_dp_cfg_lane(tcphy, 2);
>> -                       tcphy_dp_cfg_lane(tcphy, 3);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 2);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 3);
>>                  }
>>
>>                  writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
>>          }
>>
>> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
>> +       val = readl(tcphy->base + DP_MODE_CTL);
>> +       val &= ~DP_MODE_MASK;
>> +       val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
>> +       writel(val, tcphy->base + DP_MODE_CTL);
>>
>>          reset_control_deassert(tcphy->uphy_rst);
>>
>> @@ -990,7 +1035,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>>          property_enable(tcphy, &cfg->uphy_dp_sel, 1);
>>
>>          ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
>> -                                val, val & DP_MODE_A2, 1000,
>> +                                val, val & DP_MODE_A2_ACK, 1000,
>>                                   PHY_MODE_SET_TIMEOUT);
>>          if (ret < 0) {
>>                  dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
>> @@ -999,13 +1044,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>>
>>          tcphy_dp_aux_calibration(tcphy);
>>
>> -       writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
>> +       /* enter A0 mode */
>> +       val = readl(tcphy->base + DP_MODE_CTL);
>> +       val &= ~DP_MODE_MASK;
>> +       val |= DP_MODE_ENTER_A0;
>> +       writel(val, tcphy->base + DP_MODE_CTL);
>>
>>          ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
>> -                                val, val & DP_MODE_A0, 1000,
>> +                                val, val & DP_MODE_A0_ACK, 1000,
>>                                   PHY_MODE_SET_TIMEOUT);
>>          if (ret < 0) {
>> -               writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
>> +               val &= ~DP_MODE_MASK;
>> +               val |= DP_MODE_ENTER_A2;
>> +               writel(val, tcphy->base + DP_MODE_CTL);
>>                  dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
>>                  goto power_on_finish;
>>          }
>> @@ -1023,6 +1074,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>>   static int rockchip_dp_phy_power_off(struct phy *phy)
>>   {
>>          struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
>> +       u32 val;
>>
>>          mutex_lock(&tcphy->lock);
>>
>> @@ -1031,7 +1083,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
>>
>>          tcphy->mode &= ~MODE_DFP_DP;
>>
>> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
>> +       val = readl(tcphy->base + DP_MODE_CTL);
>> +       val &= ~DP_MODE_MASK;
>> +       val |= DP_MODE_ENTER_A2;
>> +       writel(val, tcphy->base + DP_MODE_CTL);
>>
>>          if (tcphy->mode == MODE_DISCONNECT)
>>                  tcphy_phy_deinit(tcphy);
>> @@ -1047,6 +1102,30 @@ static const struct phy_ops rockchip_dp_phy_ops = {
>>          .owner          = THIS_MODULE,
>>   };
>>
>> +static int type_c_dp_phy_config(struct phy *phy, int link_rate,
> s/type_c/typec/ to be coherent with the rest of the code.
>
>> +                        int lanes, u8 swing, u8 pre_emp)
>> +{
>> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
>> +       u8 i;
>> +
>> +       tcphy_cfg_dp_pll(tcphy, link_rate);
>> +
>> +       if (tcphy->mode == MODE_DFP_DP) {
>> +               for (i = 0; i < 4; i++)
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
>> +       } else {
>> +               if (tcphy->flip) {
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
>> +               } else {
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>>   static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>>                            struct device *dev)
>>   {
>> @@ -1087,6 +1166,14 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>>                  return PTR_ERR(tcphy->tcphy_rst);
>>          }
>>
>> +       /*
>> +        * check if phy_config pass from dts, if yes,
>> +        * need to use this phy config to do software training later
>> +        */
>> +       if (!of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
>> +               (u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32)))
>> +               tcphy->need_software_training = 1;
>> +
>>          return 0;
>>   }
>>
>> @@ -1171,6 +1258,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
>>                  }
>>          }
>>
>> +       tcphy->typec_phy_config = type_c_dp_phy_config;
> type_c_dp_phy_config -> typec_dp_phy_config
>
>>          pm_runtime_enable(dev);
>>
>>          for_each_available_child_of_node(np, child_np) {
>> diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
>> new file mode 100644
>> index 0000000..e25840e
>> --- /dev/null
>> +++ b/include/soc/rockchip/rockchip_phy_typec.h
>> @@ -0,0 +1,72 @@
>> +/*
> Add the SPDX License identifier ...
>
>> + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd
>> + * Author: Lin Huang <hl@rock-chips.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> + * more details.
> ... and remove the above notice.
>
>> + */
>> +#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
>> +#define __SOC_ROCKCHIP_PHY_TYPEC_H
>> +
>> +
> Remove the extra new line.
>
>> +struct usb3phy_reg {
>> +       u32 offset;
>> +       u32 enable_bit;
>> +       u32 write_enable;
>> +};
>> +
>> +/**
>> + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
>> + * @reg: the base address for usb3-phy config.
>> + * @typec_conn_dir: the register of type-c connector direction.
>> + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
>> + * @external_psm: the register of type-c phy external psm clock.
>> + * @pipe_status: the register of type-c phy pipe status.
>> + * @usb3_host_disable: the register of type-c usb3 host disable.
>> + * @usb3_host_port: the register of type-c usb3 host port.
>> + * @uphy_dp_sel: the register of type-c phy DP select control.
>> + */
>> +struct rockchip_usb3phy_port_cfg {
>> +       unsigned int reg;
>> +       struct usb3phy_reg typec_conn_dir;
>> +       struct usb3phy_reg usb3tousb2_en;
>> +       struct usb3phy_reg external_psm;
>> +       struct usb3phy_reg pipe_status;
>> +       struct usb3phy_reg usb3_host_disable;
>> +       struct usb3phy_reg usb3_host_port;
>> +       struct usb3phy_reg uphy_dp_sel;
>> +};
>> +
>> +struct phy_config {
>> +       int swing;
>> +       int pe;
>> +};
>> +
>> +struct rockchip_typec_phy {
>> +       struct device *dev;
>> +       void __iomem *base;
>> +       struct extcon_dev *extcon;
>> +       struct regmap *grf_regs;
>> +       struct clk *clk_core;
>> +       struct clk *clk_ref;
>> +       struct reset_control *uphy_rst;
>> +       struct reset_control *pipe_rst;
>> +       struct reset_control *tcphy_rst;
>> +       struct rockchip_usb3phy_port_cfg *port_cfgs;
>> +       /* mutex to protect access to individual PHYs */
>> +       struct mutex lock;
>> +       struct phy_config config[3][4];
>> +       u8 need_software_training;
>> +       bool flip;
>> +       u8 mode;
>> +       int (*typec_phy_config)(struct phy *phy, int link_rate,
>> +                               int lanes, u8 swing, u8 pre_emp);
>> +};
>> +
>> +#endif
> Best regards,
>   Enric
>
>> --
>> 2.7.4
>>
>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 2/4] phy: rockchip-typec: support variable phy config value
@ 2018-05-09  3:39       ` hl
  0 siblings, 0 replies; 26+ messages in thread
From: hl @ 2018-05-09  3:39 UTC (permalink / raw)
  To: linux-arm-kernel



On Monday, May 07, 2018 09:59 PM, Enric Balletbo Serra wrote:
> Hi Lin,
>
> Thanks for the patch, apart from the new build warnings introduced
> some more comments below.
>
> 2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>> the phy config values used to fix in dp firmware, but some boards
>> need change these values to do training and get the better eye diagram
>> result. So support that in phy driver.
>>
>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>> ---
>>   drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++++++++++++++++++-----------
>>   include/soc/rockchip/rockchip_phy_typec.h |  72 ++++++++
>>   2 files changed, 259 insertions(+), 99 deletions(-)
>>   create mode 100644 include/soc/rockchip/rockchip_phy_typec.h
>>
>> diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
>> index 76a4b58..831a93b 100644
>> --- a/drivers/phy/rockchip/phy-rockchip-typec.c
>> +++ b/drivers/phy/rockchip/phy-rockchip-typec.c
>> @@ -63,6 +63,7 @@
>>
>>   #include <linux/mfd/syscon.h>
>>   #include <linux/phy/phy.h>
>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>
>>   #define CMN_SSM_BANDGAP                        (0x21 << 2)
>>   #define CMN_SSM_BIAS                   (0x22 << 2)
>> @@ -323,23 +324,31 @@
>>    * clock 0: PLL 0 div 1
>>    * clock 1: PLL 1 div 2
>>    */
>> -#define CLK_PLL_CONFIG                 0X30
>> +#define CLK_PLL1_DIV1                  0x20
>> +#define CLK_PLL1_DIV2                  0x30
>>   #define CLK_PLL_MASK                   0x33
>>
>>   #define CMN_READY                      BIT(0)
>>
>> +#define DP_PLL_CLOCK_ENABLE_ACK                BIT(3)
>>   #define DP_PLL_CLOCK_ENABLE            BIT(2)
>> +#define DP_PLL_ENABLE_ACK              BIT(1)
>>   #define DP_PLL_ENABLE                  BIT(0)
>>   #define DP_PLL_DATA_RATE_RBR           ((2 << 12) | (4 << 8))
>>   #define DP_PLL_DATA_RATE_HBR           ((2 << 12) | (4 << 8))
>>   #define DP_PLL_DATA_RATE_HBR2          ((1 << 12) | (2 << 8))
>> +#define DP_PLL_DATA_RATE_MASK          0xff00
>>
>> -#define DP_MODE_A0                     BIT(4)
>> -#define DP_MODE_A2                     BIT(6)
>> -#define DP_MODE_ENTER_A0               0xc101
>> -#define DP_MODE_ENTER_A2               0xc104
>> +#define DP_MODE_MASK                   0xf
>> +#define DP_MODE_ENTER_A0               BIT(0)
>> +#define DP_MODE_ENTER_A2               BIT(2)
>> +#define DP_MODE_ENTER_A3               BIT(3)
>> +#define DP_MODE_A0_ACK                 BIT(4)
>> +#define DP_MODE_A2_ACK                 BIT(6)
>> +#define DP_MODE_A3_ACK                 BIT(7)
>> +#define DP_LINK_RESET_DEASSERTED       BIT(8)
>>
>> -#define PHY_MODE_SET_TIMEOUT           100000
>> +#define PHY_MODE_SET_TIMEOUT           1000000
>>
> Why do you need to increase this timeout? Is because the software link
> training timed out using the old value?
That for debug, will fix it in next version.
>
>
>>   #define PIN_ASSIGN_C_E                 0x51d9
>>   #define PIN_ASSIGN_D_F                 0x5100
>> @@ -349,51 +358,7 @@
>>   #define MODE_DFP_USB                   BIT(1)
>>   #define MODE_DFP_DP                    BIT(2)
>>
>> -struct usb3phy_reg {
>> -       u32 offset;
>> -       u32 enable_bit;
>> -       u32 write_enable;
>> -};
>> -
>> -/**
>> - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
>> - * @reg: the base address for usb3-phy config.
>> - * @typec_conn_dir: the register of type-c connector direction.
>> - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
>> - * @external_psm: the register of type-c phy external psm clock.
>> - * @pipe_status: the register of type-c phy pipe status.
>> - * @usb3_host_disable: the register of type-c usb3 host disable.
>> - * @usb3_host_port: the register of type-c usb3 host port.
>> - * @uphy_dp_sel: the register of type-c phy DP select control.
>> - */
>> -struct rockchip_usb3phy_port_cfg {
>> -       unsigned int reg;
>> -       struct usb3phy_reg typec_conn_dir;
>> -       struct usb3phy_reg usb3tousb2_en;
>> -       struct usb3phy_reg external_psm;
>> -       struct usb3phy_reg pipe_status;
>> -       struct usb3phy_reg usb3_host_disable;
>> -       struct usb3phy_reg usb3_host_port;
>> -       struct usb3phy_reg uphy_dp_sel;
>> -};
>> -
>> -struct rockchip_typec_phy {
>> -       struct device *dev;
>> -       void __iomem *base;
>> -       struct extcon_dev *extcon;
>> -       struct regmap *grf_regs;
>> -       struct clk *clk_core;
>> -       struct clk *clk_ref;
>> -       struct reset_control *uphy_rst;
>> -       struct reset_control *pipe_rst;
>> -       struct reset_control *tcphy_rst;
>> -       const struct rockchip_usb3phy_port_cfg *port_cfgs;
>> -       /* mutex to protect access to individual PHYs */
>> -       struct mutex lock;
>> -
>> -       bool flip;
>> -       u8 mode;
>> -};
>> +#define DEFAULT_RATE                   162000
>>
> DEFAULT_RATE seems a very common name for me, maybe add a prefix?
Okay, will fix it.
>
>
>>   struct phy_reg {
>>          u16 value;
>> @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = {
>>          { 0x8,          CMN_DIAG_PLL0_LF_PROG },
>>   };
>>
>> -struct phy_reg dp_pll_cfg[] = {
>> +struct phy_reg dp_pll_rbr_cfg[] = {
>>          { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>>          { 0x18,         CMN_PLL1_VCOCAL_ITER },
>>          { 0x30b9,       CMN_PLL1_VCOCAL_START },
>> -       { 0x21c,        CMN_PLL1_INTDIV },
>> +       { 0x87,         CMN_PLL1_INTDIV },
>>          { 0,            CMN_PLL1_FRACDIV },
>> -       { 0x5,          CMN_PLL1_HIGH_THR },
>> -       { 0x35,         CMN_PLL1_SS_CTRL1 },
>> -       { 0x7f1e,       CMN_PLL1_SS_CTRL2 },
>> +       { 0x22,         CMN_PLL1_HIGH_THR },
>> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
>> +       { 0,            CMN_PLL1_SS_CTRL2 },
>>          { 0x20,         CMN_PLL1_DSM_DIAG },
>>          { 0,            CMN_PLLSM1_USER_DEF_CTRL },
>>          { 0,            CMN_DIAG_PLL1_OVRD },
>> @@ -436,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = {
>>          { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>>          { 0x100,        CMN_DIAG_PLL1_PTATIS_TUNE1 },
>>          { 0x7,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
>> -       { 0x4,          CMN_DIAG_PLL1_INCLK_CTRL },
>> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>> +};
>> +
>> +struct phy_reg dp_pll_hbr_cfg[] = {
>> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
>> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
>> +       { 0xe1,         CMN_PLL1_INTDIV },
>> +       { 0,            CMN_PLL1_FRACDIV },
>> +       { 0x5,          CMN_PLL1_HIGH_THR },
>> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
>> +       { 0,            CMN_PLL1_SS_CTRL2 },
>> +       { 0x20,         CMN_PLL1_DSM_DIAG },
>> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
>> +       { 0,            CMN_DIAG_PLL1_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
>> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
>> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
>> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
>> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>>   };
>>
>> +struct phy_reg dp_pll_hbr2_cfg[] = {
>> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
>> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
>> +       { 0x30b4,       CMN_PLL1_VCOCAL_START },
>> +       { 0xe1,         CMN_PLL1_INTDIV },
>> +       { 0,            CMN_PLL1_FRACDIV },
>> +       { 0x5,          CMN_PLL1_HIGH_THR },
>> +       { 0x8000,       CMN_PLL1_SS_CTRL1 },
>> +       { 0,            CMN_PLL1_SS_CTRL2 },
>> +       { 0x20,         CMN_PLL1_DSM_DIAG },
>> +       { 0x1000,       CMN_PLLSM1_USER_DEF_CTRL },
>> +       { 0,            CMN_DIAG_PLL1_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
>> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
>> +       { 0x7,          CMN_DIAG_PLL1_V2I_TUNE },
>> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
>> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE1 },
>> +       { 0x1,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
>> +       { 0x1,          CMN_DIAG_PLL1_INCLK_CTRL },
>> +};
>>   static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
>>          {
>>                  .reg = 0xff7c0000,
>> @@ -484,7 +492,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
>>
>>          rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
>>          rdata &= ~CLK_PLL_MASK;
>> -       rdata |= CLK_PLL_CONFIG;
>> +       rdata |= CLK_PLL1_DIV2;
>>          writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
>>   }
>>
>> @@ -498,17 +506,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
>>                         tcphy->base + usb3_pll_cfg[i].addr);
>>   }
>>
>> -static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
>> +static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
>>   {
>> -       u32 i;
>> +       struct phy_reg *phy_cfg;
>> +       u32 clk_ctrl;
>> +       u32 i, cfg_size, hsclk_sel;
>> +
>> +       hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
>> +       hsclk_sel &= ~CLK_PLL_MASK;
>> +
>> +       switch (link_rate) {
>> +       case 162000:
>> +               clk_ctrl = DP_PLL_DATA_RATE_RBR;
>> +               hsclk_sel |= CLK_PLL1_DIV2;
>> +               phy_cfg = dp_pll_rbr_cfg;
>> +               cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
>> +               break;
>> +       case 270000:
>> +               clk_ctrl = DP_PLL_DATA_RATE_HBR;
>> +               hsclk_sel |= CLK_PLL1_DIV2;
>> +               phy_cfg = dp_pll_hbr_cfg;
>> +               cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
>> +               break;
>> +       case 540000:
>> +               clk_ctrl = DP_PLL_DATA_RATE_HBR2;
>> +               hsclk_sel |= CLK_PLL1_DIV1;
>> +               phy_cfg = dp_pll_hbr2_cfg;
>> +               cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
>> +               break;
>> +       }
>> +
>> +       clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
>> +       writel(clk_ctrl, tcphy->base + DP_CLK_CTL);
>>
>> -       /* set the default mode to RBR */
>> -       writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
>> -              tcphy->base + DP_CLK_CTL);
>> +       writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
>>
>>          /* load the configuration of PLL1 */
>> -       for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
>> -               writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
>> +       for (i = 0; i < cfg_size; i++)
>> +               writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
>>   }
>>
>>   static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>> @@ -535,9 +570,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>>          writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
>>   }
>>
>> -static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>> +static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
>> +                             u8 swing, u8 pre_emp, u32 lane)
>>   {
>> -       u16 rdata;
>> +       u16 val;
> >From what I see you are only renaming rdata to val, there is any reason?
not reason, just rename a new variable :)
>
>>          writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
>>          writel(0x6799, tcphy->base + TX_PSC_A0(lane));
>> @@ -545,25 +581,31 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
>>          writel(0x98, tcphy->base + TX_PSC_A2(lane));
>>          writel(0x98, tcphy->base + TX_PSC_A3(lane));
>>
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
>> -       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
>> -       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
>> -
>> -       writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
>> -       writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
>> -
>> -       rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>> -       rdata = (rdata & 0x8fff) | 0x6000;
>> -       writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>> +       writel(tcphy->config[swing][pre_emp].swing,
>> +              tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
>> +       writel(tcphy->config[swing][pre_emp].pe,
>> +              tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
>> +
>> +       if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
>> +               writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
>> +               writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
>> +       } else {
>> +               writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
>> +               writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
>> +       }
>> +
>> +       val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>> +       val = val & 0x8fff;
>> +       switch (link_rate) {
>> +       case 162000:
>> +       case 270000:
>> +               val |= (6 << 12);
>> +               break;
>> +       case 540000:
>> +               val |= (4 << 12);
>> +               break;
>> +       }
>> +       writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
>>   }
>>
>>   static inline int property_enable(struct rockchip_typec_phy *tcphy,
>> @@ -754,30 +796,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
>>          tcphy_cfg_24m(tcphy);
>>
>>          if (mode == MODE_DFP_DP) {
>> -               tcphy_cfg_dp_pll(tcphy);
>> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>>                  for (i = 0; i < 4; i++)
>> -                       tcphy_dp_cfg_lane(tcphy, i);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, i);
>>
>>                  writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
>>          } else {
>>                  tcphy_cfg_usb3_pll(tcphy);
>> -               tcphy_cfg_dp_pll(tcphy);
>> +               tcphy_cfg_dp_pll(tcphy, DEFAULT_RATE);
>>                  if (tcphy->flip) {
>>                          tcphy_tx_usb3_cfg_lane(tcphy, 3);
>>                          tcphy_rx_usb3_cfg_lane(tcphy, 2);
>> -                       tcphy_dp_cfg_lane(tcphy, 0);
>> -                       tcphy_dp_cfg_lane(tcphy, 1);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 0);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 1);
>>                  } else {
>>                          tcphy_tx_usb3_cfg_lane(tcphy, 0);
>>                          tcphy_rx_usb3_cfg_lane(tcphy, 1);
>> -                       tcphy_dp_cfg_lane(tcphy, 2);
>> -                       tcphy_dp_cfg_lane(tcphy, 3);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 2);
>> +                       tcphy_dp_cfg_lane(tcphy, DEFAULT_RATE, 0, 0, 3);
>>                  }
>>
>>                  writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
>>          }
>>
>> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
>> +       val = readl(tcphy->base + DP_MODE_CTL);
>> +       val &= ~DP_MODE_MASK;
>> +       val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
>> +       writel(val, tcphy->base + DP_MODE_CTL);
>>
>>          reset_control_deassert(tcphy->uphy_rst);
>>
>> @@ -990,7 +1035,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>>          property_enable(tcphy, &cfg->uphy_dp_sel, 1);
>>
>>          ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
>> -                                val, val & DP_MODE_A2, 1000,
>> +                                val, val & DP_MODE_A2_ACK, 1000,
>>                                   PHY_MODE_SET_TIMEOUT);
>>          if (ret < 0) {
>>                  dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
>> @@ -999,13 +1044,19 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>>
>>          tcphy_dp_aux_calibration(tcphy);
>>
>> -       writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
>> +       /* enter A0 mode */
>> +       val = readl(tcphy->base + DP_MODE_CTL);
>> +       val &= ~DP_MODE_MASK;
>> +       val |= DP_MODE_ENTER_A0;
>> +       writel(val, tcphy->base + DP_MODE_CTL);
>>
>>          ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
>> -                                val, val & DP_MODE_A0, 1000,
>> +                                val, val & DP_MODE_A0_ACK, 1000,
>>                                   PHY_MODE_SET_TIMEOUT);
>>          if (ret < 0) {
>> -               writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
>> +               val &= ~DP_MODE_MASK;
>> +               val |= DP_MODE_ENTER_A2;
>> +               writel(val, tcphy->base + DP_MODE_CTL);
>>                  dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
>>                  goto power_on_finish;
>>          }
>> @@ -1023,6 +1074,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
>>   static int rockchip_dp_phy_power_off(struct phy *phy)
>>   {
>>          struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
>> +       u32 val;
>>
>>          mutex_lock(&tcphy->lock);
>>
>> @@ -1031,7 +1083,10 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
>>
>>          tcphy->mode &= ~MODE_DFP_DP;
>>
>> -       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
>> +       val = readl(tcphy->base + DP_MODE_CTL);
>> +       val &= ~DP_MODE_MASK;
>> +       val |= DP_MODE_ENTER_A2;
>> +       writel(val, tcphy->base + DP_MODE_CTL);
>>
>>          if (tcphy->mode == MODE_DISCONNECT)
>>                  tcphy_phy_deinit(tcphy);
>> @@ -1047,6 +1102,30 @@ static const struct phy_ops rockchip_dp_phy_ops = {
>>          .owner          = THIS_MODULE,
>>   };
>>
>> +static int type_c_dp_phy_config(struct phy *phy, int link_rate,
> s/type_c/typec/ to be coherent with the rest of the code.
>
>> +                        int lanes, u8 swing, u8 pre_emp)
>> +{
>> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
>> +       u8 i;
>> +
>> +       tcphy_cfg_dp_pll(tcphy, link_rate);
>> +
>> +       if (tcphy->mode == MODE_DFP_DP) {
>> +               for (i = 0; i < 4; i++)
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
>> +       } else {
>> +               if (tcphy->flip) {
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
>> +               } else {
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
>> +                       tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>>   static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>>                            struct device *dev)
>>   {
>> @@ -1087,6 +1166,14 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
>>                  return PTR_ERR(tcphy->tcphy_rst);
>>          }
>>
>> +       /*
>> +        * check if phy_config pass from dts, if yes,
>> +        * need to use this phy config to do software training later
>> +        */
>> +       if (!of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
>> +               (u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32)))
>> +               tcphy->need_software_training = 1;
>> +
>>          return 0;
>>   }
>>
>> @@ -1171,6 +1258,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
>>                  }
>>          }
>>
>> +       tcphy->typec_phy_config = type_c_dp_phy_config;
> type_c_dp_phy_config -> typec_dp_phy_config
>
>>          pm_runtime_enable(dev);
>>
>>          for_each_available_child_of_node(np, child_np) {
>> diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h
>> new file mode 100644
>> index 0000000..e25840e
>> --- /dev/null
>> +++ b/include/soc/rockchip/rockchip_phy_typec.h
>> @@ -0,0 +1,72 @@
>> +/*
> Add the SPDX License identifier ...
>
>> + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd
>> + * Author: Lin Huang <hl@rock-chips.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> + * more details.
> ... and remove the above notice.
>
>> + */
>> +#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H
>> +#define __SOC_ROCKCHIP_PHY_TYPEC_H
>> +
>> +
> Remove the extra new line.
>
>> +struct usb3phy_reg {
>> +       u32 offset;
>> +       u32 enable_bit;
>> +       u32 write_enable;
>> +};
>> +
>> +/**
>> + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
>> + * @reg: the base address for usb3-phy config.
>> + * @typec_conn_dir: the register of type-c connector direction.
>> + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
>> + * @external_psm: the register of type-c phy external psm clock.
>> + * @pipe_status: the register of type-c phy pipe status.
>> + * @usb3_host_disable: the register of type-c usb3 host disable.
>> + * @usb3_host_port: the register of type-c usb3 host port.
>> + * @uphy_dp_sel: the register of type-c phy DP select control.
>> + */
>> +struct rockchip_usb3phy_port_cfg {
>> +       unsigned int reg;
>> +       struct usb3phy_reg typec_conn_dir;
>> +       struct usb3phy_reg usb3tousb2_en;
>> +       struct usb3phy_reg external_psm;
>> +       struct usb3phy_reg pipe_status;
>> +       struct usb3phy_reg usb3_host_disable;
>> +       struct usb3phy_reg usb3_host_port;
>> +       struct usb3phy_reg uphy_dp_sel;
>> +};
>> +
>> +struct phy_config {
>> +       int swing;
>> +       int pe;
>> +};
>> +
>> +struct rockchip_typec_phy {
>> +       struct device *dev;
>> +       void __iomem *base;
>> +       struct extcon_dev *extcon;
>> +       struct regmap *grf_regs;
>> +       struct clk *clk_core;
>> +       struct clk *clk_ref;
>> +       struct reset_control *uphy_rst;
>> +       struct reset_control *pipe_rst;
>> +       struct reset_control *tcphy_rst;
>> +       struct rockchip_usb3phy_port_cfg *port_cfgs;
>> +       /* mutex to protect access to individual PHYs */
>> +       struct mutex lock;
>> +       struct phy_config config[3][4];
>> +       u8 need_software_training;
>> +       bool flip;
>> +       u8 mode;
>> +       int (*typec_phy_config)(struct phy *phy, int link_rate,
>> +                               int lanes, u8 swing, u8 pre_emp);
>> +};
>> +
>> +#endif
> Best regards,
>   Enric
>
>> --
>> 2.7.4
>>
>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 4/4] drm/rockchip: support dp training outside dp firmware
  2018-05-07 11:29     ` Enric Balletbo Serra
@ 2018-05-09  9:47       ` hl
  -1 siblings, 0 replies; 26+ messages in thread
From: hl @ 2018-05-09  9:47 UTC (permalink / raw)
  To: Enric Balletbo Serra
  Cc: Sean Paul, David Airlie, Chris Zhong, Doug Anderson,
	Brian Norris, open list:ARM/Rockchip SoC...,
	Heiko Stübner, daniel.vetter, jani.nikula, dri-devel,
	Linux ARM, linux-kernel



On Monday, May 07, 2018 07:29 PM, Enric Balletbo Serra wrote:
> Hi Lin,
>
> Thanks for the patch.
>
> 2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>> DP firware use fix phy config value to do training, but some
> s/fiware/firmware/
>
>> board need to adjust these value to fit for their hardware design,
>> so we use new phy config to do training outside firmware to meet
>> this situation, if there have new phy config pass from dts, it will
>> use training outside firmware.
>>
> maybe you can rewrite all this in a better way.
>
> ooi, which boards needs this?
>
>
>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>> ---
>>   drivers/gpu/drm/rockchip/Makefile               |   3 +-
>>   drivers/gpu/drm/rockchip/cdn-dp-core.c          |  23 +-
>>   drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
>>   drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 ++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  33 +-
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
>>   6 files changed, 480 insertions(+), 17 deletions(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index a314e21..b932f62 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>
>>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>> +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
>> +                                       cdn-dp-link-training.o
>>   rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>>   rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>>   rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> index 268c190..a2a4208 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>>                          goto out;
>>                  }
>>          }
>> -
>> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>> -       if (ret) {
>> -               DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
>> -               goto out;
>> +       if (dp->sw_training_success == false) {
>> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>> +               if (ret) {
>> +                       DRM_DEV_ERROR(dp->dev,
>> +                                     "Failed to idle video %d\n", ret);
>> +                       goto out;
>> +               }
>>          }
>>
>>          ret = cdn_dp_config_video(dp);
>> @@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>>                  goto out;
>>          }
>>
>> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>> -       if (ret) {
>> -               DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
>> -               goto out;
>> +       if (dp->sw_training_success == false) {
>> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>> +               if (ret) {
>> +                       DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
>> +                       goto out;
>> +               }
>>          }
>> +
>>   out:
>>          mutex_unlock(&dp->lock);
>>   }
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> index 46159b2..c6050ab 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> @@ -84,6 +84,7 @@ struct cdn_dp_device {
>>          bool connected;
>>          bool active;
>>          bool suspended;
>> +       bool sw_training_success;
>>
>>          const struct firmware *fw;      /* cdn dp firmware */
>>          unsigned int fw_version;        /* cdn fw version */
>> @@ -106,6 +107,7 @@ struct cdn_dp_device {
>>          u8 ports;
>>          u8 lanes;
>>          int active_port;
>> +       u8 train_set[4];
>>
>>          u8 dpcd[DP_RECEIVER_CAP_SIZE];
>>          bool sink_has_audio;
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>> new file mode 100644
>> index 0000000..558c945
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>> @@ -0,0 +1,398 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
> For a C source file the format is:
> (https://www.kernel.org/doc/html/latest/process/license-rules.html)
>
> // SPDX-License-Identifier: <SPDX License Expression>
Thanks for pointing it, will fix it.
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Chris Zhong <zyw@rock-chips.com>
>> + */
>> +
>> +#include <linux/arm-smccc.h>
> Why you need this include?
>
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/reset.h>
>> +#include <soc/rockchip/rockchip_phy_typec.h>
>> +
> In fact, I think that there are other includes that can be removed,
> please review.
>
>> +#include "cdn-dp-core.h"
>> +#include "cdn-dp-reg.h"
>> +
>> +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
>> +{
>> +       struct cdn_dp_port *port = dp->port[dp->active_port];
>> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>> +
>> +       int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>> +       u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
>> +                  DP_TRAIN_VOLTAGE_SWING_SHIFT;
>> +       u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
>> +                         >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
>> +
>> +       tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
>> +                               swing, pre_emphasis);
>> +}
>> +
>> +static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
>> +{
>> +       u32 phy_config, global_config;
>> +       int ret;
>> +       uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
>> +
>> +       global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
>> +                       GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
>> +
>> +       phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
>> +                    DP_TX_PHY_SKEW_BYPASS(0) |
>> +                    DP_TX_PHY_DISPARITY_RST(0) |
>> +                    DP_TX_PHY_LANE0_SKEW(0) |
>> +                    DP_TX_PHY_LANE1_SKEW(1) |
>> +                    DP_TX_PHY_LANE2_SKEW(2) |
>> +                    DP_TX_PHY_LANE3_SKEW(3) |
>> +                    DP_TX_PHY_10BIT_ENABLE(0);
>> +
>> +       if (pattern != DP_TRAINING_PATTERN_DISABLE) {
>> +               global_config |= NO_VIDEO;
>> +               phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
>> +                             DP_TX_PHY_SCRAMBLER_BYPASS(1) |
>> +                             DP_TX_PHY_TRAINING_PATTERN(pattern);
>> +       }
>> +
>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
>> +       if (ret) {
>> +               DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
>> +                         ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
>> +       if (ret) {
>> +               DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
>> +                         ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
>> +       if (ret) {
>> +               DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
>> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
>> +       else
>> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
>> +       if (ret)
>> +               DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
>> +
>> +       return ret;
>> +}
>> +
>> +static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
>> +{
>> +       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> +       default:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +       }
>> +}
>> +
>> +static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
>> +                                   uint8_t link_status[DP_LINK_STATUS_SIZE])
>> +{
>> +       int i;
>> +       uint8_t v = 0, p = 0;
>> +       uint8_t preemph_max;
>> +
>> +       for (i = 0; i < dp->link.num_lanes; i++) {
>> +               v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
>> +               p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
>> +                                                                 i));
>> +       }
>> +
>> +       if (v >= VOLTAGE_LEVEL_2)
>> +               v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
>> +
>> +       preemph_max = cdn_dp_pre_emphasis_max(v);
>> +       if (p >= preemph_max)
>> +               p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
>> +
>> +       for (i = 0; i < dp->link.num_lanes; i++)
>> +               dp->train_set[i] = v | p;
>> +}
>> +
>> +/*
>> + * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
>> + * or 1.2 devices that support it, Training Pattern 2 otherwise.
>> + */
>> +static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
>> +{
>> +       u32 training_pattern = DP_TRAINING_PATTERN_2;
>> +
>> +       /*
>> +        * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
>> +        * for downstream devices that support HBR2. However, not all sinks
>> +        * follow the spec.
>> +        */
>> +       if (drm_dp_tps3_supported(dp->dpcd))
>> +               training_pattern = DP_TRAINING_PATTERN_3;
>> +       else
>> +               DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
>> +
>> +       return training_pattern;
>> +}
>> +
>> +
>> +static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
>> +{
>> +       int lane;
>> +
>> +       for (lane = 0; lane < dp->link.num_lanes; lane++)
>> +               if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
>> +                       return false;
>> +
>> +       return true;
>> +}
>> +
>> +static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
>> +{
>> +       int ret;
>> +
>> +       cdn_dp_set_signal_levels(dp);
>> +
>> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
>> +                               dp->train_set, dp->link.num_lanes);
>> +       if (ret != dp->link.num_lanes)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
>> +                                 uint8_t dp_train_pat)
>> +{
>> +       uint8_t buf[sizeof(dp->train_set) + 1];
>> +       int ret, len;
>> +
>> +       buf[0] = dp_train_pat;
>> +       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
>> +           DP_TRAINING_PATTERN_DISABLE) {
>> +               /* don't write DP_TRAINING_LANEx_SET on disable */
>> +               len = 1;
>> +       } else {
>> +               /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
>> +               memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
>> +               len = dp->link.num_lanes + 1;
>> +       }
>> +
>> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
>> +                               buf, len);
>> +       if (ret != len)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
>> +                                   uint8_t dp_train_pat)
>> +{
>> +       int ret;
>> +
>> +       memset(dp->train_set, 0, sizeof(dp->train_set));
>> +
>> +       cdn_dp_set_signal_levels(dp);
>> +
>> +       ret = cdn_dp_set_pattern(dp, dp_train_pat);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return cdn_dp_set_link_train(dp, dp_train_pat);
>> +}
>> +
>> +/* Enable corresponding port and start training pattern 1 */
>> +static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
>> +{
>> +       u8 voltage, link_config[2];
>> +       u8 link_status[DP_LINK_STATUS_SIZE];
>> +       u32 voltage_tries, max_vswing_tries;
>> +       u32 rate, sink_max, source_max;
>> +       int ret;
>> +
>> +       source_max = dp->lanes;
>> +       sink_max = drm_dp_max_lane_count(dp->dpcd);
>> +       dp->link.num_lanes = min(source_max, sink_max);
>> +
>> +       source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
>> +       sink_max = drm_dp_max_link_rate(dp->dpcd);
>> +       rate = min(source_max, sink_max);
>> +       dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
>> +
>> +       /* Write the link configuration data */
>> +       link_config[0] = dp->link.rate;
>> +       link_config[1] = dp->link.num_lanes;
>> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
>> +               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
>> +       drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
>> +
>> +       link_config[0] = 0;
>> +       link_config[1] = 0;
>> +       if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
>> +               link_config[1] = DP_SET_ANSI_8B10B;
>> +
>> +       drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
>> +
>> +       /* clock recovery */
>> +       ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
>> +                                         DP_LINK_SCRAMBLING_DISABLE);
>> +       if (ret) {
>> +               DRM_ERROR("failed to start link train\n");
>> +               return ret;
>> +       }
>> +
>> +       voltage_tries = 1;
>> +       max_vswing_tries = 0;
>> +       for (;;) {
>> +               drm_dp_link_train_clock_recovery_delay(dp->dpcd);
>> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
>> +                   DP_LINK_STATUS_SIZE) {
>> +                       DRM_ERROR("failed to get link status\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
>> +                       DRM_DEBUG_KMS("clock recovery OK\n");
>> +                       return 0;
>> +               }
>> +
>> +               if (voltage_tries >= 5) {
>> +                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               if (max_vswing_tries >= 1) {
>> +                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
>> +
>> +               /* Update training set as requested by target */
>> +               cdn_dp_get_adjust_train(dp, link_status);
>> +               if (cdn_dp_update_link_train(dp)) {
>> +                       DRM_ERROR("failed to update link training\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
>> +                   voltage)
>> +                       ++voltage_tries;
>> +               else
>> +                       voltage_tries = 1;
>> +
>> +               if (cdn_dp_link_max_vswing_reached(dp))
>> +                       ++max_vswing_tries;
>> +       }
>> +}
>> +
>> +static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
>> +{
>> +       int tries, ret;
>> +       u32 training_pattern;
>> +       uint8_t link_status[DP_LINK_STATUS_SIZE];
>> +
>> +       training_pattern = cdn_dp_select_chaneq_pattern(dp);
>> +       training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
>> +
>> +       ret = cdn_dp_set_pattern(dp, training_pattern);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_set_link_train(dp, training_pattern);
>> +       if (ret) {
>> +               DRM_ERROR("failed to start channel equalization\n");
>> +               return ret;
>> +       }
>> +
>> +       for (tries = 0; tries < 5; tries++) {
>> +               drm_dp_link_train_channel_eq_delay(dp->dpcd);
>> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
>> +                   DP_LINK_STATUS_SIZE) {
>> +                       DRM_ERROR("failed to get link status\n");
>> +                       break;
>> +               }
>> +
>> +               /* Make sure clock is still ok */
>> +               if (!drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
>> +                       DRM_DEBUG_KMS("Clock recovery check failed, cannot "
>> +                                     "continue channel equalization\n");
>> +                       break;
>> +               }
>> +
>> +               if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
>> +                       DRM_DEBUG_KMS("Channel EQ done. DP Training "
>> +                                     "successful\n");
>> +                       return 0;
>> +               }
>> +
>> +               /* Update training set as requested by target */
>> +               cdn_dp_get_adjust_train(dp, link_status);
>> +               if (cdn_dp_update_link_train(dp)) {
>> +                       DRM_ERROR("failed to update link training\n");
>> +                       break;
>> +               }
>> +       }
>> +
>> +       /* Try 5 times, else fail and try at lower BW */
>> +       if (tries == 5)
>> +               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
>> +
>> +       return -EINVAL;
>> +
>> +}
>> +
>> +static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
>> +{
>> +       int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
>> +
>> +       if (ret)
>> +               return ret;
>> +
>> +       return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
>> +}
>> +
>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
>> +{
>> +       int ret, stop_err;
>> +
>> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
>> +                              sizeof(dp->dpcd));
>> +       if (ret < 0) {
>> +               DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = cdn_dp_link_training_clock_recovery(dp);
>> +       if (ret) {
>> +               DRM_ERROR("training clock recovery fail, error: %d\n", ret);
>> +               goto out;
>> +       }
>> +
>> +       ret = cdn_dp_link_training_channel_equalization(dp);
>> +       if (ret) {
>> +               DRM_ERROR("training channel equalization fail, error: %d\n",
>> +                         ret);
>> +               goto out;
>> +       }
>> +out:
>> +       stop_err = cdn_dp_stop_link_train(dp);
>> +       if (stop_err) {
>> +               DRM_ERROR("stop training fail, error: %d\n", stop_err);
>> +               return stop_err;
>> +       }
>> +
>> +       return ret;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> index b2f532a..72780f1 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> @@ -17,7 +17,9 @@
>>   #include <linux/delay.h>
>>   #include <linux/io.h>
>>   #include <linux/iopoll.h>
>> +#include <linux/phy/phy.h>
>>   #include <linux/reset.h>
>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>
>>   #include "cdn-dp-core.h"
>>   #include "cdn-dp-reg.h"
>> @@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
>>          return 0;
>>   }
>>
>> -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>>   {
>>          u8 msg[6];
>>
>> @@ -605,22 +607,41 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
>>   int cdn_dp_train_link(struct cdn_dp_device *dp)
>>   {
>>          int ret;
>> +       struct cdn_dp_port *port = dp->port[dp->active_port];
>> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>>
>> +       /*
>> +        * DP firmware uses fixed phy config values to do training, but some
>> +        * boards need to adjust these values to fit for their unique hardware
>> +        * design. So if the phy is using custom config values, do software
>> +        * link training instead of relying on firmware, if software training
>> +        * fail, keep firmware training as a fallback if sw training fails.
>> +        */
>> +       if (tcphy->need_software_training) {
>> +               ret = cdn_dp_software_train_link(dp);
>> +               if (ret) {
>> +                       DRM_DEV_ERROR(dp->dev,
>> +                               "Failed to do software training %d\n", ret);
>> +                       goto do_fw_training;
>> +               }
>> +               cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
> Check for an error here and act accordingly? Or doesn't matter?
yes, it need, thanks.
>
>> +               dp->sw_training_success = true;
>> +               return 0;
>> +       }
>> +
> nit: Personally I don't like this goto. Maybe you can refactor this.
>
>         if (tcphy->need_software_training) {
>                 ret = cdn_dp_software_train_link(dp);
>                 if (!ret) {
>                         cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
>                         dp->sw_training_success = true;
>                         return 0;
>                 }
>                 DRM_DEV_ERROR(dp->dev, "Failed to do software training
> %d\n", ret);
>         }
if check the cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf) error, i think 
we still need this goto.
>> +do_fw_training:
> Then remove the goto label.
>
>> +       dp->sw_training_success = false;
>>          ret = cdn_dp_training_start(dp);
>>          if (ret) {
>>                  DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
>>                  return ret;
>>          }
>> -
> Do not remove the new line.
>
>>          ret = cdn_dp_get_training_status(dp);
>>          if (ret) {
>>                  DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
>>                  return ret;
>>          }
>> -
>> -       DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
>> -                         dp->link.num_lanes);
> Why you remove this debug message?
>
>> -       return ret;
>> +       return 0;
>>   }
>>
>>   int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> index aedf2dc..b60a6b2 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> @@ -137,7 +137,7 @@
>>   #define HPD_EVENT_MASK                 0x211c
>>   #define HPD_EVENT_DET                  0x2120
>>
>> -/* dpyx framer addr */
>> +/* dptx framer addr */
>>   #define DP_FRAMER_GLOBAL_CONFIG                0x2200
>>   #define DP_SW_RESET                    0x2204
>>   #define DP_FRAMER_TU                   0x2208
>> @@ -431,6 +431,40 @@
>>   /* Reference cycles when using lane clock as reference */
>>   #define LANE_REF_CYC                           0x8000
>>
>> +/* register CM_VID_CTRL */
>> +#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
>> +#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
>> +
>> +/* register DP_TX_PHY_CONFIG_REG */
>> +#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
>> +#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
>> +#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
>> +#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
>> +#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
>> +#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
>> +#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
>> +#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
>> +#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
>> +#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
>> +#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
>> +
>> +/* register DP_FRAMER_GLOBAL_CONFIG */
>> +#define NUM_LANES(x)           ((x) & 3)
>> +#define SST_MODE               (0 << 2)
>> +#define GLOBAL_EN              (1 << 3)
>> +#define RG_EN                  (0 << 4)
>> +#define NO_VIDEO               (1 << 5)
>> +#define ENC_RST_DIS            (1 << 6)
>> +#define WR_VHSYNC_FALL         (1 << 7)
>> +
> Use the BIT() macros for these.
will fix it
>
>>   enum voltage_swing_level {
>>          VOLTAGE_LEVEL_0,
>>          VOLTAGE_LEVEL_1,
>> @@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>>   int cdn_dp_event_config(struct cdn_dp_device *dp);
>>   u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>>   int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
>>   ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>>                            u8 *data, u16 len);
>>   ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
>> @@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
>>   int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
>>   int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>>   int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp);
>>   #endif /* _CDN_DP_REG_H */
>> --
>> 2.7.4
>>
>
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 4/4] drm/rockchip: support dp training outside dp firmware
@ 2018-05-09  9:47       ` hl
  0 siblings, 0 replies; 26+ messages in thread
From: hl @ 2018-05-09  9:47 UTC (permalink / raw)
  To: linux-arm-kernel



On Monday, May 07, 2018 07:29 PM, Enric Balletbo Serra wrote:
> Hi Lin,
>
> Thanks for the patch.
>
> 2018-05-04 10:08 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>> DP firware use fix phy config value to do training, but some
> s/fiware/firmware/
>
>> board need to adjust these value to fit for their hardware design,
>> so we use new phy config to do training outside firmware to meet
>> this situation, if there have new phy config pass from dts, it will
>> use training outside firmware.
>>
> maybe you can rewrite all this in a better way.
>
> ooi, which boards needs this?
>
>
>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>> ---
>>   drivers/gpu/drm/rockchip/Makefile               |   3 +-
>>   drivers/gpu/drm/rockchip/cdn-dp-core.c          |  23 +-
>>   drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
>>   drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 ++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  33 +-
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
>>   6 files changed, 480 insertions(+), 17 deletions(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index a314e21..b932f62 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>
>>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>> +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
>> +                                       cdn-dp-link-training.o
>>   rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>>   rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>>   rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> index 268c190..a2a4208 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>>                          goto out;
>>                  }
>>          }
>> -
>> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>> -       if (ret) {
>> -               DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
>> -               goto out;
>> +       if (dp->sw_training_success == false) {
>> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>> +               if (ret) {
>> +                       DRM_DEV_ERROR(dp->dev,
>> +                                     "Failed to idle video %d\n", ret);
>> +                       goto out;
>> +               }
>>          }
>>
>>          ret = cdn_dp_config_video(dp);
>> @@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
>>                  goto out;
>>          }
>>
>> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>> -       if (ret) {
>> -               DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
>> -               goto out;
>> +       if (dp->sw_training_success == false) {
>> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>> +               if (ret) {
>> +                       DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
>> +                       goto out;
>> +               }
>>          }
>> +
>>   out:
>>          mutex_unlock(&dp->lock);
>>   }
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> index 46159b2..c6050ab 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> @@ -84,6 +84,7 @@ struct cdn_dp_device {
>>          bool connected;
>>          bool active;
>>          bool suspended;
>> +       bool sw_training_success;
>>
>>          const struct firmware *fw;      /* cdn dp firmware */
>>          unsigned int fw_version;        /* cdn fw version */
>> @@ -106,6 +107,7 @@ struct cdn_dp_device {
>>          u8 ports;
>>          u8 lanes;
>>          int active_port;
>> +       u8 train_set[4];
>>
>>          u8 dpcd[DP_RECEIVER_CAP_SIZE];
>>          bool sink_has_audio;
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>> new file mode 100644
>> index 0000000..558c945
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>> @@ -0,0 +1,398 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
> For a C source file the format is:
> (https://www.kernel.org/doc/html/latest/process/license-rules.html)
>
> // SPDX-License-Identifier: <SPDX License Expression>
Thanks for pointing it, will fix it.
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Chris Zhong <zyw@rock-chips.com>
>> + */
>> +
>> +#include <linux/arm-smccc.h>
> Why you need this include?
>
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/reset.h>
>> +#include <soc/rockchip/rockchip_phy_typec.h>
>> +
> In fact, I think that there are other includes that can be removed,
> please review.
>
>> +#include "cdn-dp-core.h"
>> +#include "cdn-dp-reg.h"
>> +
>> +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
>> +{
>> +       struct cdn_dp_port *port = dp->port[dp->active_port];
>> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>> +
>> +       int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>> +       u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
>> +                  DP_TRAIN_VOLTAGE_SWING_SHIFT;
>> +       u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
>> +                         >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
>> +
>> +       tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
>> +                               swing, pre_emphasis);
>> +}
>> +
>> +static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
>> +{
>> +       u32 phy_config, global_config;
>> +       int ret;
>> +       uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
>> +
>> +       global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
>> +                       GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
>> +
>> +       phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
>> +                    DP_TX_PHY_SKEW_BYPASS(0) |
>> +                    DP_TX_PHY_DISPARITY_RST(0) |
>> +                    DP_TX_PHY_LANE0_SKEW(0) |
>> +                    DP_TX_PHY_LANE1_SKEW(1) |
>> +                    DP_TX_PHY_LANE2_SKEW(2) |
>> +                    DP_TX_PHY_LANE3_SKEW(3) |
>> +                    DP_TX_PHY_10BIT_ENABLE(0);
>> +
>> +       if (pattern != DP_TRAINING_PATTERN_DISABLE) {
>> +               global_config |= NO_VIDEO;
>> +               phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
>> +                             DP_TX_PHY_SCRAMBLER_BYPASS(1) |
>> +                             DP_TX_PHY_TRAINING_PATTERN(pattern);
>> +       }
>> +
>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG, global_config);
>> +       if (ret) {
>> +               DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error: %d\n",
>> +                         ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
>> +       if (ret) {
>> +               DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error: %d\n",
>> +                         ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
>> +       if (ret) {
>> +               DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
>> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
>> +       else
>> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
>> +       if (ret)
>> +               DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
>> +
>> +       return ret;
>> +}
>> +
>> +static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
>> +{
>> +       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> +       default:
>> +               return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +       }
>> +}
>> +
>> +static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
>> +                                   uint8_t link_status[DP_LINK_STATUS_SIZE])
>> +{
>> +       int i;
>> +       uint8_t v = 0, p = 0;
>> +       uint8_t preemph_max;
>> +
>> +       for (i = 0; i < dp->link.num_lanes; i++) {
>> +               v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
>> +               p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
>> +                                                                 i));
>> +       }
>> +
>> +       if (v >= VOLTAGE_LEVEL_2)
>> +               v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
>> +
>> +       preemph_max = cdn_dp_pre_emphasis_max(v);
>> +       if (p >= preemph_max)
>> +               p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
>> +
>> +       for (i = 0; i < dp->link.num_lanes; i++)
>> +               dp->train_set[i] = v | p;
>> +}
>> +
>> +/*
>> + * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
>> + * or 1.2 devices that support it, Training Pattern 2 otherwise.
>> + */
>> +static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
>> +{
>> +       u32 training_pattern = DP_TRAINING_PATTERN_2;
>> +
>> +       /*
>> +        * cdn dp support HBR2 also support TPS3. TPS3 support is also mandatory
>> +        * for downstream devices that support HBR2. However, not all sinks
>> +        * follow the spec.
>> +        */
>> +       if (drm_dp_tps3_supported(dp->dpcd))
>> +               training_pattern = DP_TRAINING_PATTERN_3;
>> +       else
>> +               DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n");
>> +
>> +       return training_pattern;
>> +}
>> +
>> +
>> +static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
>> +{
>> +       int lane;
>> +
>> +       for (lane = 0; lane < dp->link.num_lanes; lane++)
>> +               if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
>> +                       return false;
>> +
>> +       return true;
>> +}
>> +
>> +static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
>> +{
>> +       int ret;
>> +
>> +       cdn_dp_set_signal_levels(dp);
>> +
>> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
>> +                               dp->train_set, dp->link.num_lanes);
>> +       if (ret != dp->link.num_lanes)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
>> +                                 uint8_t dp_train_pat)
>> +{
>> +       uint8_t buf[sizeof(dp->train_set) + 1];
>> +       int ret, len;
>> +
>> +       buf[0] = dp_train_pat;
>> +       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
>> +           DP_TRAINING_PATTERN_DISABLE) {
>> +               /* don't write DP_TRAINING_LANEx_SET on disable */
>> +               len = 1;
>> +       } else {
>> +               /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
>> +               memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
>> +               len = dp->link.num_lanes + 1;
>> +       }
>> +
>> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
>> +                               buf, len);
>> +       if (ret != len)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
>> +                                   uint8_t dp_train_pat)
>> +{
>> +       int ret;
>> +
>> +       memset(dp->train_set, 0, sizeof(dp->train_set));
>> +
>> +       cdn_dp_set_signal_levels(dp);
>> +
>> +       ret = cdn_dp_set_pattern(dp, dp_train_pat);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return cdn_dp_set_link_train(dp, dp_train_pat);
>> +}
>> +
>> +/* Enable corresponding port and start training pattern 1 */
>> +static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
>> +{
>> +       u8 voltage, link_config[2];
>> +       u8 link_status[DP_LINK_STATUS_SIZE];
>> +       u32 voltage_tries, max_vswing_tries;
>> +       u32 rate, sink_max, source_max;
>> +       int ret;
>> +
>> +       source_max = dp->lanes;
>> +       sink_max = drm_dp_max_lane_count(dp->dpcd);
>> +       dp->link.num_lanes = min(source_max, sink_max);
>> +
>> +       source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
>> +       sink_max = drm_dp_max_link_rate(dp->dpcd);
>> +       rate = min(source_max, sink_max);
>> +       dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
>> +
>> +       /* Write the link configuration data */
>> +       link_config[0] = dp->link.rate;
>> +       link_config[1] = dp->link.num_lanes;
>> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
>> +               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
>> +       drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
>> +
>> +       link_config[0] = 0;
>> +       link_config[1] = 0;
>> +       if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
>> +               link_config[1] = DP_SET_ANSI_8B10B;
>> +
>> +       drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
>> +
>> +       /* clock recovery */
>> +       ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
>> +                                         DP_LINK_SCRAMBLING_DISABLE);
>> +       if (ret) {
>> +               DRM_ERROR("failed to start link train\n");
>> +               return ret;
>> +       }
>> +
>> +       voltage_tries = 1;
>> +       max_vswing_tries = 0;
>> +       for (;;) {
>> +               drm_dp_link_train_clock_recovery_delay(dp->dpcd);
>> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
>> +                   DP_LINK_STATUS_SIZE) {
>> +                       DRM_ERROR("failed to get link status\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
>> +                       DRM_DEBUG_KMS("clock recovery OK\n");
>> +                       return 0;
>> +               }
>> +
>> +               if (voltage_tries >= 5) {
>> +                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               if (max_vswing_tries >= 1) {
>> +                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
>> +
>> +               /* Update training set as requested by target */
>> +               cdn_dp_get_adjust_train(dp, link_status);
>> +               if (cdn_dp_update_link_train(dp)) {
>> +                       DRM_ERROR("failed to update link training\n");
>> +                       return -EINVAL;
>> +               }
>> +
>> +               if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
>> +                   voltage)
>> +                       ++voltage_tries;
>> +               else
>> +                       voltage_tries = 1;
>> +
>> +               if (cdn_dp_link_max_vswing_reached(dp))
>> +                       ++max_vswing_tries;
>> +       }
>> +}
>> +
>> +static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp)
>> +{
>> +       int tries, ret;
>> +       u32 training_pattern;
>> +       uint8_t link_status[DP_LINK_STATUS_SIZE];
>> +
>> +       training_pattern = cdn_dp_select_chaneq_pattern(dp);
>> +       training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
>> +
>> +       ret = cdn_dp_set_pattern(dp, training_pattern);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_set_link_train(dp, training_pattern);
>> +       if (ret) {
>> +               DRM_ERROR("failed to start channel equalization\n");
>> +               return ret;
>> +       }
>> +
>> +       for (tries = 0; tries < 5; tries++) {
>> +               drm_dp_link_train_channel_eq_delay(dp->dpcd);
>> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
>> +                   DP_LINK_STATUS_SIZE) {
>> +                       DRM_ERROR("failed to get link status\n");
>> +                       break;
>> +               }
>> +
>> +               /* Make sure clock is still ok */
>> +               if (!drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
>> +                       DRM_DEBUG_KMS("Clock recovery check failed, cannot "
>> +                                     "continue channel equalization\n");
>> +                       break;
>> +               }
>> +
>> +               if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
>> +                       DRM_DEBUG_KMS("Channel EQ done. DP Training "
>> +                                     "successful\n");
>> +                       return 0;
>> +               }
>> +
>> +               /* Update training set as requested by target */
>> +               cdn_dp_get_adjust_train(dp, link_status);
>> +               if (cdn_dp_update_link_train(dp)) {
>> +                       DRM_ERROR("failed to update link training\n");
>> +                       break;
>> +               }
>> +       }
>> +
>> +       /* Try 5 times, else fail and try at lower BW */
>> +       if (tries == 5)
>> +               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
>> +
>> +       return -EINVAL;
>> +
>> +}
>> +
>> +static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
>> +{
>> +       int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
>> +
>> +       if (ret)
>> +               return ret;
>> +
>> +       return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
>> +}
>> +
>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
>> +{
>> +       int ret, stop_err;
>> +
>> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
>> +                              sizeof(dp->dpcd));
>> +       if (ret < 0) {
>> +               DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = cdn_dp_link_training_clock_recovery(dp);
>> +       if (ret) {
>> +               DRM_ERROR("training clock recovery fail, error: %d\n", ret);
>> +               goto out;
>> +       }
>> +
>> +       ret = cdn_dp_link_training_channel_equalization(dp);
>> +       if (ret) {
>> +               DRM_ERROR("training channel equalization fail, error: %d\n",
>> +                         ret);
>> +               goto out;
>> +       }
>> +out:
>> +       stop_err = cdn_dp_stop_link_train(dp);
>> +       if (stop_err) {
>> +               DRM_ERROR("stop training fail, error: %d\n", stop_err);
>> +               return stop_err;
>> +       }
>> +
>> +       return ret;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> index b2f532a..72780f1 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> @@ -17,7 +17,9 @@
>>   #include <linux/delay.h>
>>   #include <linux/io.h>
>>   #include <linux/iopoll.h>
>> +#include <linux/phy/phy.h>
>>   #include <linux/reset.h>
>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>
>>   #include "cdn-dp-core.h"
>>   #include "cdn-dp-reg.h"
>> @@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
>>          return 0;
>>   }
>>
>> -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>>   {
>>          u8 msg[6];
>>
>> @@ -605,22 +607,41 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
>>   int cdn_dp_train_link(struct cdn_dp_device *dp)
>>   {
>>          int ret;
>> +       struct cdn_dp_port *port = dp->port[dp->active_port];
>> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>>
>> +       /*
>> +        * DP firmware uses fixed phy config values to do training, but some
>> +        * boards need to adjust these values to fit for their unique hardware
>> +        * design. So if the phy is using custom config values, do software
>> +        * link training instead of relying on firmware, if software training
>> +        * fail, keep firmware training as a fallback if sw training fails.
>> +        */
>> +       if (tcphy->need_software_training) {
>> +               ret = cdn_dp_software_train_link(dp);
>> +               if (ret) {
>> +                       DRM_DEV_ERROR(dp->dev,
>> +                               "Failed to do software training %d\n", ret);
>> +                       goto do_fw_training;
>> +               }
>> +               cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
> Check for an error here and act accordingly? Or doesn't matter?
yes, it need, thanks.
>
>> +               dp->sw_training_success = true;
>> +               return 0;
>> +       }
>> +
> nit: Personally I don't like this goto. Maybe you can refactor this.
>
>         if (tcphy->need_software_training) {
>                 ret = cdn_dp_software_train_link(dp);
>                 if (!ret) {
>                         cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
>                         dp->sw_training_success = true;
>                         return 0;
>                 }
>                 DRM_DEV_ERROR(dp->dev, "Failed to do software training
> %d\n", ret);
>         }
if check the cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf) error, i think 
we still need this goto.
>> +do_fw_training:
> Then remove the goto label.
>
>> +       dp->sw_training_success = false;
>>          ret = cdn_dp_training_start(dp);
>>          if (ret) {
>>                  DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
>>                  return ret;
>>          }
>> -
> Do not remove the new line.
>
>>          ret = cdn_dp_get_training_status(dp);
>>          if (ret) {
>>                  DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
>>                  return ret;
>>          }
>> -
>> -       DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
>> -                         dp->link.num_lanes);
> Why you remove this debug message?
>
>> -       return ret;
>> +       return 0;
>>   }
>>
>>   int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> index aedf2dc..b60a6b2 100644
>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> @@ -137,7 +137,7 @@
>>   #define HPD_EVENT_MASK                 0x211c
>>   #define HPD_EVENT_DET                  0x2120
>>
>> -/* dpyx framer addr */
>> +/* dptx framer addr */
>>   #define DP_FRAMER_GLOBAL_CONFIG                0x2200
>>   #define DP_SW_RESET                    0x2204
>>   #define DP_FRAMER_TU                   0x2208
>> @@ -431,6 +431,40 @@
>>   /* Reference cycles when using lane clock as reference */
>>   #define LANE_REF_CYC                           0x8000
>>
>> +/* register CM_VID_CTRL */
>> +#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
>> +#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
>> +
>> +/* register DP_TX_PHY_CONFIG_REG */
>> +#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
>> +#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
>> +#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
>> +#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
>> +#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
>> +#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
>> +#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
>> +#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
>> +#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
>> +#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
>> +#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
>> +#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
>> +
>> +/* register DP_FRAMER_GLOBAL_CONFIG */
>> +#define NUM_LANES(x)           ((x) & 3)
>> +#define SST_MODE               (0 << 2)
>> +#define GLOBAL_EN              (1 << 3)
>> +#define RG_EN                  (0 << 4)
>> +#define NO_VIDEO               (1 << 5)
>> +#define ENC_RST_DIS            (1 << 6)
>> +#define WR_VHSYNC_FALL         (1 << 7)
>> +
> Use the BIT() macros for these.
will fix it
>
>>   enum voltage_swing_level {
>>          VOLTAGE_LEVEL_0,
>>          VOLTAGE_LEVEL_1,
>> @@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
>>   int cdn_dp_event_config(struct cdn_dp_device *dp);
>>   u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>>   int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
>>   ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>>                            u8 *data, u16 len);
>>   ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
>> @@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
>>   int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
>>   int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>>   int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp);
>>   #endif /* _CDN_DP_REG_H */
>> --
>> 2.7.4
>>
>
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2018-05-09  9:48 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-04  8:08 [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp Lin Huang
2018-05-04  8:08 ` Lin Huang
2018-05-04  8:08 ` [PATCH 2/4] phy: rockchip-typec: support variable phy config value Lin Huang
2018-05-04  8:08   ` Lin Huang
2018-05-04 17:51   ` kbuild test robot
2018-05-04 17:51     ` kbuild test robot
2018-05-04 17:51     ` kbuild test robot
2018-05-07 13:59   ` Enric Balletbo Serra
2018-05-07 13:59     ` Enric Balletbo Serra
2018-05-07 13:59     ` Enric Balletbo Serra
2018-05-09  3:39     ` hl
2018-05-09  3:39       ` hl
2018-05-04  8:08 ` [PATCH 3/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY Lin Huang
2018-05-04  8:08   ` Lin Huang
2018-05-04  8:08 ` [PATCH 4/4] drm/rockchip: support dp training outside dp firmware Lin Huang
2018-05-04  8:08   ` Lin Huang
2018-05-07 11:29   ` Enric Balletbo Serra
2018-05-07 11:29     ` Enric Balletbo Serra
2018-05-07 11:29     ` Enric Balletbo Serra
2018-05-09  9:47     ` hl
2018-05-09  9:47       ` hl
2018-05-07 11:27 ` [PATCH 1/4] drm/rockchip: add transfer function for cdn-dp Enric Balletbo Serra
2018-05-07 11:27   ` Enric Balletbo Serra
2018-05-07 11:27   ` Enric Balletbo Serra
2018-05-09  2:59   ` hl
2018-05-09  2:59     ` hl

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.