2018-05-14 09:55:46

by Lin Huang

[permalink] [raw]
Subject: [PATCH v3 1/4] drm/rockchip: add transfer function for cdn-dp

From: Chris Zhong <[email protected]>

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 <[email protected]>
Signed-off-by: Lin Huang <[email protected]>
Reviewed-by: Sean Paul <[email protected]>
Reviewed-by: Enric Balletbo <[email protected]>
---
Changes in v2:
- update patch following Enric suggest
- None
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 | 69 ++++++++++++++++++++++++++++++----
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++++++-
4 files changed, 122 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..cce64c1 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_STATUS_ACK)
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ else if (status == AUX_STATUS_NACK)
+ msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+ else if (status == AUX_STATUS_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..979355d 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -221,7 +221,12 @@ 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 was
+ * not send successfully;
+ */
+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 +252,41 @@ 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 was not send
+ * success; -EINVAL is returned if get the wrong data size after message
+ * is sent
+ */
+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 +299,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 +312,33 @@ 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..6580b11 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_STATUS_ACK 0
+#define AUX_STATUS_NACK 1
+#define AUX_STATUS_DEFER 2
+#define AUX_STATUS_SINK_ERROR 3
+#define AUX_STATUS_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



2018-05-14 09:54:56

by Lin Huang

[permalink] [raw]
Subject: [PATCH v3 3/4] phy: rockchip-typec: support variable phy config value

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 <[email protected]>
Signed-off-by: Lin Huang <[email protected]>
---
Changes in v2:
- update patch following Enric suggest
Changes in v3:
- delete need_software_training variable
- add default phy config value, if dts do not define phy config value, use these value

drivers/phy/rockchip/phy-rockchip-typec.c | 305 ++++++++++++++++++++----------
include/soc/rockchip/rockchip_phy_typec.h | 63 ++++++
2 files changed, 270 insertions(+), 98 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..10253ad 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,21 +324,29 @@
* 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

@@ -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 DP_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,
@@ -463,6 +471,24 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
{ /* sentinel */ }
};

+/* default phy config */
+struct phy_config configs[3][4] = {
+ {{ 0x2a, 0x00 },
+ { 0x1f, 0x15 },
+ { 0x14, 0x22 },
+ { 0x02, 0x2b } },
+
+ {{ 0x21, 0x00 },
+ { 0x12, 0x15 },
+ { 0x02, 0x22 },
+ { 0, 0 } },
+
+ {{ 0x15, 0x00 },
+ { 0x00, 0x15 },
+ { 0, 0 },
+ { 0, 0 } },
+};
+
static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
{
u32 i, rdata;
@@ -484,7 +510,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 +524,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 +588,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 +599,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 +814,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, DP_DEFAULT_RATE);
for (i = 0; i < 4; i++)
- tcphy_dp_cfg_lane(tcphy, i);
+ tcphy_dp_cfg_lane(tcphy, DP_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, DP_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, DP_DEFAULT_RATE, 0, 0, 0);
+ tcphy_dp_cfg_lane(tcphy, DP_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, DP_DEFAULT_RATE, 0, 0, 2);
+ tcphy_dp_cfg_lane(tcphy, DP_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 +1053,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 +1062,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 +1092,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 +1101,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,9 +1120,35 @@ static const struct phy_ops rockchip_dp_phy_ops = {
.owner = THIS_MODULE,
};

+static int typec_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)
{
+ int ret;
+
tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(tcphy->grf_regs)) {
@@ -1087,6 +1186,15 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
return PTR_ERR(tcphy->tcphy_rst);
}

+ /*
+ * check if phy_config pass from dts, if no,
+ * use default phy config value.
+ */
+ ret = of_property_read_u32_array(dev->of_node, "rockchip,phy_config",
+ (u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32));
+ if (ret)
+ memcpy(tcphy->config, configs, sizeof(tcphy->config));
+
return 0;
}

@@ -1171,6 +1279,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
}
}

+ tcphy->typec_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..be6af0e
--- /dev/null
+++ b/include/soc/rockchip/rockchip_phy_typec.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Lin Huang <[email protected]>
+ */
+
+#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;
+ const struct rockchip_usb3phy_port_cfg *port_cfgs;
+ /* mutex to protect access to individual PHYs */
+ struct mutex lock;
+ struct phy_config config[3][4];
+ 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


2018-05-14 09:55:02

by Lin Huang

