From 0aefe26fd954beb473acdc95c657cc79ed57b084 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 15 May 2018 11:22:37 +0800 Subject: [PATCH 1/3] FROMLIST: drm/rockchip: add transfer function for cdn-dp We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Change-Id: If89911e6205546df1a5ae8997ea214d5d2a60af6 Signed-off-by: Chris Zhong Signed-off-by: Lin Huang Reviewed-by: Sean Paul Reviewed-by: Enric Balletbo Signed-off-by: Wyon Bi (am from https://patchwork.kernel.org/patch/10420461/) Signed-off-by: Zhang Yubing --- 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 3333003d1556..200dc100b32a 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -151,8 +151,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); @@ -351,9 +351,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; } @@ -551,8 +551,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->max_rate || !dp->max_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; } @@ -962,6 +962,40 @@ out: drm_kms_helper_hotplug_event(dp->drm_dev); } +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); @@ -979,6 +1013,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_DELAYED_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 519900c673cb..8ec702a13c89 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -68,6 +68,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct delayed_work event_work; struct edid *edid; + struct drm_dp_aux aux; struct rockchip_drm_sub_dev sub_dev; struct mutex lock; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index 33fb4d05c506..f2aa71759f0f 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -213,7 +213,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; @@ -239,24 +244,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; @@ -269,8 +291,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) @@ -278,6 +304,33 @@ err_dpcd_write: 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 441248b7a79e..3ce25590c65d 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -320,6 +320,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 @@ -461,8 +468,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); From 99b9c4c7715feba0bb5feef924f83c12c52c20c6 Mon Sep 17 00:00:00 2001 From: Zhang Yubing Date: Mon, 18 Oct 2021 17:30:04 +0800 Subject: [PATCH 2/3] drm/rockchip: cdn-dp: Avoid drm_dp_link helpers in dp training The drm_dp_link is removed. And link.num_lanes is instead by max_lanes. Link.rate is instead by max_rate. Signed-off-by: Zhang Yubing Change-Id: I95cbaa541bdf28133ab86f46ce3ac9f0903d364d --- .../gpu/drm/rockchip/cdn-dp-link-training.c | 48 +++++++++---------- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 4 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c index 08962e96b17c..f54db3829381 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -14,13 +14,13 @@ static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp) { struct cdn_dp_port *port = dp->port[dp->active_port]; - int rate = drm_dp_bw_code_to_link_rate(dp->link.rate); + int rate = drm_dp_bw_code_to_link_rate(dp->max_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_dp_set_phy_config(port->phy, rate, dp->link.num_lanes, + tcphy_dp_set_phy_config(port->phy, rate, dp->max_lanes, swing, pre_emphasis); } @@ -30,7 +30,7 @@ static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat) int ret; uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK; - global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE | + global_config = NUM_LANES(dp->max_lanes - 1) | SST_MODE | GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL; phy_config = DP_TX_PHY_ENCODER_BYPASS(0) | @@ -63,7 +63,7 @@ static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat) return ret; } - ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1); + ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->max_lanes) - 1); if (ret) { DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret); return ret; @@ -106,7 +106,7 @@ static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp, uint8_t v = 0, p = 0; uint8_t preemph_max; - for (i = 0; i < dp->link.num_lanes; i++) { + for (i = 0; i < dp->max_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)); @@ -119,7 +119,7 @@ static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp, if (p >= preemph_max) p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; - for (i = 0; i < dp->link.num_lanes; i++) + for (i = 0; i < dp->max_lanes; i++) dp->train_set[i] = v | p; } @@ -149,7 +149,7 @@ static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp) { int lane; - for (lane = 0; lane < dp->link.num_lanes; lane++) + for (lane = 0; lane < dp->max_lanes; lane++) if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0) return false; @@ -163,8 +163,8 @@ static int cdn_dp_update_link_train(struct cdn_dp_device *dp) 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) + dp->train_set, dp->max_lanes); + if (ret != dp->max_lanes) return -EINVAL; return 0; @@ -183,8 +183,8 @@ static int cdn_dp_set_link_train(struct cdn_dp_device *dp, 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; + memcpy(buf + 1, dp->train_set, dp->max_lanes); + len = dp->max_lanes + 1; } ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET, @@ -237,7 +237,7 @@ static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp) return -EINVAL; } - if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) { + if (drm_dp_clock_recovery_ok(link_status, dp->max_lanes)) { DRM_DEBUG_KMS("clock recovery OK\n"); return 0; } @@ -301,12 +301,12 @@ static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp) /* Make sure clock is still ok */ if (!drm_dp_clock_recovery_ok(link_status, - dp->link.num_lanes)) { + dp->max_lanes)) { DRM_DEBUG_KMS("Clock recovery check failed\n"); break; } - if (drm_dp_channel_eq_ok(link_status, dp->link.num_lanes)) { + if (drm_dp_channel_eq_ok(link_status, dp->max_lanes)) { DRM_DEBUG_KMS("Channel EQ done\n"); return 0; } @@ -338,17 +338,17 @@ static int cdn_dp_stop_link_train(struct cdn_dp_device *dp) static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp) { - switch (dp->link.rate) { + switch (dp->max_rate) { case DP_LINK_BW_1_62: return -EINVAL; case DP_LINK_BW_2_7: - dp->link.rate = DP_LINK_BW_1_62; + dp->max_rate = DP_LINK_BW_1_62; break; case DP_LINK_BW_5_4: - dp->link.rate = DP_LINK_BW_2_7; + dp->max_rate = DP_LINK_BW_2_7; break; default: - dp->link.rate = DP_LINK_BW_5_4; + dp->max_rate = DP_LINK_BW_5_4; break; } @@ -372,12 +372,12 @@ int cdn_dp_software_train_link(struct cdn_dp_device *dp) source_max = dp->lanes; sink_max = drm_dp_max_lane_count(dp->dpcd); - dp->link.num_lanes = min(source_max, sink_max); + dp->max_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); + dp->max_rate = drm_dp_link_rate_to_bw_code(rate); ssc_on = !!(dp->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5); link_config[0] = ssc_on ? DP_SPREAD_AMP_0_5 : 0; @@ -388,22 +388,22 @@ int cdn_dp_software_train_link(struct cdn_dp_device *dp) while (true) { ret = tcphy_dp_set_link_rate(port->phy, - drm_dp_bw_code_to_link_rate(dp->link.rate), + drm_dp_bw_code_to_link_rate(dp->max_rate), ssc_on); if (ret) { DRM_ERROR("failed to set link rate: %d\n", ret); return ret; } - ret = tcphy_dp_set_lane_count(port->phy, dp->link.num_lanes); + ret = tcphy_dp_set_lane_count(port->phy, dp->max_lanes); if (ret) { DRM_ERROR("failed to set lane count: %d\n", ret); return ret; } /* Write the link configuration data */ - link_config[0] = dp->link.rate; - link_config[1] = dp->link.num_lanes; + link_config[0] = dp->max_rate; + link_config[1] = dp->max_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); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index f2aa71759f0f..f9154996d797 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -588,7 +588,7 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp) if (ret) goto err_get_training_status; - dp->max_rate = drm_dp_bw_code_to_link_rate(status[0]); + dp->max_rate = status[0]; dp->max_lanes = status[1]; err_get_training_status: @@ -692,7 +692,7 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? (video->color_depth * 2) : (video->color_depth * 3); - link_rate = dp->max_rate / 1000; + link_rate = drm_dp_bw_code_to_link_rate(dp->max_rate) / 1000; ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); if (ret) From 6397c9ae572a61003dde39d2563c487bf12a0dc9 Mon Sep 17 00:00:00 2001 From: Wyon Bi Date: Fri, 12 Jul 2019 09:09:58 +0800 Subject: [PATCH 3/3] drm/rockchip: cdn-dp: 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 get phy config values from dts and use software link training instead of relying on firmware, if software training fail, keep firmware training as a fallback if sw training fails. Change-Id: I075bff6aa153a5e18b6a5ddec2645131f1411913 Signed-off-by: Chris Zhong Signed-off-by: Lin Huang Signed-off-by: Wyon Bi Signed-off-by: Zhang Yubing --- 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-reg.c | 27 ++++++++++++++++- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 40 +++++++++++++++++++++++++- 5 files changed, 84 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index e44327013423..7a42624fb9d3 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -16,7 +16,8 @@ rockchipdrm-$(CONFIG_ROCKCHIP_DRM_SELF_TEST) += rockchip_drm_display_pattern.o \ rockchip_drm_self_test.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_DRM_TVE) += rockchip_drm_tve.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o \ diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 200dc100b32a..bae50c5bd12b 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -598,11 +598,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) { + 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); @@ -611,11 +613,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) { + 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 8ec702a13c89..60a8c095b30f 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -75,6 +75,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 */ @@ -98,6 +99,7 @@ struct cdn_dp_device { unsigned int max_rate; 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-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index f9154996d797..2a544869e8de 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -181,7 +181,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]; @@ -601,6 +601,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); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h index 3ce25590c65d..26c097b8461b 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -8,6 +8,8 @@ #define _CDN_DP_REG_H #include +#include +#include #define ADDR_IMEM 0x10000 #define ADDR_DMEM 0x20000 @@ -129,7 +131,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 @@ -423,6 +425,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, @@ -468,6 +504,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, @@ -481,4 +518,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 */