2018-07-03 10:04:29

by Damian Kos

[permalink] [raw]
Subject: [PATCH 00/12] drm: add support for Cadence MHDP DPI/DP bridge.

Damian Kos (3):
dt-bindings: drm/bridge: Document Cadence MHDP bridge bindings
drm/rockchip: added implementation for a few FW commands.
drm/rockchip: add support for CDNS MHDP IP controller.

Quentin Schulz (9):
HACK: increase timeout for drm_atomic_helper_wait_for_vblanks
drm/dp: make dp_link_status and dp_get_lane_status usable from
outside of the core
drm/dp: add helpers for drm_dp_set_adjust_request_pre_emphasis and
drm_dp_set_adjust_request_voltage
drm/dp: fix training interval formula for DP 1.3+
drm/dp: fix link probing for devices supporting DP 1.4+
drm/dp: fix drm_dp_link_power_* for DP 1.2+
drm/dp: fix drm_dp_link_train_clock_recovery_delay for DP 1.4
drm/dp: add max number of lanes supported
drm/dp: add pixel encoding and colorimetry format indicator field in
MISC1

.../bindings/display/bridge/cdns,mhdp.txt | 39 +
drivers/gpu/drm/drm_atomic_helper.c | 2 +-
drivers/gpu/drm/drm_dp_helper.c | 97 ++-
drivers/gpu/drm/rockchip/cdn-dp-core.c | 953 +++++++++++++++++++-
drivers/gpu/drm/rockchip/cdn-dp-core.h | 25 +
drivers/gpu/drm/rockchip/cdn-dp-reg.c | 167 ++++-
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 147 +++-
include/drm/drm_dp_helper.h | 14 +-
8 files changed, 1400 insertions(+), 44 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt



2018-07-03 10:04:10

by Damian Kos

[permalink] [raw]
Subject: [PATCH 01/12] HACK: increase timeout for drm_atomic_helper_wait_for_vblanks

From: Quentin Schulz <[email protected]>

Due to the DP IP being simulated in a different and way slower way than
the rest of the hardware, increase the timeout in
drm_atomic_helper_wait_for_vblanks so that we don't miss the vblank
event.

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/drm_atomic_helper.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index c356545..0171249 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1345,7 +1345,7 @@ int drm_atomic_helper_wait_for_fences(struct drm_device *dev,
ret = wait_event_timeout(dev->vblank[i].queue,
old_state->crtcs[i].last_vblank_count !=
drm_crtc_vblank_count(crtc),
- msecs_to_jiffies(50));
+ msecs_to_jiffies(4000));

WARN(!ret, "[CRTC:%d:%s] vblank wait timed out\n",
crtc->base.id, crtc->name);
--
1.7.1


2018-07-03 10:04:30

by Damian Kos

[permalink] [raw]
Subject: [PATCH 03/12] drm/dp: add helpers for drm_dp_set_adjust_request_pre_emphasis and drm_dp_set_adjust_request_voltage

From: Quentin Schulz <[email protected]>

We already have functions to get the adjust request voltage and
pre-emphasis for a lane so it makes also sense to be able to set them so
that we can then easily update them via a DPCD write.

Add helpers for drm_dp_set_adjust_request_pre_emphasis and
drm_dp_set_adjust_request_voltage that respectively set the
pre-emphasis and voltage of a lane in the link status array.

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/drm_dp_helper.c | 28 ++++++++++++++++++++++++++++
include/drm/drm_dp_helper.h | 4 ++++
2 files changed, 32 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 3bc2e98..ca2f469 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -120,6 +120,34 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SI
}
EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);

+void drm_dp_set_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
+ int lane, u8 volt)
+{
+ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+ int s = ((lane & 1) ?
+ DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+ DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+ int idx = i - DP_LANE0_1_STATUS;
+
+ link_status[idx] &= ~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << s);
+ link_status[idx] |= volt << s;
+}
+EXPORT_SYMBOL(drm_dp_set_adjust_request_voltage);
+
+void drm_dp_set_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
+ int lane, u8 pre_emphasis)
+{
+ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+ int s = ((lane & 1) ?
+ DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+ int idx = i - DP_LANE0_1_STATUS;
+
+ link_status[idx] &= ~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << s);
+ link_status[idx] |= pre_emphasis << s;
+}
+EXPORT_SYMBOL(drm_dp_set_adjust_request_pre_emphasis);
+
void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
udelay(100);
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index a488af0..6e64b2a 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -946,6 +946,10 @@ u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
int lane);
u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
int lane);
+void drm_dp_set_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
+ int lane, u8 volt);
+void drm_dp_set_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
+ int lane, u8 pre_emphasis);

#define DP_BRANCH_OUI_HEADER_SIZE 0xc
#define DP_RECEIVER_CAP_SIZE 0xf
--
1.7.1


2018-07-03 10:05:17

by Damian Kos

[permalink] [raw]
Subject: [PATCH 11/12] drm/rockchip: added implementation for a few FW commands.

Added support for a register read, register write and register field write
commands.
Added support for adjust link training command.
Updated cdn_dp_get_event function, so it reads all SW event registers.
Added definitions mostly for Framer and Streamer.

Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/rockchip/cdn-dp-reg.c | 167 ++++++++++++++++++++++++++++++++-
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 143 +++++++++++++++++++++++++++-
2 files changed, 305 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index eb3042c..b061cfc 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -410,7 +410,10 @@ int cdn_dp_event_config(struct cdn_dp_device *dp)

u32 cdn_dp_get_event(struct cdn_dp_device *dp)
{
- return readl(dp->regs + SW_EVENTS0);
+ return readl(dp->regs + SW_EVENTS0)
+ | (readl(dp->regs + SW_EVENTS1) << 8)
+ | (readl(dp->regs + SW_EVENTS2) << 16)
+ | (readl(dp->regs + SW_EVENTS3) << 24);
}

int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
@@ -981,3 +984,165 @@ int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio)
DRM_DEV_ERROR(dp->dev, "audio config failed: %d\n", ret);
return ret;
}
+
+int cdn_dp_register_read(struct cdn_dp_device *dp, u32 addr, u32 *value)
+{
+ u8 msg[4], resp[8];
+ int ret;
+
+ if (addr == 0) {
+ ret = -EINVAL;
+ goto err_register_read;
+ }
+
+ msg[0] = (u8)(addr >> 24);
+ msg[1] = (u8)(addr >> 16);
+ msg[2] = (u8)(addr >> 8);
+ msg[3] = (u8)addr;
+
+ ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+ GENERAL_REGISTER_READ,
+ sizeof(msg), msg);
+ if (ret)
+ goto err_register_read;
+
+ ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_GENERAL,
+ GENERAL_REGISTER_READ,
+ sizeof(resp));
+ if (ret)
+ goto err_register_read;
+
+ ret = cdn_dp_mailbox_read_receive(dp, resp, sizeof(resp));
+ if (ret)
+ goto err_register_read;
+
+ /* Returned address value should be the same as requested */
+ if (memcmp(msg, resp, sizeof(msg))) {
+ ret = -EINVAL;
+ goto err_register_read;
+ }
+
+ *value = (resp[4] << 24) | (resp[5] << 16) | (resp[6] << 8) | resp[7];
+
+err_register_read:
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev, "Failed to read register.\n");
+ *value = 0;
+ }
+
+ return ret;
+}
+
+int cdn_dp_register_write(struct cdn_dp_device *dp, u32 addr, u32 value)
+{
+ u8 msg[8];
+ int ret;
+
+ if (addr == 0)
+ return -EINVAL;
+
+ msg[0] = (u8)(addr >> 24);
+ msg[1] = (u8)(addr >> 16);
+ msg[2] = (u8)(addr >> 8);
+ msg[3] = (u8)addr;
+ msg[4] = (u8)(value >> 24);
+ msg[5] = (u8)(value >> 16);
+ msg[6] = (u8)(value >> 8);
+ msg[7] = (u8)value;
+
+ ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+ GENERAL_REGISTER_WRITE,
+ sizeof(msg), msg);
+ if (ret)
+ DRM_DEV_ERROR(dp->dev, "Failed to write register.\n");
+
+ return ret;
+}
+
+int cdn_dp_register_write_field(struct cdn_dp_device *dp, u32 addr,
+ u8 index, u8 nbits, u32 value)
+{
+ u8 msg[10];
+ int ret;
+
+ if (addr == 0)
+ return -EINVAL;
+
+ msg[0] = (u8)(addr >> 24);
+ msg[1] = (u8)(addr >> 16);
+ msg[2] = (u8)(addr >> 8);
+ msg[3] = (u8)addr;
+ msg[4] = index;
+ msg[5] = nbits;
+ msg[6] = (u8)(value >> 24);
+ msg[7] = (u8)(value >> 16);
+ msg[8] = (u8)(value >> 8);
+ msg[9] = (u8)value;
+
+ ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+ GENERAL_REGISTER_WRITE_FIELD,
+ sizeof(msg), msg);
+ if (ret)
+ DRM_DEV_ERROR(dp->dev, "Failed to write register field.\n");
+
+ return ret;
+}
+/* rep should be a pointer already allocated with .regs of size 6 */
+int cdn_dp_adjust_lt(struct cdn_dp_device *dp, u8 nlanes,
+ u16 udelay, u8 *lanes_data,
+ u8 *dpcd)
+{
+ u8 payload[10];
+ u8 hdr[5]; /* For DPCD read response header */
+ u32 addr;
+ u8 const nregs = 6; /* Registers 0x202-0x207 */
+ int ret;
+
+ if (nlanes != 4 && nlanes != 2 && nlanes != 1) {
+ DRM_DEV_ERROR(dp->dev, "invalid number of lanes: %d\n", nlanes);
+ ret = -EINVAL;
+ goto err_adjust_lt;
+ }
+
+ payload[0] = nlanes;
+ payload[1] = (u8)(udelay >> 8);
+ payload[2] = (u8)udelay;
+
+ payload[3] = lanes_data[0];
+ if (nlanes > 1)
+ payload[4] = lanes_data[1];
+ if (nlanes > 2) {
+ payload[5] = lanes_data[2];
+ payload[6] = lanes_data[3];
+ }
+
+ ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+ DPTX_ADJUST_LT,
+ sizeof(payload), payload);
+ if (ret)
+ goto err_adjust_lt;
+
+ /* Yes, read the DPCD read command response */
+ ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_DPCD,
+ sizeof(hdr) + nregs);
+ if (ret)
+ goto err_adjust_lt;
+
+ ret = cdn_dp_mailbox_read_receive(dp, hdr, sizeof(hdr));
+ if (ret)
+ goto err_adjust_lt;
+
+ addr = (hdr[2] << 24) | (hdr[3] << 8) | hdr[4];
+ if (addr != DP_LANE0_1_STATUS)
+ goto err_adjust_lt;
+
+ ret = cdn_dp_mailbox_read_receive(dp, dpcd, nregs);
+
+err_adjust_lt:
+ if (ret)
+ DRM_DEV_ERROR(dp->dev, "Failed to adjust Link Training.\n");
+
+ return ret;
+}
+
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index c4bbb4a..b5472ad 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -156,6 +156,14 @@
#define DP_FRONT_BACK_PORCH 0x2278
#define DP_BYTE_COUNT 0x227c