[permalink] [raw]
Subject: [PATCH v3 4/4] drm/rockchip: support dp training outside dp firmware

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.

Signed-off-by: Chris Zhong <[email protected]>
Signed-off-by: Lin Huang <[email protected]>
---
Changes in v2:
- update patch following Enric suggest
Changes in v3:
- use variable fw_training instead sw_training_success
- base on DP SPCE, if training fail use lower link rate to retry training

drivers/gpu/drm/rockchip/Makefile | 3 +-
drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +-
drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 +
drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 416 ++++++++++++++++++++++++
drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +-
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++-
6 files changed, 501 insertions(+), 13 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 cce64c1..d9d0d4d 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->use_fw_training == true) {
+ 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,15 @@ 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->use_fw_training == true) {
+ 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..77a9793 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 use_fw_training;

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..b8fd5bc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <[email protected]>
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/phy/phy.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;
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ u32 voltage_tries, max_vswing_tries;
+ int ret;
+
+ /* 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\n");
+ break;
+ }
+
+ if (drm_dp_channel_eq_ok(link_status, dp->link.num_lanes)) {
+ DRM_DEBUG_KMS("Channel EQ done\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);
+}
+
+static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp)
+{
+ if (dp->link.rate == DP_LINK_BW_1_62)
+ return -EINVAL;
+ else if (dp->link.rate == DP_LINK_BW_2_7)
+ dp->link.rate = DP_LINK_BW_1_62;
+ else
+ dp->link.rate = DP_LINK_BW_2_7;
+
+ return 0;
+}
+
+int cdn_dp_software_train_link(struct cdn_dp_device *dp)
+{
+ int ret, stop_err;
+ u8 link_config[2];
+ u32 rate, sink_max, source_max;
+
+ 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;
+ }
+
+ 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);
+
+retry:
+ /* 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);
+
+ ret = cdn_dp_link_training_clock_recovery(dp);
+ if (ret) {
+ if (cdn_dp_get_lower_link_rate(dp)) {
+ DRM_ERROR("training clock recovery fail, err: %d\n",
+ ret);
+ goto stop_training;
+ }
+
+ /* use lower link rate to retraining */
+ goto retry;
+ }
+
+ ret = cdn_dp_link_training_channel_equalization(dp);
+ if (ret) {
+ if (cdn_dp_get_lower_link_rate(dp)) {
+ DRM_ERROR("training channel equalization fail, err: %d\n",
+ ret);
+ goto stop_training;
+ }
+
+ /* use lower link rate to retraining */
+ goto retry;
+ }
+
+stop_training:
+ 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 979355d..e1273e6 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];

@@ -609,6 +611,31 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
{
int ret;

+ /*
+ * 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.
+ */
+ 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;
+ }
+ ret = cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev,
+ "Failed to write SOURCE_HDTX_CAR register %d\n", ret);
+ goto do_fw_training;
+ }
+ dp->use_fw_training = false;
+ return 0;
+
+do_fw_training:
+ dp->use_fw_training = true;
+ DRM_DEV_DEBUG_KMS(dp->dev, "use fw training\n");
ret = cdn_dp_training_start(dp);
if (ret) {
DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
@@ -623,7 +650,7 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)

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 6580b11..3420771 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 RG_EN (0 << 4)
+#define GLOBAL_EN BIT(3)
+#define NO_VIDEO BIT(5)
+#define ENC_RST_DIS BIT(6)
+#define WR_VHSYNC_FALL BIT(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


2018-05-14 09:55:22

by Lin Huang

