diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index f76f5fa8934e..20fd44f073d2 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -14,7 +14,8 @@ rockchipdrm-$(CONFIG_ROCKCHIP_DRM_SELF_TEST) += rockchip_drm_display_pattern.o \ rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o rockchip_post_csc.o rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.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 3f4c5344a420..878ad7076407 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -613,11 +613,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); @@ -626,10 +628,13 @@ 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; + } } cdn_dp_audio_handle_plugged_change(dp, true); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index ea58391ba310..b4c9e4b60013 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-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c index 08962e96b17c..d5b783c127a1 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, @@ -230,14 +230,14 @@ static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp) voltage_tries = 1; max_vswing_tries = 0; for (;;) { - drm_dp_link_train_clock_recovery_delay(dp->dpcd); + drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd); if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) != DP_LINK_STATUS_SIZE) { DRM_ERROR("failed to get link status\n"); return -EINVAL; } - if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) { + if (drm_dp_clock_recovery_ok(link_status, dp->max_lanes)) { DRM_DEBUG_KMS("clock recovery OK\n"); return 0; } @@ -292,7 +292,7 @@ static int cdn_dp_link_training_channel_equalization(struct cdn_dp_device *dp) } for (tries = 0; tries < 5; tries++) { - drm_dp_link_train_channel_eq_delay(dp->dpcd); + drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd); if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) != DP_LINK_STATUS_SIZE) { DRM_ERROR("failed to get link status\n"); @@ -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..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]; @@ -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: @@ -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); @@ -692,7 +717,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) 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 */