+/* dptx framer global config fields */
+#define DP_FRAMER_NUM_LANES(x) (x - 1)
+#define DP_FRAMER_EN BIT(3)
+#define DP_FRAMER_RATE_GOVERNOR_EN BIT(4)
+#define DP_FRAMER_NO_VIDEO_MODE BIT(5)
+#define DP_FRAMER_DISABLE_PHY_RST BIT(6)
+#define DP_FRAMER_WR_FAILING_EDGE_VSYNC BIT(7)
+
/* dptx stream addr */
#define MSA_HORIZONTAL_0 0x2280
#define MSA_HORIZONTAL_1 0x2284
@@ -323,10 +331,13 @@
#define MB_MODULE_ID_GENERAL 0x0a

/* general opcode */
-#define GENERAL_MAIN_CONTROL 0x01
-#define GENERAL_TEST_ECHO 0x02
-#define GENERAL_BUS_SETTINGS 0x03
-#define GENERAL_TEST_ACCESS 0x04
+#define GENERAL_MAIN_CONTROL 0x01
+#define GENERAL_TEST_ECHO 0x02
+#define GENERAL_BUS_SETTINGS 0x03
+#define GENERAL_TEST_ACCESS 0x04
+#define GENERAL_REGISTER_WRITE 0x05
+#define GENERAL_REGISTER_WRITE_FIELD 0x06
+#define GENERAL_REGISTER_READ 0x07

#define DPTX_SET_POWER_MNG 0x00
#define DPTX_SET_HOST_CAPABILITIES 0x01
@@ -346,6 +357,7 @@
#define DPTX_SET_LINK_BREAK_POINT 0x0f
#define DPTX_FORCE_LANES 0x10
#define DPTX_HPD_STATE 0x11
+#define DPTX_ADJUST_LT 0x12

#define FW_STANDBY 0
#define FW_ACTIVE 1
@@ -424,6 +436,122 @@
/* Reference cycles when using lane clock as reference */
#define LANE_REF_CYC 0x8000

+#define CDN_DPTX_FRAMER 0x02200
+#define CDN_DP_FRAMER_GLOBAL_CONFIG (CDN_DPTX_FRAMER + 0x00)
+#define CDN_DP_NUM_LANES(x) (x - 1)
+#define CDN_DP_FRAMER_EN BIT(3)
+#define CDN_DP_RATE_GOVERNOR_EN BIT(4)
+#define CDN_DP_NO_VIDEO_MODE BIT(5)
+#define CDN_DP_DISABLE_PHY_RST BIT(6)
+#define CDN_DP_WR_FAILING_EDGE_VSYNC BIT(7)
+
+#define CDN_DP_SW_RESET (CDN_DPTX_FRAMER + 0x04)
+#define CDN_DP_FRAMER_TU (CDN_DPTX_FRAMER + 0x08)
+#define CDN_DP_FRAMER_TU_SIZE(x) (((x) & GENMASK(6, 0)) << 8)
+#define CDN_DP_FRAMER_TU_VS(x) ((x) & GENMASK(5, 0))
+#define CDN_DP_FRAMER_TU_CNT_RST_EN BIT(15)
+
+#define CDN_DPTX_STREAM 0x03000
+#define CDN_DP_MSA_HORIZONTAL_0 (CDN_DPTX_STREAM + 0x00)
+#define CDN_DP_MSAH0_H_TOTAL(x) (x)
+#define CDN_DP_MSAH0_HSYNC_START(x) ((x) << 16)
+
+#define CDN_DP_MSA_HORIZONTAL_1 (CDN_DPTX_STREAM + 0x04)
+#define CDN_DP_MSAH1_HSYNC_WIDTH(x) (x)
+#define CDN_DP_MSAH1_HSYNC_POL_LOW BIT(15)
+#define CDN_DP_MSAH1_HDISP_WIDTH(x) ((x) << 16)
+
+#define CDN_DP_MSA_VERTICAL_0 (CDN_DPTX_STREAM + 0x08)
+#define CDN_DP_MSAV0_V_TOTAL(x) (x)
+#define CDN_DP_MSAV0_VSYNC_START(x) ((x) << 16)
+
+#define CDN_DP_MSA_VERTICAL_1 (CDN_DPTX_STREAM + 0x0c)
+#define CDN_DP_MSAV1_VSYNC_WIDTH(x) (x)
+#define CDN_DP_MSAV1_VSYNC_POL_LOW BIT(15)
+#define CDN_DP_MSAV1_VDISP_WIDTH(x) ((x) << 16)
+
+#define CDN_DP_MSA_MISC (CDN_DPTX_STREAM + 0x10)
+#define CDN_DP_STREAM_CONFIG (CDN_DPTX_STREAM + 0x14)
+#define CDN_DP_RATE_GOVERNOR_STATUS (CDN_DPTX_STREAM + 0x2c)
+#define CDN_DP_RG_TU_VS_DIFF(x) ((x) << 8)
+
+#define CDN_DP_HORIZONTAL (CDN_DPTX_STREAM + 0x30)
+#define CDN_DP_H_HSYNC_WIDTH(x) (x)
+#define CDN_DP_H_H_TOTAL(x) ((x) << 16)
+
+#define CDN_DP_VERTICAL_0 (CDN_DPTX_STREAM + 0x34)
+#define CDN_DP_V0_VHEIGHT(x) (x)
+#define CDN_DP_V0_VSTART(x) ((x) << 16)
+
+#define CDN_DP_VERTICAL_1 (CDN_DPTX_STREAM + 0x38)
+#define CDN_DP_V1_VTOTAL(x) (x)
+#define CDN_DP_V1_VTOTAL_EVEN BIT(16)
+
+#define CDN_DP_FRAMER_PXL_REPR (CDN_DPTX_STREAM + 0x4c)
+#define CDN_DP_FRAMER_6_BPC BIT(0)
+#define CDN_DP_FRAMER_8_BPC BIT(1)
+#define CDN_DP_FRAMER_10_BPC BIT(2)
+#define CDN_DP_FRAMER_12_BPC BIT(3)
+#define CDN_DP_FRAMER_16_BPC BIT(4)
+#define CDN_DP_FRAMER_PXL_FORMAT 0x8
+#define CDN_DP_FRAMER_RGB BIT(0)
+#define CDN_DP_FRAMER_YCBCR444 BIT(1)
+#define CDN_DP_FRAMER_YCBCR422 BIT(2)
+#define CDN_DP_FRAMER_YCBCR420 BIT(3)
+#define CDN_DP_FRAMER_Y_ONLY BIT(4)
+
+#define CDN_DP_FRAMER_SP (CDN_DPTX_STREAM + 0x10)
+#define CDN_DP_FRAMER_VSYNC_POL_LOW BIT(0)
+#define CDN_DP_FRAMER_HSYNC_POL_LOW BIT(1)
+#define CDN_DP_FRAMER_INTERLACE BIT(2)
+
+#define CDN_DP_LINE_THRESH (CDN_DPTX_STREAM + 0x64)
+#define CDN_DP_VB_ID (CDN_DPTX_STREAM + 0x68)
+#define CDN_DP_VB_ID_INTERLACED BIT(2)
+
+#define CDN_DP_FRONT_BACK_PORCH (CDN_DPTX_STREAM + 0x78)
+#define CDN_DP_BACK_PORCH(x) (x)
+#define CDN_DP_FRONT_PORCH(x) ((x) << 16)
+
+#define CDN_DP_BYTE_COUNT (CDN_DPTX_STREAM + 0x7c)
+
+#define CDN_DPTX_GLOBAL 0x02300
+#define CDN_DP_LANE_EN (CDN_DPTX_GLOBAL + 0x00)
+#define CDN_DP_LANE_EN_LANES(x) GENMASK(x - 1, 0)
+#define CDN_DP_ENHNCD (CDN_DPTX_GLOBAL + 0x04)
+
+#define CDN_SOURCE_VIDEO_INTERFACE 0x00b00
+#define CDN_BND_HSYNC2VSYNC (CDN_SOURCE_VIDEO_INTERFACE + 0x00)
+#define CDN_IP_DTCT_WIN GENMASK(11, 0)
+#define CDN_IP_DET_INTERLACE_FORMAT BIT(12)
+#define CDN_IP_BYPASS_V_INTERFACE BIT(13)
+
+#define CDN_HSYNC2VSYNC_POL_CTRL (CDN_SOURCE_VIDEO_INTERFACE + 0x10)
+#define CDN_H2V_HSYNC_POL_ACTIVE_LOW BIT(1)
+#define CDN_H2V_VSYNC_POL_ACTIVE_LOW BIT(2)
+
+#define CDN_DPTX_PHY_CONFIG 0x02000
+#define CDN_PHY_TRAINING_EN BIT(0)
+#define CDN_PHY_TRAINING_TYPE(x) (((x) & GENMASK(3, 0)) << 1)
+#define CDN_PHY_SCRAMBLER_BYPASS BIT(5)
+#define CDN_PHY_ENCODER_BYPASS BIT(6)
+#define CDN_PHY_SKEW_BYPASS BIT(7)
+#define CDN_PHY_TRAINING_AUTO BIT(8)
+#define CDN_PHY_LANE0_SKEW(x) (((x) & GENMASK(2, 0)) << 9)
+#define CDN_PHY_LANE1_SKEW(x) (((x) & GENMASK(2, 0)) << 12)
+#define CDN_PHY_LANE2_SKEW(x) (((x) & GENMASK(2, 0)) << 15)
+#define CDN_PHY_LANE3_SKEW(x) (((x) & GENMASK(2, 0)) << 18)
+#define CDN_PHY_COMMON_CONFIG (CDN_PHY_LANE1_SKEW(1) | CDN_PHY_LANE2_SKEW(2) | CDN_PHY_LANE3_SKEW(3))
+#define CDN_PHY_10BIT_EN BIT(21)
+
+#define CDN_PRE_EMPHASIS(x) ((x) & GENMASK(1, 0))
+#define CDN_FORCE_PRE_EMPHASIS BIT(2)
+
+#define CDN_VOLT_SWING(x) ((x) & GENMASK(1, 0))
+#define CDN_FORCE_VOLT_SWING BIT(2)
+
+#define CDN_DP_TRAINING_PATTERN_4 0x7
+
enum voltage_swing_level {
VOLTAGE_LEVEL_0,
VOLTAGE_LEVEL_1,
@@ -479,4 +607,11 @@ int cdn_dp_get_edid_block(void *dp, u8 *edid,
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_register_read(struct cdn_dp_device *dp, u32 addr, u32 *value);
+int cdn_dp_register_write(struct cdn_dp_device *dp, u32 addr, u32 value);
+int cdn_dp_register_write_field(struct cdn_dp_device *dp, u32 addr,
+ u8 index, u8 nbits, u32 value);
+int cdn_dp_adjust_lt(struct cdn_dp_device *dp, u8 nlanes,
+ u16 udelay, u8 *lanes_data,
+ u8 *dpcd);
#endif /* _CDN_DP_REG_H */
--
1.7.1


2018-07-03 10:05:34

by Damian Kos

[permalink] [raw]
Subject: [PATCH 07/12] drm/dp: fix drm_dp_link_train_clock_recovery_delay for DP 1.4

From: Quentin Schulz <[email protected]>

In DP 1.4, interval between link status and adjust request read for the
clock recovery phase is fixed to 100us whatever the value of the
register is.

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/drm_dp_helper.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index b6a27ab..92f3880 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -152,6 +152,11 @@ void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
unsigned int training_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
DP_TRAINING_AUX_RD_INTERVAL_MASK;

+ if (dpcd[DP_DPCD_REV] == 0x14) {
+ udelay(100);
+ return;
+ }
+
if (training_interval == 0)
udelay(100);
else
--
1.7.1


2018-07-03 10:05:40

by Damian Kos

[permalink] [raw]
Subject: [PATCH 12/12] drm/rockchip: add support for CDNS MHDP IP controller.

Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/rockchip/cdn-dp-core.c | 953 +++++++++++++++++++++++++++++++-
drivers/gpu/drm/rockchip/cdn-dp-core.h | 25 +
drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 +-
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 4 +
4 files changed, 960 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index c6fbdcd..64e3ab0 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -18,6 +18,7 @@
#include <drm/drm_dp_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
+#include <drm/drm_bridge.h>

#include <linux/clk.h>
#include <linux/component.h>
@@ -27,6 +28,7 @@
#include <linux/reset.h>
#include <linux/mfd/syscon.h>
#include <linux/phy/phy.h>
+#include <linux/iopoll.h>

#include <sound/hdmi-codec.h>

@@ -49,7 +51,11 @@

#define CDN_FW_TIMEOUT_MS (64 * 1000)
#define CDN_DPCD_TIMEOUT_MS 5000
-#define CDN_DP_FIRMWARE "rockchip/dptx.bin"
+#define RK_DP_FIRMWARE "rockchip/dptx.bin"
+#define CDN_DP_FIRMWARE "cadence/dptx.bin"
+
+#define FW_ALIVE_TIMEOUT_US 1000000
+#define HPD_EVENT_TIMEOUT 40000

struct cdn_dp_data {
u8 max_phy;
@@ -62,7 +68,8 @@ struct cdn_dp_data rk3399_cdn_dp = {
static const struct of_device_id cdn_dp_dt_ids[] = {
{ .compatible = "rockchip,rk3399-cdn-dp",
.data = (void *)&rk3399_cdn_dp },
- {}
+ { .compatible = "cdns,mhdp", .data = NULL },
+ { /* sentinel */ }
};

MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
@@ -237,17 +244,27 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp)
struct cdn_dp_device *dp = connector_to_dp(connector);
enum drm_connector_status status = connector_status_disconnected;

- mutex_lock(&dp->lock);
- if (dp->connected)
- status = connector_status_connected;
- mutex_unlock(&dp->lock);
+ if (dp->mhdp_ip) {
+ int ret = cdn_dp_get_hpd_status(dp);
+
+ if (ret > 0)
+ status = connector_status_connected;
+ } else {
+ mutex_lock(&dp->lock);
+ if (dp->connected)
+ status = connector_status_connected;
+ mutex_unlock(&dp->lock);
+ }

return status;
}

static void cdn_dp_connector_destroy(struct drm_connector *connector)
{
- drm_connector_unregister(connector);
+ struct cdn_dp_device *dp = connector_to_dp(connector);
+
+ if (!dp->mhdp_ip)
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}

@@ -258,6 +275,7 @@ static void cdn_dp_connector_destroy(struct drm_connector *connector)
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .dpms = drm_helper_connector_dpms,
};

static int cdn_dp_connector_get_modes(struct drm_connector *connector)
@@ -267,7 +285,12 @@ static int cdn_dp_connector_get_modes(struct drm_connector *connector)
int ret = 0;

mutex_lock(&dp->lock);
- edid = dp->edid;
+
+ if (dp->mhdp_ip)
+ edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+ else
+ edid = dp->edid;
+
if (edid) {
DRM_DEV_DEBUG_KMS(dp->dev, "got edid: width[%d] x height[%d]\n",
edid->width_cm, edid->height_cm);
@@ -278,6 +301,7 @@ static int cdn_dp_connector_get_modes(struct drm_connector *connector)
drm_mode_connector_update_edid_property(connector,
edid);
}
+
mutex_unlock(&dp->lock);

return ret;
@@ -360,7 +384,7 @@ static int cdn_dp_firmware_init(struct cdn_dp_device *dp)

ret = cdn_dp_set_firmware_active(dp, true);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "active ucpu failed: %d\n", ret);
+ DRM_DEV_ERROR(dp->dev, "active fw failed: %d\n", ret);
return ret;
}