[permalink] [raw]
Subject: [PATCH v3 2/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY

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

Signed-off-by: Lin Huang <[email protected]>
---
Changes in v2:
- rebase
Changes in v3:
- modify property description and add this property to example

.../devicetree/bindings/phy/phy-rockchip-typec.txt | 29 +++++++++++++++++++++-
1 file changed, 28 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..af298f2 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -17,7 +17,8 @@ Required properties:

Optional properties:
- extcon : extcon specifier for the Power Delivery
-
+ - rockchip,phy_config : A list of voltage swing(mv) and pre-emphasis
+ (dB) pairs.
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:
@@ -50,6 +51,19 @@ Example:
<&cru SRST_P_UPHY0_TCPHY>;
reset-names = "uphy", "uphy-pipe", "uphy-tcphy";

+ rockchip,phy_config =<0x2a 0x00
+ 0x1f 0x15
+ 0x14 0x22
+ 0x02 0x2b
+ 0x21 0x00
+ 0x12 0x15
+ 0x02 0x22
+ 0 0
+ 0x15 0x00
+ 0x00 0x15
+ 0 0
+ 0 0>;
+
tcphy0_dp: dp-port {
#phy-cells = <0>;
};
@@ -74,6 +88,19 @@ Example:
<&cru SRST_P_UPHY1_TCPHY>;
reset-names = "uphy", "uphy-pipe", "uphy-tcphy";

+ rockchip,phy_config =<0x2a 0x00
+ 0x1f 0x15
+ 0x14 0x22
+ 0x02 0x2b
+ 0x21 0x00
+ 0x12 0x15
+ 0x02 0x22
+ 0 0
+ 0x15 0x00
+ 0x00 0x15
+ 0 0
+ 0 0>;
+
tcphy1_dp: dp-port {
#phy-cells = <0>;
};
--
2.7.4


2018-05-14 17:03:33

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH v3 3/4] phy: rockchip-typec: support variable phy config value

On Mon, May 14, 2018 at 05:53:54PM +0800, Lin Huang wrote:
> 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 <[email protected]>
> Signed-off-by: Lin Huang <[email protected]>
> ---
> Changes in v2:
> - update patch following Enric suggest
> Changes in v3:
> - delete need_software_training variable
> - add default phy config value, if dts do not define phy config value, use these value
>
> drivers/phy/rockchip/phy-rockchip-typec.c | 305 ++++++++++++++++++++----------
> include/soc/rockchip/rockchip_phy_typec.h | 63 ++++++
> 2 files changed, 270 insertions(+), 98 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..10253ad 100644
> --- a/drivers/phy/rockchip/phy-rockchip-typec.c
> +++ b/drivers/phy/rockchip/phy-rockchip-typec.c

/snip

>
> +/* default phy config */
> +struct phy_config configs[3][4] = {

static const

Also, configs isn't a good name. How about tcphy_default_config?


> + {{ 0x2a, 0x00 },

Can you please expand the assignment for all of these, ie:

{ .swing = 0x2a, .pe = 0x00 },

> + { 0x1f, 0x15 },
> + { 0x14, 0x22 },
> + { 0x02, 0x2b } },
> +
> + {{ 0x21, 0x00 },
> + { 0x12, 0x15 },
> + { 0x02, 0x22 },
> + { 0, 0 } },
> +
> + {{ 0x15, 0x00 },
> + { 0x00, 0x15 },
> + { 0, 0 },
> + { 0, 0 } },
> +};
> +

/snip

--
Sean Paul, Software Engineer, Google / Chromium OS

2018-05-14 17:17:42

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH v3 4/4] drm/rockchip: support dp training outside dp firmware

On Mon, May 14, 2018 at 05:53:55PM +0800, Lin Huang wrote:
> 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.
>
> Signed-off-by: Chris Zhong <[email protected]>
> Signed-off-by: Lin Huang <[email protected]>
> ---
> Changes in v2:
> - update patch following Enric suggest
> Changes in v3:
> - use variable fw_training instead sw_training_success
> - base on DP SPCE, if training fail use lower link rate to retry training
>
> drivers/gpu/drm/rockchip/Makefile | 3 +-
> drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +-
> drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 +
> drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 416 ++++++++++++++++++++++++
> drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +-
> drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++-
> 6 files changed, 501 insertions(+), 13 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 cce64c1..d9d0d4d 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->use_fw_training == true) {
> + 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,15 @@ 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->use_fw_training == true) {
> + 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..77a9793 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 use_fw_training;
>
> 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..b8fd5bc
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c

/snip

> +static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp)
> +{
> + if (dp->link.rate == DP_LINK_BW_1_62)
> + return -EINVAL;
> + else if (dp->link.rate == DP_LINK_BW_2_7)
> + dp->link.rate = DP_LINK_BW_1_62;

Extra indent

> + else
> + dp->link.rate = DP_LINK_BW_2_7;


This is better expressed as a switch statement:


switch (dp->link.rate) {
case DP_LINK_BW_1_62:
return -EINVAL;
case DP_LINK_BW_2_7:
dp->link.rate = DP_LINK_BW_1_62:
break;
default:
dp->link.rate = DP_LINK_BW_2_7:
break;
}

You might also consider adding an additional case since there are rates higher
than 5.4GHz. ie:
case DP_LINK_BW_5_4:
dp->link.rate = DP_LINK_BW_2_7:
break;
default:
dp->link.rate = DP_LINK_BW_5_4:
break;


> +
> + return 0;
> +}
> +
> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
> +{
> + int ret, stop_err;
> + u8 link_config[2];
> + u32 rate, sink_max, source_max;
> +
> + 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;
> + }
> +
> + 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);
> +
> +retry:
> + /* 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);
> +
> + ret = cdn_dp_link_training_clock_recovery(dp);
> + if (ret) {
> + if (cdn_dp_get_lower_link_rate(dp)) {
> + DRM_ERROR("training clock recovery fail, err: %d\n",
> + ret);
> + goto stop_training;
> + }
> +
> + /* use lower link rate to retraining */
> + goto retry;
> + }
> +
> + ret = cdn_dp_link_training_channel_equalization(dp);
> + if (ret) {
> + if (cdn_dp_get_lower_link_rate(dp)) {
> + DRM_ERROR("training channel equalization fail, err: %d\n",
> + ret);
> + goto stop_training;
> + }
> +
> + /* use lower link rate to retraining */
> + goto retry;
> + }
> +
> +stop_training:
> + 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;

