mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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 <zyw@rock-chips.com> Signed-off-by: Lin Huang <hl@rock-chips.com> Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com> Signed-off-by: Zhang Yubing <yubing.zhang@rock-chips.com>
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#define _CDN_DP_REG_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-rockchip-typec.h>
|
||||
|
||||
#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 */
|
||||
|
||||
Reference in New Issue
Block a user