@@ -706,8 +730,6 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp)
{
struct device *dev = dp->dev;
struct device_node *np = dev->of_node;
- struct platform_device *pdev = to_platform_device(dev);
- struct resource *res;

dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(dp->grf)) {
@@ -715,13 +737,6 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp)
return PTR_ERR(dp->grf);
}

- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dp->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(dp->regs)) {
- DRM_DEV_ERROR(dev, "ioremap reg failed\n");
- return PTR_ERR(dp->regs);
- }
-
dp->core_clk = devm_clk_get(dev, "core-clk");
if (IS_ERR(dp->core_clk)) {
DRM_DEV_ERROR(dev, "cannot get core_clk_dp\n");
@@ -897,7 +912,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp)
mutex_unlock(&dp->lock);

while (time_before(jiffies, timeout)) {
- ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+ ret = request_firmware(&dp->fw, RK_DP_FIRMWARE, dp->dev);
if (ret == -ENOENT) {
msleep(sleep);
sleep *= 2;
@@ -974,8 +989,8 @@ static void cdn_dp_pd_event_work(struct work_struct *work)
}

/* If training result is changed, update the video config */
- if (mode->clock &&
- (rate != dp->link.rate || lanes != dp->link.num_lanes)) {
+ if ((rate != dp->link.rate || lanes != dp->link.num_lanes) &&
+ mode->clock) {
ret = cdn_dp_config_video(dp);
if (ret) {
dp->connected = false;
@@ -1145,10 +1160,870 @@ int cdn_dp_resume(struct device *dev)
return 0;
}

+static inline struct cdn_dp_device *bridge_to_dp(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct cdn_dp_device, bridge);
+}
+
+static unsigned int max_link_rate(struct cdn_mhdp_host host,
+ struct cdn_mhdp_sink sink)
+{
+ return min(host.link_rate, sink.link_rate);
+}
+
+static void cdn_mhdp_link_training_init(struct cdn_dp_device *dp)
+{
+ u32 reg32;
+
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+
+ /* Reset PHY configuration */
+ reg32 = CDN_PHY_COMMON_CONFIG | CDN_PHY_TRAINING_TYPE(1);
+ if (!(dp->host.lanes_cnt & SCRAMBLER_EN))
+ reg32 |= CDN_PHY_SCRAMBLER_BYPASS;
+
+ cdn_dp_register_write(dp, CDN_DPTX_PHY_CONFIG, reg32);
+
+ cdn_dp_register_write(dp, CDN_DP_ENHNCD,
+ dp->sink.enhanced & dp->host.enhanced);
+
+ cdn_dp_register_write(dp, CDN_DP_LANE_EN,
+ CDN_DP_LANE_EN_LANES(dp->link.num_lanes));
+
+ drm_dp_link_configure(&dp->aux, &dp->link);
+
+ cdn_dp_register_write(dp, CDN_DPTX_PHY_CONFIG, CDN_PHY_COMMON_CONFIG |
+ CDN_PHY_TRAINING_EN | CDN_PHY_TRAINING_TYPE(1) |
+ CDN_PHY_SCRAMBLER_BYPASS);
+
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE);
+}
+
+static void cdn_mhdp_get_adjust_train(struct cdn_dp_device *dp,
+ u8 link_status[DP_LINK_STATUS_SIZE],
+ u8 lanes_data[DP_MAX_NUM_LANES])
+{
+ unsigned int i;
+ u8 adjust, max_pre_emphasis, max_volt_swing;
+
+ max_pre_emphasis = CDN_PRE_EMPHASIS(dp->host.pre_emphasis)
+ << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ max_volt_swing = CDN_VOLT_SWING(dp->host.volt_swing);
+
+ for (i = 0; i < dp->link.num_lanes; i++) {
+ adjust = drm_dp_get_adjust_request_voltage(link_status, i);
+ lanes_data[i] = min_t(u8, adjust, max_volt_swing);
+ if (lanes_data[i] != adjust)
+ lanes_data[i] |= DP_TRAIN_MAX_SWING_REACHED;
+
+ adjust = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+ lanes_data[i] |= min_t(u8, adjust, max_pre_emphasis);
+ if ((lanes_data[i] >> DP_TRAIN_PRE_EMPHASIS_SHIFT) != adjust)
+ lanes_data[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+ }
+}
+
+static void cdn_mhdp_adjust_requested_eq(struct cdn_dp_device *dp,
+ u8 link_status[DP_LINK_STATUS_SIZE])
+{
+ unsigned int i;
+ u8 pre, volt, max_pre = CDN_VOLT_SWING(dp->host.volt_swing),
+ max_volt = CDN_PRE_EMPHASIS(dp->host.pre_emphasis);
+
+ for (i = 0; i < dp->link.num_lanes; i++) {
+ volt = drm_dp_get_adjust_request_voltage(link_status, i);
+ pre = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+ if (volt + pre > 3)
+ drm_dp_set_adjust_request_voltage(link_status, i,
+ 3 - pre);
+ if (dp->host.volt_swing & CDN_FORCE_VOLT_SWING)
+ drm_dp_set_adjust_request_voltage(link_status, i,
+ max_volt);
+ if (dp->host.pre_emphasis & CDN_FORCE_PRE_EMPHASIS)
+ drm_dp_set_adjust_request_pre_emphasis(link_status, i,
+ max_pre);
+ }
+}
+
+static bool cdn_mhdp_link_training_channel_eq(struct cdn_dp_device *dp,
+ u8 eq_tps,
+ unsigned int training_interval)
+{
+ u8 lanes_data[DP_MAX_NUM_LANES], fail_counter_short = 0;
+ u8 *dpcd;
+ u32 reg32;
+
+ dpcd = kzalloc(sizeof(u8) * DP_LINK_STATUS_SIZE, GFP_KERNEL);
+
+ dev_dbg(dp->dev, "Link training - Starting EQ phase\n");
+
+ /* Enable link training TPS[eq_tps] in PHY */
+ reg32 = CDN_PHY_COMMON_CONFIG | CDN_PHY_TRAINING_EN |
+ CDN_PHY_TRAINING_TYPE(eq_tps);
+ if (eq_tps != 4)
+ reg32 |= CDN_PHY_SCRAMBLER_BYPASS;
+ cdn_dp_register_write(dp, CDN_DPTX_PHY_CONFIG, reg32);
+
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+ (eq_tps != 4) ? eq_tps | DP_LINK_SCRAMBLING_DISABLE :
+ CDN_DP_TRAINING_PATTERN_4);
+
+ drm_dp_dpcd_read_link_status(&dp->aux, dpcd);
+
+ do {
+ cdn_mhdp_get_adjust_train(dp, dpcd, lanes_data);
+
+ cdn_dp_adjust_lt(dp, dp->link.num_lanes,
+ training_interval, lanes_data, dpcd);
+
+ if (!drm_dp_clock_recovery_ok(dpcd, dp->link.num_lanes))
+ goto err;
+
+ if (drm_dp_channel_eq_ok(dpcd, dp->link.num_lanes)) {
+ dev_dbg(dp->dev,
+ "Link training: EQ phase succeeded\n");
+ kfree(dpcd);
+ return true;
+ }
+
+ fail_counter_short++;
+
+ cdn_mhdp_adjust_requested_eq(dp, dpcd);
+ } while (fail_counter_short < 5);
+
+err:
+ dev_dbg(dp->dev,
+ "Link training - EQ phase failed for %d lanes and %d rate\n",
+ dp->link.num_lanes, dp->link.rate);
+
+ kfree(dpcd);
+ return false;
+}
+
+static void cdn_mhdp_adjust_requested_cr(struct cdn_dp_device *dp,
+ u8 link_status[DP_LINK_STATUS_SIZE],
+ u8 *req_volt, u8 *req_pre)
+{
+ unsigned int i, max_volt = CDN_VOLT_SWING(dp->host.volt_swing),
+ max_pre = CDN_PRE_EMPHASIS(dp->host.pre_emphasis);
+
+ for (i = 0; i < dp->link.num_lanes; i++) {
+ if (dp->host.volt_swing & CDN_FORCE_VOLT_SWING)
+ drm_dp_set_adjust_request_voltage(link_status, i,
+ max_volt);
+ else
+ drm_dp_set_adjust_request_voltage(link_status, i,
+ req_volt[i]);
+
+ if (dp->host.pre_emphasis & CDN_FORCE_PRE_EMPHASIS)
+ drm_dp_set_adjust_request_pre_emphasis(link_status, i,
+ max_pre);
+ else
+ drm_dp_set_adjust_request_pre_emphasis(link_status, i,
+ req_pre[i]);
+ }
+}
+
+static void cdn_mhdp_validate_cr(struct cdn_dp_device *dp, bool *cr_done,
+ bool *same_before_adjust,
+ bool *max_swing_reached,
+ u8 before_cr[DP_LINK_STATUS_SIZE],
+ u8 after_cr[DP_LINK_STATUS_SIZE],
+ u8 *req_volt, u8 *req_pre)
+{
+ unsigned int i;
+ u8 tmp, max_volt = CDN_VOLT_SWING(dp->host.volt_swing),
+ max_pre = CDN_PRE_EMPHASIS(dp->host.pre_emphasis), lane_status;
+ bool same_pre, same_volt;
+
+ *same_before_adjust = false;
+ *max_swing_reached = false;
+ *cr_done = true;
+
+ for (i = 0; i < dp->link.num_lanes; i++) {
+ tmp = drm_dp_get_adjust_request_voltage(after_cr, i);
+ req_volt[i] = min_t(u8, tmp, max_volt);
+
+ tmp = drm_dp_get_adjust_request_pre_emphasis(after_cr, i) >>
+ DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ req_pre[i] = min_t(u8, tmp, max_pre);
+
+ same_pre = (before_cr[i] & DP_TRAIN_PRE_EMPHASIS_MASK) ==
+ (req_pre[i] << DP_TRAIN_PRE_EMPHASIS_SHIFT);
+ same_volt = (before_cr[i] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
+ req_volt[i];
+ if (same_pre && same_volt)
+ *same_before_adjust = true;
+
+ lane_status = drm_dp_get_lane_status(after_cr, i);
+ if (!(lane_status & DP_LANE_CR_DONE)) {
+ *cr_done = false;
+ /* 3.1.5.2 in DP Standard v1.4. Table 3-1 */
+ if (req_volt[i] + req_pre[i] >= 3) {
+ *max_swing_reached = true;
+ return;
+ }
+ }
+ }
+}
+
+static bool cdn_mhdp_link_training_clock_recovery(struct cdn_dp_device *dp)
+{
+ u8 lanes_data[DP_MAX_NUM_LANES], fail_counter_short = 0,
+ fail_counter_cr_long = 0;
+ u8 *dpcd;
+ bool cr_done;
+
+ dpcd = kzalloc(sizeof(u8) * DP_LINK_STATUS_SIZE, GFP_KERNEL);
+
+ dev_dbg(dp->dev, "Link training starting CR phase\n");
+
+ cdn_mhdp_link_training_init(dp);
+
+ drm_dp_dpcd_read_link_status(&dp->aux, dpcd);
+
+ do {
+ u8 requested_adjust_volt_swing[DP_MAX_NUM_LANES] = {},
+ requested_adjust_pre_emphasis[DP_MAX_NUM_LANES] = {};
+ bool same_before_adjust, max_swing_reached;
+
+ cdn_mhdp_get_adjust_train(dp, dpcd, lanes_data);
+
+ cdn_dp_adjust_lt(dp, dp->link.num_lanes, 100,
+ lanes_data, dpcd);
+
+ cdn_mhdp_validate_cr(dp, &cr_done, &same_before_adjust,
+ &max_swing_reached, lanes_data, dpcd,
+ requested_adjust_volt_swing,
+ requested_adjust_pre_emphasis);
+
+ if (max_swing_reached)
+ goto err;
+
+ if (cr_done) {
+ dev_dbg(dp->dev,
+ "Link training: CR phase succeeded\n");
+ kfree(dpcd);
+ return true;
+ }
+
+ /* Not all CR_DONE bits set */
+ fail_counter_cr_long++;
+
+ if (same_before_adjust) {
+ fail_counter_short++;
+ continue;
+ }
+
+ fail_counter_short = 0;
+ /*
+ * Voltage swing/pre-emphasis adjust requested during CR phase
+ */
+ cdn_mhdp_adjust_requested_cr(dp, dpcd,
+ requested_adjust_volt_swing,
+ requested_adjust_pre_emphasis);
+ } while (fail_counter_short < 5 && fail_counter_cr_long < 10);
+
+err:
+ dev_dbg(dp->dev,
+ "Link training: CR phase failed for %d lanes and %d rate\n",
+ dp->link.num_lanes, dp->link.rate);
+
+ kfree(dpcd);
+
+ return false;
+}
+
+static void lower_link_rate(struct drm_dp_link *link)
+{
+ switch (drm_dp_link_rate_to_bw_code(link->rate)) {
+ case DP_LINK_BW_2_7:
+ link->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_1_62);
+ break;
+ case DP_LINK_BW_5_4:
+ link->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_2_7);
+ break;
+ case DP_LINK_BW_8_1:
+ link->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4);
+ break;
+ }
+}
+
+static u8 eq_training_pattern_supported(struct cdn_mhdp_host *host,
+ struct cdn_mhdp_sink *sink)
+{
+ return fls(host->pattern_supp & sink->pattern_supp);
+}
+
+static int cdn_mhdp_link_training(struct cdn_dp_device *dp,
+ unsigned int video_mode,
+ unsigned int training_interval)
+{
+ u32 reg32;
+ u8 eq_tps = eq_training_pattern_supported(&dp->host, &dp->sink);
+
+ while (1) {
+ if (!cdn_mhdp_link_training_clock_recovery(dp)) {
+ if (drm_dp_link_rate_to_bw_code(dp->link.rate) !=
+ DP_LINK_BW_1_62) {
+ dev_dbg(dp->dev,
+ "Reducing link rate during CR phase\n");
+ lower_link_rate(&dp->link);
+ drm_dp_link_configure(&dp->aux, &dp->link);
+
+ continue;
+ } else if (dp->link.num_lanes > 1) {
+ dev_dbg(dp->dev,
+ "Reducing lanes number during CR phase\n");
+ dp->link.num_lanes >>= 1;
+ dp->link.rate = max_link_rate(dp->host,
+ dp->sink);
+ drm_dp_link_configure(&dp->aux, &dp->link);
+
+ continue;
+ }
+
+ dev_dbg(dp->dev,
+ "Link training failed during CR phase\n");
+ goto err;
+ }
+
+ if (cdn_mhdp_link_training_channel_eq(dp, eq_tps,
+ training_interval))
+ break;
+
+ if (dp->link.num_lanes > 1) {
+ dev_dbg(dp->dev,
+ "Reducing lanes number during EQ phase\n");
+ dp->link.num_lanes >>= 1;
+ drm_dp_link_configure(&dp->aux, &dp->link);
+
+ continue;
+ } else if (drm_dp_link_rate_to_bw_code(dp->link.rate) !=
+ DP_LINK_BW_1_62) {
+ dev_dbg(dp->dev,
+ "Reducing link rate during EQ phase\n");
+ lower_link_rate(&dp->link);
+ drm_dp_link_configure(&dp->aux, &dp->link);
+
+ continue;
+ }
+
+ dev_dbg(dp->dev, "Link training failed during EQ phase\n");
+ goto err;
+ }
+
+ dev_dbg(dp->dev, "Link training successful\n");
+
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+ (dp->host.lanes_cnt & SCRAMBLER_EN) ? 0 :
+ DP_LINK_SCRAMBLING_DISABLE);
+
+ /* SW reset DPTX framer */
+ cdn_dp_register_write(dp, CDN_DP_SW_RESET, 1);
+ cdn_dp_register_write(dp, CDN_DP_SW_RESET, 0);
+
+ /* Enable framer */
+ /* FIXME: update when MST supported, BIT(2) */
+ cdn_dp_register_write(dp, CDN_DP_FRAMER_GLOBAL_CONFIG,
+ CDN_DP_FRAMER_EN |
+ CDN_DP_NUM_LANES(dp->link.num_lanes) |
+ CDN_DP_DISABLE_PHY_RST |
+ CDN_DP_WR_FAILING_EDGE_VSYNC |
+ (video_mode ? CDN_DP_NO_VIDEO_MODE : 0));
+
+ /* Reset PHY config */
+ reg32 = CDN_PHY_COMMON_CONFIG | CDN_PHY_TRAINING_TYPE(1);
+ if (!(dp->host.lanes_cnt & SCRAMBLER_EN))
+ reg32 |= CDN_PHY_SCRAMBLER_BYPASS;
+ cdn_dp_register_write(dp, CDN_DPTX_PHY_CONFIG, reg32);
+
+ return 0;
+err:
+ /* Reset PHY config */
+ reg32 = CDN_PHY_COMMON_CONFIG | CDN_PHY_TRAINING_TYPE(1);
+ if (!(dp->host.lanes_cnt & SCRAMBLER_EN))
+ reg32 |= CDN_PHY_SCRAMBLER_BYPASS;
+ cdn_dp_register_write(dp, CDN_DPTX_PHY_CONFIG, reg32);
+
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+
+ return -EIO;
+}
+
+static void cdn_mhdp_enable(struct drm_bridge *bridge)
+{
+ struct cdn_dp_device *dp = bridge_to_dp(bridge);
+ struct drm_display_mode *mode;
+ struct drm_display_info *disp_info = &dp->connector.display_info;
+ enum vic_pxl_encoding_format pxlfmt;
+ int pxlclock;
+ unsigned int rate, tu_size = 30, vs, vs_f, bpp, required_bandwidth,
+ available_bandwidth, dp_framer_sp = 0, msa_horizontal_1,
+ msa_vertical_1, bnd_hsync2vsync, hsync2vsync_pol_ctrl,
+ misc0 = 0, misc1 = 0, line_thresh = 0, pxl_repr,
+ front_porch, back_porch, msa_h0, msa_v0, hsync, vsync,
+ dp_vertical_1, line_thresh1, line_thresh2;
+ u32 reg_rd_resp;
+
+ unsigned int size = DP_RECEIVER_CAP_SIZE, dp_framer_global_config,
+ video_mode, training_interval_us;
+ u8 reg0[size], reg8, amp[2];
+
+ mode = &bridge->encoder->crtc->state->adjusted_mode;
+ pxlclock = mode->crtc_clock;
+
+ /*
+ * Upon power-on reset/device disconnection: [2:0] bits should be 0b001
+ * and [7:5] bits 0b000.
+ */
+ drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, 1);
+
+ drm_dp_link_probe(&dp->aux, &dp->link);
+
+ dev_dbg(dp->dev, "Set sink device power state via DPCD\n");
+ drm_dp_link_power_up(&dp->aux, &dp->link);
+ /* FIXME (CDNS): do we have to wait for 100ms before going on? */
+ mdelay(100);
+
+ dp->sink.link_rate = dp->link.rate;
+ dp->sink.lanes_cnt = dp->link.num_lanes;
+ dp->sink.enhanced = !!(dp->link.capabilities &
+ DP_LINK_CAP_ENHANCED_FRAMING);
+
+ drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, reg0, size);
+
+ dp->sink.pattern_supp = PTS1 | PTS2;
+ if (drm_dp_tps3_supported(reg0))
+ dp->sink.pattern_supp |= PTS3;
+ if (drm_dp_tps4_supported(reg0))
+ dp->sink.pattern_supp |= PTS4;
+
+ dp->sink.fast_link = !!(reg0[DP_MAX_DOWNSPREAD] &
+ DP_NO_AUX_HANDSHAKE_LINK_TRAINING);
+
+ dp->link.rate = max_link_rate(dp->host, dp->sink);
+ dp->link.num_lanes = min_t(u8, dp->sink.lanes_cnt,
+ dp->host.lanes_cnt & GENMASK(2, 0));
+
+ reg8 = reg0[DP_TRAINING_AUX_RD_INTERVAL] &
+ DP_TRAINING_AUX_RD_INTERVAL_MASK;
+ switch (reg8) {
+ case 0:
+ training_interval_us = 400;
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ training_interval_us = 4000 << (reg8 - 1);
+ break;
+ default:
+ dev_err(dp->dev,
+ "wrong training interval returned by DPCD: %d\n",
+ reg8);
+ return;
+ }
+
+ cdn_dp_register_read(dp, CDN_DP_FRAMER_GLOBAL_CONFIG, &reg_rd_resp);
+
+ dp_framer_global_config = reg_rd_resp;
+
+ video_mode = !(dp_framer_global_config & CDN_DP_NO_VIDEO_MODE);
+
+ if (dp_framer_global_config & CDN_DP_FRAMER_EN)
+ cdn_dp_register_write(dp, CDN_DP_FRAMER_GLOBAL_CONFIG,
+ dp_framer_global_config &
+ ~CDN_DP_FRAMER_EN);
+
+ /* Spread AMP if required, enable 8b/10b coding */
+ amp[0] = (dp->host.lanes_cnt & SSC) ? DP_SPREAD_AMP_0_5 : 0;
+ amp[1] = DP_SET_ANSI_8B10B;
+ drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, amp, 2);
+
+ if (dp->host.fast_link & dp->sink.fast_link) {
+ /* FIXME: implement fastlink */
+ DRM_DEV_DEBUG_KMS(dp->dev, "fastlink\n");
+ } else {
+ int lt_result = cdn_mhdp_link_training(dp, video_mode,
+ training_interval_us);
+ if (lt_result) {
+ dev_err(dp->dev, "Link training failed. Exiting.\n");
+ return;
+ }
+ }
+
+ rate = dp->link.rate / 1000;
+
+ /* FIXME: what about Y_ONLY? how is it handled in the kernel? */
+ if (disp_info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ pxlfmt = YCBCR_4_4_4;
+ else if (disp_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ pxlfmt = YCBCR_4_2_2;
+ else if (disp_info->color_formats & DRM_COLOR_FORMAT_YCRCB420)
+ pxlfmt = YCBCR_4_2_0;
+ else
+ pxlfmt = PXL_RGB;
+
+ /* if YCBCR supported and stream not SD, use ITU709 */
+ /* FIXME: handle ITU version with YCBCR420 when supported */
+ if ((pxlfmt == YCBCR_4_4_4 ||
+ pxlfmt == YCBCR_4_2_2) && mode->crtc_vdisplay >= 720)
+ misc0 = DP_YCBCR_COEFFICIENTS_ITU709;
+
+ switch (pxlfmt) {
+ case PXL_RGB:
+ bpp = disp_info->bpc * 3;
+ pxl_repr = CDN_DP_FRAMER_RGB << CDN_DP_FRAMER_PXL_FORMAT;
+ misc0 |= DP_COLOR_FORMAT_RGB;
+ break;
+ case YCBCR_4_4_4:
+ bpp = disp_info->bpc * 3;
+ pxl_repr = CDN_DP_FRAMER_YCBCR444 << CDN_DP_FRAMER_PXL_FORMAT;
+ misc0 |= DP_COLOR_FORMAT_YCbCr444 | DP_TEST_DYNAMIC_RANGE_CEA;
+ break;
+ case YCBCR_4_2_2:
+ bpp = disp_info->bpc * 2;
+ pxl_repr = CDN_DP_FRAMER_YCBCR422 << CDN_DP_FRAMER_PXL_FORMAT;
+ misc0 |= DP_COLOR_FORMAT_YCbCr422 | DP_TEST_DYNAMIC_RANGE_CEA;
+ break;
+ case YCBCR_4_2_0:
+ bpp = disp_info->bpc * 3 / 2;
+ pxl_repr = CDN_DP_FRAMER_YCBCR420 << CDN_DP_FRAMER_PXL_FORMAT;
+ break;
+ default:
+ bpp = disp_info->bpc;
+ pxl_repr = CDN_DP_FRAMER_Y_ONLY << CDN_DP_FRAMER_PXL_FORMAT;
+ }
+
+ switch (disp_info->bpc) {
+ case 6:
+ misc0 |= DP_TEST_BIT_DEPTH_6;
+ pxl_repr |= CDN_DP_FRAMER_6_BPC;
+ break;
+ case 8:
+ misc0 |= DP_TEST_BIT_DEPTH_8;
+ pxl_repr |= CDN_DP_FRAMER_8_BPC;
+ break;
+ case 10:
+ misc0 |= DP_TEST_BIT_DEPTH_10;
+ pxl_repr |= CDN_DP_FRAMER_10_BPC;
+ break;
+ case 12:
+ misc0 |= DP_TEST_BIT_DEPTH_12;
+ pxl_repr |= CDN_DP_FRAMER_12_BPC;
+ break;
+ case 16:
+ misc0 |= DP_TEST_BIT_DEPTH_16;
+ pxl_repr |= CDN_DP_FRAMER_16_BPC;
+ break;
+ }
+
+ /* find optimal tu_size */
+ required_bandwidth = pxlclock * bpp / 8;
+ available_bandwidth = dp->link.num_lanes * rate;
+ do {
+ tu_size += 2;
+
+ vs_f = tu_size * required_bandwidth / available_bandwidth;
+ vs = vs_f / 1000;
+ vs_f = vs_f % 1000;
+ /* FIXME downspreading? It's unused is what I've been told. */
+ } while ((vs == 1 || ((vs_f > 850 || vs_f < 100) && vs_f != 0) ||
+ tu_size - vs < 2) && tu_size < 64);
+
+ if (vs > 64)
+ return;
+
+ bnd_hsync2vsync = CDN_IP_BYPASS_V_INTERFACE;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ bnd_hsync2vsync |= CDN_IP_DET_INTERLACE_FORMAT;
+
+ cdn_dp_register_write(dp, BND_HSYNC2VSYNC, bnd_hsync2vsync);
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE &&
+ mode->flags & DRM_MODE_FLAG_PHSYNC)
+ hsync2vsync_pol_ctrl = CDN_H2V_HSYNC_POL_ACTIVE_LOW |
+ CDN_H2V_VSYNC_POL_ACTIVE_LOW;
+ else
+ hsync2vsync_pol_ctrl = 0;
+
+ cdn_dp_register_write(dp, CDN_HSYNC2VSYNC_POL_CTRL,
+ hsync2vsync_pol_ctrl);
+
+ cdn_dp_register_write(dp, CDN_DP_FRAMER_TU, CDN_DP_FRAMER_TU_VS(vs) |
+ CDN_DP_FRAMER_TU_SIZE(tu_size) |
+ CDN_DP_FRAMER_TU_CNT_RST_EN);
+
+ cdn_dp_register_write(dp, CDN_DP_FRAMER_PXL_REPR, pxl_repr);
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ dp_framer_sp |= CDN_DP_FRAMER_INTERLACE;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ dp_framer_sp |= CDN_DP_FRAMER_HSYNC_POL_LOW;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ dp_framer_sp |= CDN_DP_FRAMER_VSYNC_POL_LOW;
+ cdn_dp_register_write(dp, CDN_DP_FRAMER_SP, dp_framer_sp);
+
+ front_porch = mode->crtc_hsync_start - mode->crtc_hdisplay;
+ back_porch = mode->crtc_htotal - mode->crtc_hsync_end;
+ cdn_dp_register_write(dp, CDN_DP_FRONT_BACK_PORCH,
+ CDN_DP_FRONT_PORCH(front_porch) |
+ CDN_DP_BACK_PORCH(back_porch));
+
+ cdn_dp_register_write(dp, CDN_DP_BYTE_COUNT,
+ mode->crtc_hdisplay * bpp / 8);
+
+ msa_h0 = mode->crtc_htotal - mode->crtc_hsync_start;
+ cdn_dp_register_write(dp, CDN_DP_MSA_HORIZONTAL_0,
+ CDN_DP_MSAH0_H_TOTAL(mode->crtc_htotal) |
+ CDN_DP_MSAH0_HSYNC_START(msa_h0));
+
+ hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ msa_horizontal_1 = CDN_DP_MSAH1_HSYNC_WIDTH(hsync) |
+ CDN_DP_MSAH1_HDISP_WIDTH(mode->crtc_hdisplay);
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ msa_horizontal_1 |= CDN_DP_MSAH1_HSYNC_POL_LOW;
+ cdn_dp_register_write(dp, CDN_DP_MSA_HORIZONTAL_1, msa_horizontal_1);
+
+ msa_v0 = mode->crtc_vtotal - mode->crtc_vsync_start;
+ cdn_dp_register_write(dp, CDN_DP_MSA_VERTICAL_0,
+ CDN_DP_MSAV0_V_TOTAL(mode->crtc_vtotal) |
+ CDN_DP_MSAV0_VSYNC_START(msa_v0));
+
+ vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ msa_vertical_1 = CDN_DP_MSAV1_VSYNC_WIDTH(vsync) |
+ CDN_DP_MSAV1_VDISP_WIDTH(mode->crtc_vdisplay);
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ msa_vertical_1 |= CDN_DP_MSAV1_VSYNC_POL_LOW;
+ cdn_dp_register_write(dp, CDN_DP_MSA_VERTICAL_1, msa_vertical_1);
+
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+ mode->crtc_vtotal % 2 == 0)
+ misc1 = DP_TEST_INTERLACED;
+ if (pxlfmt == Y_ONLY)
+ misc1 |= DP_TEST_COLOR_FORMAT_RAW_Y_ONLY;
+ /* FIXME: use VSC SDP for Y420 */
+ /* FIXME: (CDN) no code for Y420 in bare metal test */
+ if (pxlfmt == YCBCR_4_2_0)
+ misc1 = DP_TEST_VSC_SDP;
+
+ cdn_dp_register_write(dp, CDN_DP_MSA_MISC, misc0 | (misc1 << 8));
+
+ /* FIXME: to be changed if MST mode */
+ cdn_dp_register_write(dp, CDN_DP_STREAM_CONFIG, 1);
+
+ cdn_dp_register_write(dp, CDN_DP_HORIZONTAL,
+ CDN_DP_H_HSYNC_WIDTH(hsync) |
+ CDN_DP_H_H_TOTAL(mode->crtc_hdisplay));
+
+ cdn_dp_register_write(dp, CDN_DP_VERTICAL_0,
+ CDN_DP_V0_VHEIGHT(mode->crtc_vdisplay) |
+ CDN_DP_V0_VSTART(msa_v0));
+
+ dp_vertical_1 = CDN_DP_V1_VTOTAL(mode->crtc_vtotal);
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+ mode->crtc_vtotal % 2 == 0)
+ dp_vertical_1 |= CDN_DP_V1_VTOTAL_EVEN;
+
+ cdn_dp_register_write(dp, CDN_DP_VERTICAL_1, dp_vertical_1);
+
+ cdn_dp_register_write_field(dp, CDN_DP_VB_ID, 2, 1,
+ (mode->flags & DRM_MODE_FLAG_INTERLACE) ?
+ CDN_DP_VB_ID_INTERLACED : 0);
+
+ line_thresh1 = ((vs + 1) << 5) * 8 / bpp;
+ line_thresh2 = (pxlclock << 5) / 1000 / rate * (vs + 1) - (1 << 5);
+ line_thresh = line_thresh1 - line_thresh2 / dp->link.num_lanes;
+ line_thresh = (line_thresh >> 5) + 2;
+ cdn_dp_register_write(dp, CDN_DP_LINE_THRESH,
+ line_thresh & GENMASK(5, 0));
+
+ cdn_dp_register_write(dp, CDN_DP_RATE_GOVERNOR_STATUS,
+ CDN_DP_RG_TU_VS_DIFF((tu_size - vs > 3) ?
+ 0 : tu_size - vs));
+
+ cdn_dp_set_video_status(dp, 1);
+
+ /* __simu_enable_mhdp(dp->regs); */
+}
+
+static void cdn_mhdp_disable(struct drm_bridge *bridge)
+{
+ struct cdn_dp_device *dp = bridge_to_dp(bridge);
+
+ /* __simu_disable_mhdp(dp->regs); */
+
+ cdn_dp_set_video_status(dp, 0);
+
+ drm_dp_link_power_down(&dp->aux, &dp->link);
+}
+
+static int cdn_mhdp_attach(struct drm_bridge *bridge)
+{
+ struct cdn_dp_device *dp = bridge_to_dp(bridge);
+ struct drm_connector *conn = &dp->connector;
+ int ret;
+
+ conn->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+
+ ret = drm_connector_init(bridge->dev, conn,
+ &cdn_dp_atomic_connector_funcs,
+ DRM_MODE_CONNECTOR_DisplayPort);
+ if (ret) {
+ dev_err(dp->dev, "failed to init connector\n");
+ return ret;
+ }
+
+ drm_connector_helper_add(conn, &cdn_dp_connector_helper_funcs);
+
+ ret = drm_mode_connector_attach_encoder(conn, bridge->encoder);
+ if (ret) {
+ dev_err(dp->dev, "failed to attach connector to encoder\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct drm_bridge_funcs cdn_mhdp_bridge_funcs = {
+ .enable = cdn_mhdp_enable,
+ .disable = cdn_mhdp_disable,
+ .attach = cdn_mhdp_attach,
+};
+
+static ssize_t cdn_mhdp_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ struct cdn_dp_device *dp = dev_get_drvdata(aux->dev);
+ int ret;
+
+ if (msg->request != DP_AUX_NATIVE_WRITE &&
+ msg->request != DP_AUX_NATIVE_READ)
+ return -ENOTSUPP;
+
+ if (msg->request == DP_AUX_NATIVE_WRITE) {
+ int i;
+
+ for (i = 0; i < msg->size; ++i) {
+ ret = cdn_dp_dpcd_write(dp,
+ msg->address + i,
+ *((u8 *)msg->buffer + i));
+ if (!ret)
+ continue;
+
+ DRM_DEV_ERROR(dp->dev, "Failed to write DPCD\n");
+
+ return i;
+ }
+ } else {
+ ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
+ msg->size);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev, "Failed to read DPCD\n");
+ return 0;
+ }
+ }
+
+ return msg->size;
+}
+
+int cdn_mhdp_probe(struct cdn_dp_device *dp)
+{
+ unsigned long clk_rate;
+ const struct firmware *fw;
+ int ret;
+ u32 reg;
+
+ dp->core_clk = devm_clk_get(dp->dev, "clk");
+ if (IS_ERR(dp->core_clk)) {
+ DRM_DEV_ERROR(dp->dev, "cannot get core_clk_dp\n");
+ return PTR_ERR(dp->core_clk);
+ }
+
+ drm_dp_aux_init(&dp->aux);
+ dp->aux.dev = dp->dev;
+ dp->aux.transfer = cdn_mhdp_transfer;
+
+ clk_rate = clk_get_rate(dp->core_clk);
+ cdn_dp_set_fw_clk(dp, clk_rate);
+
+ ret = request_firmware(&fw, CDN_DP_FIRMWARE, dp->dev);
+ if (ret) {
+ dev_err(dp->dev, "failed to load firmware (%s), ret: %d\n",
+ CDN_DP_FIRMWARE, ret);
+ return ret;
+ }
+
+ memcpy_toio(dp->regs + ADDR_IMEM, fw->data, fw->size);
+
+ release_firmware(fw);
+
+ /* __simu_configure_mhdp(dp->regs); */
+
+ /* un-reset ucpu */
+ writel(0, dp->regs + APB_CTRL);
+
+ /* check the keep alive register to make sure fw working */
+ ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+ reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dp->dev, "failed to loaded the FW reg = %x\n",
+ reg);
+ return -EINVAL;
+ }
+
+ /*
+ * FIXME (CDNS): how are the characteristics/features of the host
+ * defined? Will they be always hardcoded?
+ */
+ /* FIXME: link rate 2.7; num_lanes = 2, */
+ /* FIXME: read capabilities from PHY */
+ /* FIXME: get number of lanes */
+ dp->host.link_rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4);
+ dp->host.lanes_cnt = LANE_4 | SCRAMBLER_EN;
+ dp->host.volt_swing = VOLTAGE_LEVEL_3;
+ dp->host.pre_emphasis = PRE_EMPHASIS_LEVEL_2;
+ dp->host.pattern_supp = PTS1 | PTS2 | PTS3 | PTS4;
+ dp->host.fast_link = 0;
+ dp->host.lane_mapping = LANE_MAPPING_FLIPPED;
+ dp->host.enhanced = true;
+
+ dp->bridge.of_node = dp->dev->of_node;
+ dp->bridge.funcs = &cdn_mhdp_bridge_funcs;
+
+ ret = cdn_dp_set_firmware_active(dp, true);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev, "active ucpu failed: %d\n", ret);
+ return ret;
+ }
+
+ /* __simu_phy_reset(dp->regs); */
+
+ ret = readl_poll_timeout(dp->regs + SW_EVENTS0, reg,
+ reg & DPTX_HPD_EVENT, 500,
+ HPD_EVENT_TIMEOUT);
+ if (ret) {
+ dev_err(dp->dev, "no HPD received %d\n", reg);
+ return -ENODEV;
+ }
+
+ drm_bridge_add(&dp->bridge);
+
+ /* __simu_configure2_mhdp(dp->regs); */
+
+ return 0;
+}
+
static int cdn_dp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *match;
+ struct resource *res;
struct cdn_dp_data *dp_data;
struct cdn_dp_port *port;
struct cdn_dp_device *dp;
@@ -1161,7 +2036,24 @@ static int cdn_dp_probe(struct platform_device *pdev)
return -ENOMEM;
dp->dev = dev;