Using labels to do loops reduces readability, it seems like you can also pull
out the downspread control write.

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);

while (true) {
/* 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);

ret = cdn_dp_link_training_clock_recovery(dp);
if (ret) {
if (!cdn_dp_get_lower_link_rate(dp))
continue;

DRM_ERROR("training clock recovery failed: %d\n", ret);
break;
}

ret = cdn_dp_link_training_channel_equalization(dp);
if (ret) {
if (!cdn_dp_get_lower_link_rate(dp))
continue;

DRM_ERROR("training channel eq failed: %d\n", ret);
break;
}

return 0;
}

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;

> +
> +stop_training:
> + 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 979355d..e1273e6 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];
>
> @@ -609,6 +611,31 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
> {
> int ret;
>
> + /*
> + * 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

This comment is no longer accurate.

> + * fail, keep firmware training as a fallback if sw training fails.
> + */
> + 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;
> + }
> + ret = cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
> + if (ret) {
> + DRM_DEV_ERROR(dp->dev,
> + "Failed to write SOURCE_HDTX_CAR register %d\n", ret);
> + goto do_fw_training;
> + }
> + dp->use_fw_training = false;
> + return 0;
> +
> +do_fw_training:
> + dp->use_fw_training = true;
> + DRM_DEV_DEBUG_KMS(dp->dev, "use fw training\n");
> ret = cdn_dp_training_start(dp);
> if (ret) {
> DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
> @@ -623,7 +650,7 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
>
> 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)
>

/snip

--
Sean Paul, Software Engineer, Google / Chromium OS

2018-05-14 20:31:23

by Enric Balletbo Serra

[permalink] [raw]
Subject: Re: [PATCH v3 1/4] drm/rockchip: add transfer function for cdn-dp

Hi Lin,

2018-05-14 11:53 GMT+02:00 Lin Huang <[email protected]>:
> From: Chris Zhong <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Lin Huang <[email protected]>
> Reviewed-by: Sean Paul <[email protected]>
> Reviewed-by: Enric Balletbo <[email protected]>

If you need to send another version, could you use the following name
and email address instead?

Reviewed-by: Enric Balletbo i Serra <[email protected]>

Thanks :)
Enric

> ---
> Changes in v2:
> - update patch following Enric suggest
> - None
> 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 | 69 ++++++++++++++++++++++++++++++----
> drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++++++-
> 4 files changed, 122 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..cce64c1 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_STATUS_ACK)
> + msg->reply = DP_AUX_NATIVE_REPLY_ACK;
> + else if (status == AUX_STATUS_NACK)
> + msg->reply = DP_AUX_NATIVE_REPLY_NACK;
> + else if (status == AUX_STATUS_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..979355d 100644
> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -221,7 +221,12 @@ 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 was
> + * not send successfully;
> + */
> +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 +252,41 @@ 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 was not send
> + * success; -EINVAL is returned if get the wrong data size after message
> + * is sent
> + */
> +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 +299,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 +312,33 @@ 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..6580b11 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_STATUS_ACK 0
> +#define AUX_STATUS_NACK 1
> +#define AUX_STATUS_DEFER 2
> +#define AUX_STATUS_SINK_ERROR 3
> +#define AUX_STATUS_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
>