+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dp->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dp->regs)) {
+ DRM_DEV_ERROR(dev, "ioremap reg failed\n");
+ return PTR_ERR(dp->regs);
+ }
+
match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
+ if (!match)
+ return -EINVAL;
+
+ dp->mhdp_ip = !strcmp("cdns,mhdp", match->compatible);
+
+ if (dp->mhdp_ip) {
+ cdn_mhdp_probe(dp);
+ goto skip_phy_init;
+ }
+
dp_data = (struct cdn_dp_data *)match->data;

for (i = 0; i < dp_data->max_phy; i++) {
@@ -1194,6 +2086,8 @@ static int cdn_dp_probe(struct platform_device *pdev)
mutex_init(&dp->lock);
dev_set_drvdata(dev, dp);

+skip_phy_init:
+
cdn_dp_audio_codec_init(dp, dev);

return component_add(dev, &cdn_dp_component_ops);
@@ -1202,10 +2096,23 @@ static int cdn_dp_probe(struct platform_device *pdev)
static int cdn_dp_remove(struct platform_device *pdev)
{
struct cdn_dp_device *dp = platform_get_drvdata(pdev);
+ int ret;

platform_device_unregister(dp->audio_pdev);
- cdn_dp_suspend(dp->dev);
- component_del(&pdev->dev, &cdn_dp_component_ops);
+
+ if (dp->mhdp_ip) {
+ drm_bridge_remove(&dp->bridge);
+
+ ret = cdn_dp_set_firmware_active(dp, false);
+ if (ret) {
+ DRM_DEV_ERROR(dp->dev, "disabling fw failed: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ cdn_dp_suspend(dp->dev);
+ component_del(&pdev->dev, &cdn_dp_component_ops);
+ }

return 0;
}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index f57e296..9c43f6d 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -69,6 +69,25 @@ struct cdn_dp_port {
u8 id;
};

+struct cdn_mhdp_host {
+ unsigned int link_rate;
+ u8 lanes_cnt;
+ u8 volt_swing;
+ u8 pre_emphasis;
+ u8 pattern_supp;
+ u8 fast_link;
+ u8 lane_mapping;
+ u8 enhanced;
+};
+
+struct cdn_mhdp_sink {
+ unsigned int link_rate;
+ u8 lanes_cnt;
+ u8 pattern_supp;
+ u8 fast_link;
+ u8 enhanced;
+};
+
struct cdn_dp_device {
struct device *dev;
struct drm_device *drm_dev;
@@ -108,5 +127,11 @@ struct cdn_dp_device {

u8 dpcd[DP_RECEIVER_CAP_SIZE];
bool sink_has_audio;
+ bool mhdp_ip;
+
+ struct cdn_mhdp_host host;
+ struct cdn_mhdp_sink sink;
+ struct drm_bridge bridge;
+ struct drm_dp_aux aux;
};
#endif /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index b061cfc..52a284b 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -1060,7 +1060,7 @@ int cdn_dp_register_write(struct cdn_dp_device *dp, u32 addr, u32 value)
}

int cdn_dp_register_write_field(struct cdn_dp_device *dp, u32 addr,
- u8 index, u8 nbits, u32 value)
+ u8 index, u8 nbits, u32 value)
{
u8 msg[10];
int ret;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index b5472ad..135f36e 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -388,6 +388,10 @@
#define LANE_MAPPING_NORMAL 0x1b
#define LANE_MAPPING_FLIPPED 0xe4
#define ENHANCED 1
+#define LANE_1 BIT(0)
+#define LANE_2 BIT(1)
+#define LANE_4 BIT(2)
+#define SSC BIT(3)
#define SCRAMBLER_EN BIT(4)

#define FULL_LT_STARTED BIT(0)
--
1.7.1


2018-07-03 10:06:01

by Damian Kos

[permalink] [raw]
Subject: [PATCH 09/12] drm/dp: add pixel encoding and colorimetry format indicator field in MISC1

From: Quentin Schulz <[email protected]>

If DP_TEST_COLOR_FORMAT_RAW_Y_ONLY is set, if MISC0 color format is
DP_COLOR_FORMAT_RGB and all other bits are 0, then color format is
actually Y-Only.

If DP_TEST_COLOR_FORMAT_RAW_Y_ONLY is set, if MISC0 color format is
DP_COLOR_FORMAT_YCbCr422, DP_TEST_DYNAMIC_RANGE_CEA is not set and
chosen ITU is 601, then color format is actually RAW.

If DP_TEST_VSC_SDP is set, then DP_TEST_COLOR_FORMAT_RAW_Y_ONLY,
DP_TEST_COLOR_FORMAT, DP_TEST_DYNAMIC_RANGE_CEA,
DP_TEST_YCBCR_COEFFICIENTS and DP_TEST_BIT_DEPTH are ignored and we
should use VSC SDP for setting the colorimetry. This is used for Y420.

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
include/drm/drm_dp_helper.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index accefbb..22f6c3e 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -609,6 +609,8 @@
#define DP_TEST_MISC1 0x233
# define DP_TEST_REFRESH_DENOMINATOR (1 << 0)
# define DP_TEST_INTERLACED (1 << 1)
+# define DP_TEST_VSC_SDP (1 << 6) /* 1.3+ */
+# define DP_TEST_COLOR_FORMAT_RAW_Y_ONLY (1 << 7)

#define DP_TEST_REFRESH_RATE_NUMERATOR 0x234

--
1.7.1


2018-07-03 10:06:04

by Damian Kos

[permalink] [raw]
Subject: [PATCH 10/12] dt-bindings: drm/bridge: Document Cadence MHDP bridge bindings

Document the bindings used for the Cadence MHDP DPI/DP bridge.

Signed-off-by: Damian Kos <[email protected]>
---
.../bindings/display/bridge/cdns,mhdp.txt | 39 ++++++++++++++++++++
1 files changed, 39 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt
new file mode 100644
index 0000000..d872e26
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt
@@ -0,0 +1,39 @@
+Cadence MHDP bridge
+==========================
+
+The Cadence MHDP bridge is a DPI to DP bridge.
+
+Required properties:
+ - compatible: should be "cdns,mhdp",
+ - reg: physical base address and length of the controller's registers,
+ - clocks: DP bridge clock, it's used by the IP to know how to translate
+ a number of clock cycles into a time (which is used to comply
+ with DP standard timings and delays),
+ - clock-names: must be "clk",
+
+Required subnodes:
+ - ports: Ports as described in Documentation/devictree/bindings/graph.txt
+ Currently contains a single input port at address 0 representing the DPI
+ input.
+ Port 0 should be connected to a DPI encoder output.
+
+Example:
+
+ mhdp: mhdp@0xf0fb000000 {
+ compatible = "cdns,mhdp";
+ reg = <0xf0 0xfb000000 0x0 0x1000000>;
+ clocks = <&mhdp_clock>;
+ clock-names = "clk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ mhdp0_dpi_input: endpoint {
+ remote-endpoint = <&xxx_dpi_output>;
+ };
+ };
+ };
+ };
--
1.7.1


2018-07-03 10:06:39

by Damian Kos

[permalink] [raw]
Subject: [PATCH 08/12] drm/dp: add max number of lanes supported

From: Quentin Schulz <[email protected]>

Currently, a maximum of 4 lanes is supported in DP.

It can be useful to add this maximum so that we can e.g. create arrays
in drivers that can store data for several lanes (e.g.
DP_TRAINING_LANEx_SET).

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
include/drm/drm_dp_helper.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index a63a548..accefbb 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -69,6 +69,7 @@

#define DP_MAX_LANE_COUNT 0x002
# define DP_MAX_LANE_COUNT_MASK 0x1f
+# define DP_MAX_NUM_LANES 4
# define DP_TPS3_SUPPORTED (1 << 6) /* 1.2 */
# define DP_ENHANCED_FRAME_CAP (1 << 7)

--
1.7.1


2018-07-03 10:07:05

by Damian Kos

[permalink] [raw]
Subject: [PATCH 06/12] drm/dp: fix drm_dp_link_power_* for DP 1.2+

From: Quentin Schulz <[email protected]>

In DP 1.1, DP_SET_POWER had only two bits for the power state (D0 or D3
and two other reserved values), thus DP_SET_POWER_MASK being 0x3.

However, since DP 1.2, DP_SET_POWER has 3 bits (same values for D0 and
D3, a mixed D3 which leaves only AUX powered and five other reserved
values). Thus, we need to expand DP_SET_POWER_MASK to 3 bits (thus 0x7).

It is safe to expand DP_SET_POWER_MASK to 3 bits since bit 2 in DP 1.1
is reserved and read 0.

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
include/drm/drm_dp_helper.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index bd593df..a63a548 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -671,7 +671,7 @@
#define DP_SET_POWER 0x600
# define DP_SET_POWER_D0 0x1
# define DP_SET_POWER_D3 0x2
-# define DP_SET_POWER_MASK 0x3
+# define DP_SET_POWER_MASK 0x7
# define DP_SET_POWER_D3_AUX_ON 0x5

#define DP_EDP_DPCD_REV 0x700 /* eDP 1.2 */
--
1.7.1


2018-07-03 10:07:33

by Damian Kos

[permalink] [raw]
Subject: [PATCH 05/12] drm/dp: fix link probing for devices supporting DP 1.4+

From: Quentin Schulz <[email protected]>

DP 1.4 introduced a DP_EXTENDED_RCVR_CAPA_FIELD_PRESENT bit in
DP_TRAINING_AUX_RD_INTERVAL register. If set, DPCD registers from
DP_DPCD_REV to DP_ADAPTER_CAP should be retrieved starting from
DP_DPCD_REV_EXTENDED. All registers are copied except DP_DPCD_REV,
DP_MAX_LINK_RATE and DP_DOWNSTREAMPORT_PRESENT which represent the
"true capabilities" of DPRX device.

Original DP_DPCD_REV, DP_MAX_LINK_RATE and DP_DOWNSTREAMPORT_PRESENT
might falsely return lower capabilities to "avoid interoperability
issues with some of the existing DP Source devices that malfunction
when they discover the higher capabilities within those three
registers.".

Before DP 1.4, DP_EXTENDED_RCVR_CAPA_FIELD_PRESENT bit was reserved
and read 0 so it's safe to check against it even if DP revision is
<1.4

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/drm_dp_helper.c | 30 +++++++++++++++++++++++++++++-
1 files changed, 29 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 7f5d568..b6a27ab 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -370,10 +370,38 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
{
u8 values[3];
int err;
+ unsigned int addr;

memset(link, 0, sizeof(*link));

- err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
+ /*
+ * DP 1.4 introduced a DP_EXTENDED_RCVR_CAPA_FIELD_PRESENT bit in
+ * DP_TRAINING_AUX_RD_INTERVAL register. If set, DPCD registers from
+ * DP_DPCD_REV to DP_ADAPTER_CAP should be retrieved starting from
+ * DP_DPCD_REV_EXTENDED. All registers are copied except DP_DPCD_REV,
+ * DP_MAX_LINK_RATE and DP_DOWNSTREAMPORT_PRESENT which represent the
+ * "true capabilities" of DPRX device.
+ *
+ * Original DP_DPCD_REV, DP_MAX_LINK_RATE and DP_DOWNSTREAMPORT_PRESENT
+ * might falsely return lower capabilities to "avoid interoperability
+ * issues with some of the existing DP Source devices that malfunction
+ * when they discover the higher capabilities within those three
+ * registers.".
+ *
+ * Before DP 1.4, DP_EXTENDED_RCVR_CAPA_FIELD_PRESENT bit was reserved
+ * and read 0 so it's safe to check against it even if DP revision is
+ * <1.4
+ */
+ err = drm_dp_dpcd_readb(aux, DP_TRAINING_AUX_RD_INTERVAL, values);
+ if (err < 0)
+ return err;
+
+ if (values[0] & DP_EXTENDED_RCVR_CAPA_FIELD_PRESENT)
+ addr = DP_DP13_DPCD_REV;
+ else
+ addr = DP_DPCD_REV;
+
+ err = drm_dp_dpcd_read(aux, addr, values, sizeof(values));
if (err < 0)
return err;

--
1.7.1


2018-07-03 10:07:36

by Damian Kos

[permalink] [raw]
Subject: [PATCH 04/12] drm/dp: fix training interval formula for DP 1.3+

From: Quentin Schulz <[email protected]>

In DP standard v1.2, DP_TRAINING_AUX_RD_INTERVAL DPCD "register" only
has 0x0 to 0x4 values that are valid, the rest is reserved.

In DP standard v1.3+, DP_TRAINING_AUX_RD_INTERVAL DPCD "register" has
the same 0x0 to 0x4 valid values but there is an additional bit (bit 7)
that specifies if there is an Extended Receiver Capability field or not.

Thus, the formula for getting the training interval is now broken on DP
1.3+.

Let's add a mask for the training interval and a define for the Extended
Receiver Capability field presence.

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/drm_dp_helper.c | 14 ++++++++++----
include/drm/drm_dp_helper.h | 2 ++
2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index ca2f469..7f5d568 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -149,18 +149,24 @@ void drm_dp_set_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
EXPORT_SYMBOL(drm_dp_set_adjust_request_pre_emphasis);

void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
- if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+ unsigned int training_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
+ DP_TRAINING_AUX_RD_INTERVAL_MASK;
+
+ if (training_interval == 0)
udelay(100);
else
- mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+ mdelay(training_interval * 4);
}
EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);

void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
- if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+ unsigned int training_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
+ DP_TRAINING_AUX_RD_INTERVAL_MASK;
+
+ if (training_interval == 0)
udelay(400);
else
- mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+ mdelay(training_interval * 4);
}
EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);

diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 6e64b2a..bd593df 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -119,6 +119,8 @@
# define DP_DPCD_DISPLAY_CONTROL_CAPABLE (1 << 3) /* edp v1.2 or higher */

#define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */
+# define DP_TRAINING_AUX_RD_INTERVAL_MASK GENMASK(6, 0)
+# define DP_EXTENDED_RCVR_CAPA_FIELD_PRESENT BIT(7) /* 1.3 */

#define DP_ADAPTER_CAP 0x00f /* 1.2 */
# define DP_FORCE_LOAD_SENSE_CAP (1 << 0)
--
1.7.1


2018-07-03 10:08:32

by Damian Kos

[permalink] [raw]
Subject: [PATCH 02/12] drm/dp: make dp_link_status and dp_get_lane_status usable from outside of the core

From: Quentin Schulz <[email protected]>

dp_link_status and dp_get_lane_status are pretty generic and can be used
for other means, so let's make it "public".

This adds drm_dp_link_status and drm_dp_get_lane_status to the header
file and add the appropriate EXPORT_SYMBOL for it so that it can be used
by other drivers, be they compiled built-in or as modules.

Signed-off-by: Quentin Schulz <[email protected]>
Signed-off-by: Damian Kos <[email protected]>
---
drivers/gpu/drm/drm_dp_helper.c | 20 +++++++++++---------
include/drm/drm_dp_helper.h | 3 +++
2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index ffe14ec..3bc2e98 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -43,19 +43,21 @@
*/

/* Helpers for DP link training */
-static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+u8 drm_dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
{
return link_status[r - DP_LANE0_1_STATUS];
}
+EXPORT_SYMBOL(drm_dp_link_status);

-static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
+u8 drm_dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
int lane)
{
int i = DP_LANE0_1_STATUS + (lane >> 1);
int s = (lane & 1) * 4;
- u8 l = dp_link_status(link_status, i);
+ u8 l = drm_dp_link_status(link_status, i);
return (l >> s) & 0xf;
}
+EXPORT_SYMBOL(drm_dp_get_lane_status);

bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
int lane_count)
@@ -64,12 +66,12 @@ bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
u8 lane_status;
int lane;

- lane_align = dp_link_status(link_status,
- DP_LANE_ALIGN_STATUS_UPDATED);
+ lane_align = drm_dp_link_status(link_status,
+ DP_LANE_ALIGN_STATUS_UPDATED);
if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
return false;
for (lane = 0; lane < lane_count; lane++) {
- lane_status = dp_get_lane_status(link_status, lane);
+ lane_status = drm_dp_get_lane_status(link_status, lane);
if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
return false;
}
@@ -84,7 +86,7 @@ bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
u8 lane_status;

for (lane = 0; lane < lane_count; lane++) {
- lane_status = dp_get_lane_status(link_status, lane);
+ lane_status = drm_dp_get_lane_status(link_status, lane);
if ((lane_status & DP_LANE_CR_DONE) == 0)
return false;
}
@@ -99,7 +101,7 @@ u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
int s = ((lane & 1) ?
DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
- u8 l = dp_link_status(link_status, i);
+ u8 l = drm_dp_link_status(link_status, i);

return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
}
@@ -112,7 +114,7 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SI
int s = ((lane & 1) ?
DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
- u8 l = dp_link_status(link_status, i);
+ u8 l = drm_dp_link_status(link_status, i);

return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
}
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 62903ba..a488af0 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -935,6 +935,9 @@
#define DP_MST_LOGICAL_PORT_0 8

#define DP_LINK_STATUS_SIZE 6
+
+u8 drm_dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r);
+u8 drm_dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE], int lane);
bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
int lane_count);
bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
--
1.7.1


2018-07-03 11:04:24

by Heiko Stuebner

[permalink] [raw]
Subject: Re: [PATCH 12/12] drm/rockchip: add support for CDNS MHDP IP controller.

Hi Damien,

it's very cool to see collaboration from vendors on this.


Am Dienstag, 3. Juli 2018, 12:02:23 CEST schrieb Damian Kos:
>

It would be really nice to explain a bit about the added controller support
in the commit message, so that people reviewing the patch can get a
feeling for it.

> Signed-off-by: Damian Kos <[email protected]>
> ---
> drivers/gpu/drm/rockchip/cdn-dp-core.c | 953
> +++++++++++++++++++++++++++++++- drivers/gpu/drm/rockchip/cdn-dp-core.h |
> 25 +
> drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 +-
> drivers/gpu/drm/rockchip/cdn-dp-reg.h | 4 +

From the changes below, it looks that this seems to add support for a
bridge chip based on that IP block. So it seems like the bridge+glue driver
model would be a perfect fit for this, instead of stapling this onto the
Rockchip-specific driver.

So essentially, you could take the Rockchip cdn-dp driver, move the common
parts to drivers/gpu/drm/bridge and then create separate glue drivers for
both Rockchip and your external bridge IP block.

This would prevent code duplication and also allow your bridge driver to
be compiled without the Rockchip drm being present :-) .
And also pave the way for future socs using your DP ip block.


Nowadays we have quite a number of examples you could take as
inspiration for this:
- bridge/analogix/* (shared between Exynos and Rockchip right now)
- bridge/synopsys/dw-hdmi* (shared between a quite big number of users)
- bridge/synopsys/dw-mipi-dsi.c (shared between Rockchip [pending] and stm)


Thanks
Heiko




2018-07-03 14:10:13

by Damian Kos

[permalink] [raw]
Subject: RE: [PATCH 12/12] drm/rockchip: add support for CDNS MHDP IP controller.

Hi Heiko,

Thank you for your feedback! Initially, MHDP driver was developed as a DRM bridge driver and was planned to be placed in drivers/gpu/drm/bridge/cadence/mhdp.c. However, there was already a driver for Cadence's DP controller developed by RockChip, but that driver uses different DRM framework. Both controllers (including firmware) are quite different internally (MST/FEC/DSC support, link training done by driver, additional commands, etc.) but they have very similar register map, except for Framer/Streamer, so they appear similar.

We would be more than happy to provide fully separate driver (that was basically pasted in RockChip's driver) for DP DRM bridge. Some parts can definitely shared between these two drivers like code for mailbox and commands sent/received by it, audio init.

Moving cdn-dp-* to drivers/gpu/drm/bridge/ was also a plan, but it seems that cdn-dp-core.c use some stuff from drivers/gpu/drm/rockchip/*. cdn-dp-core driver in this case seems like a part of a bigger picture while the driver that we want to upstream is standalone.

We'll move/add everything that can be shared by both drivers to drivers/gpu/drm/ and add new DPI/DP bridge driver as you advised and provide a new patch.

PS. Should we post patches 01-08 done in DRM helper by Quentin as a separate patch, so that reviewers will not have to go through them every time when we send updated version?

Regards,
Damian

-----Original Message-----
From: Heiko St?bner <[email protected]>
Sent: Tuesday, July 3, 2018 13:03
To: Damian Kos <[email protected]>
Cc: David Airlie <[email protected]>; Rob Herring <[email protected]>; Mark Rutland <[email protected]>; Gustavo Padovan <[email protected]>; Maarten Lankhorst <[email protected]>; Sean Paul <[email protected]>; Sandy Huang <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Lukasz Tyrala <[email protected]>; Przemyslaw Gaj <[email protected]>; Scott Telford <[email protected]>
Subject: Re: [PATCH 12/12] drm/rockchip: add support for CDNS MHDP IP controller.

EXTERNAL MAIL


Hi Damien,

it's very cool to see collaboration from vendors on this.


Am Dienstag, 3. Juli 2018, 12:02:23 CEST schrieb Damian Kos:
>

It would be really nice to explain a bit about the added controller support in the commit message, so that people reviewing the patch can get a feeling for it.

> Signed-off-by: Damian Kos <[email protected]>
> ---
> drivers/gpu/drm/rockchip/cdn-dp-core.c | 953
> +++++++++++++++++++++++++++++++-
> +++++++++++++++++++++++++++++++drivers/gpu/drm/rockchip/cdn-dp-core.h
> +++++++++++++++++++++++++++++++|
> 25 +
> drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 +-
> drivers/gpu/drm/rockchip/cdn-dp-reg.h | 4 +

From the changes below, it looks that this seems to add support for a bridge chip based on that IP block. So it seems like the bridge+glue driver model would be a perfect fit for this, instead of stapling this onto the Rockchip-specific driver.

So essentially, you could take the Rockchip cdn-dp driver, move the common parts to drivers/gpu/drm/bridge and then create separate glue drivers for both Rockchip and your external bridge IP block.

This would prevent code duplication and also allow your bridge driver to be compiled without the Rockchip drm being present :-) .
And also pave the way for future socs using your DP ip block.


Nowadays we have quite a number of examples you could take as inspiration for this:
- bridge/analogix/* (shared between Exynos and Rockchip right now)
- bridge/synopsys/dw-hdmi* (shared between a quite big number of users)
- bridge/synopsys/dw-mipi-dsi.c (shared between Rockchip [pending] and stm)


Thanks
Heiko




2018-07-04 08:18:45

by Daniel Vetter

[permalink] [raw]
Subject: Re: [PATCH 03/12] drm/dp: add helpers for drm_dp_set_adjust_request_pre_emphasis and drm_dp_set_adjust_request_voltage

On Tue, Jul 03, 2018 at 11:02:14AM +0100, Damian Kos wrote:
> From: Quentin Schulz <[email protected]>
>
> We already have functions to get the adjust request voltage and
> pre-emphasis for a lane so it makes also sense to be able to set them so
> that we can then easily update them via a DPCD write.
>
> Add helpers for drm_dp_set_adjust_request_pre_emphasis and
> drm_dp_set_adjust_request_voltage that respectively set the
> pre-emphasis and voltage of a lane in the link status array.
>
> Signed-off-by: Quentin Schulz <[email protected]>
> Signed-off-by: Damian Kos <[email protected]>

Hm usually this is source dependent - some sources only have one
adj/pre-emph value for all lanes, some only 2 (for groups of 2), some for
all four. That's kinda why we don't have helpers for this stuff.

An excellent way to show that your new helpers are useful would be to go
through existing drivers and convert them over, where it makes sense. Same
kinda holds for patch 1.

Thanks, Daniel
> ---
> drivers/gpu/drm/drm_dp_helper.c | 28 ++++++++++++++++++++++++++++
> include/drm/drm_dp_helper.h | 4 ++++
> 2 files changed, 32 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 3bc2e98..ca2f469 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -120,6 +120,34 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SI
> }
> EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
>
> +void drm_dp_set_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
> + int lane, u8 volt)
> +{
> + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> + int s = ((lane & 1) ?
> + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
> + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
> + int idx = i - DP_LANE0_1_STATUS;
> +
> + link_status[idx] &= ~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << s);
> + link_status[idx] |= volt << s;
> +}
> +EXPORT_SYMBOL(drm_dp_set_adjust_request_voltage);
> +
> +void drm_dp_set_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
> + int lane, u8 pre_emphasis)
> +{
> + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> + int s = ((lane & 1) ?
> + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
> + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
> + int idx = i - DP_LANE0_1_STATUS;
> +
> + link_status[idx] &= ~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << s);
> + link_status[idx] |= pre_emphasis << s;
> +}
> +EXPORT_SYMBOL(drm_dp_set_adjust_request_pre_emphasis);
> +
> void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
> if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
> udelay(100);
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index a488af0..6e64b2a 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -946,6 +946,10 @@ u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
> int lane);
> u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
> int lane);
> +void drm_dp_set_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
> + int lane, u8 volt);
> +void drm_dp_set_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
> + int lane, u8 pre_emphasis);
>
> #define DP_BRANCH_OUI_HEADER_SIZE 0xc
> #define DP_RECEIVER_CAP_SIZE 0xf
> --
> 1.7.1
>
> _______________________________________________
> dri-devel mailing list
> [email protected]
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

2018-07-16 21:16:47

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 10/12] dt-bindings: drm/bridge: Document Cadence MHDP bridge bindings

On Tue, Jul 03, 2018 at 11:02:21AM +0100, Damian Kos wrote:
> Document the bindings used for the Cadence MHDP DPI/DP bridge.
>
> Signed-off-by: Damian Kos <[email protected]>
> ---
> .../bindings/display/bridge/cdns,mhdp.txt | 39 ++++++++++++++++++++
> 1 files changed, 39 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt
> new file mode 100644
> index 0000000..d872e26
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.txt
> @@ -0,0 +1,39 @@
> +Cadence MHDP bridge
> +==========================
> +
> +The Cadence MHDP bridge is a DPI to DP bridge.
> +
> +Required properties:
> + - compatible: should be "cdns,mhdp",

Not very specific. Only 1 version of this block?

> + - reg: physical base address and length of the controller's registers,
> + - clocks: DP bridge clock, it's used by the IP to know how to translate
> + a number of clock cycles into a time (which is used to comply
> + with DP standard timings and delays),
> + - clock-names: must be "clk",

Not a useful name. With only 1 clock, you can just drop this.

> +
> +Required subnodes:
> + - ports: Ports as described in Documentation/devictree/bindings/graph.txt
> + Currently contains a single input port at address 0 representing the DPI
> + input.

Currently? You should have a port connecting to a connector node.

> + Port 0 should be connected to a DPI encoder output.
> +
> +Example:
> +
> + mhdp: mhdp@0xf0fb000000 {

dp-bridge@f0fb000000

> + compatible = "cdns,mhdp";
> + reg = <0xf0 0xfb000000 0x0 0x1000000>;
> + clocks = <&mhdp_clock>;
> + clock-names = "clk";
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> + mhdp0_dpi_input: endpoint {
> + remote-endpoint = <&xxx_dpi_output>;
> + };
> + };
> + };
> + };
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html