video/drm: analogix_dp: Add support for rk3568

This patch adds support for Analogix eDP TX IP used on RK3568 SoC.

Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
Change-Id: Ia48f1f99f336d4d98d5fba4e5fd15a35bdbaf373
This commit is contained in:
Wyon Bi 2020-12-10 03:27:32 +00:00
parent c5b1fb658e
commit 699c29a5d8
3 changed files with 105 additions and 16 deletions

View File

@ -27,13 +27,15 @@
* @lcdsel_grf_reg: grf register offset of lcdc select * @lcdsel_grf_reg: grf register offset of lcdc select
* @lcdsel_big: reg value of selecting vop big for eDP * @lcdsel_big: reg value of selecting vop big for eDP
* @lcdsel_lit: reg value of selecting vop little for eDP * @lcdsel_lit: reg value of selecting vop little for eDP
* @chip_type: specific chip type
* @ssc: check if SSC is supported by source
*/ */
struct rockchip_dp_chip_data { struct rockchip_dp_chip_data {
u32 lcdsel_grf_reg; u32 lcdsel_grf_reg;
u32 lcdsel_big; u32 lcdsel_big;
u32 lcdsel_lit; u32 lcdsel_lit;
u32 chip_type; u32 chip_type;
bool has_vop_sel; bool ssc;
}; };
static void static void
@ -105,6 +107,14 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
if (retval) if (retval)
return retval; return retval;
/* Spread AMP if required, enable 8b/10b coding */
buf[0] = analogix_dp_ssc_supported(dp) ? DP_SPREAD_AMP_0_5 : 0;
buf[1] = DP_SET_ANSI_8B10B;
retval = analogix_dp_write_bytes_to_dpcd(dp, DP_DOWNSPREAD_CTRL,
2, buf);
if (retval < 0)
return retval;
/* Set TX voltage-swing and pre-emphasis to minimum */ /* Set TX voltage-swing and pre-emphasis to minimum */
for (lane = 0; lane < lane_count; lane++) for (lane = 0; lane < lane_count; lane++)
dp->link_train.training_lane[lane] = dp->link_train.training_lane[lane] =
@ -397,6 +407,8 @@ static int analogix_dp_init_training(struct analogix_dp_device *dp,
enum link_lane_count_type max_lane, enum link_lane_count_type max_lane,
int max_rate) int max_rate)
{ {
u8 dpcd;
/* /*
* MACRO_RST must be applied after the PLL_LOCK to avoid * MACRO_RST must be applied after the PLL_LOCK to avoid
* the DP inter pair skew issue for at least 10 us * the DP inter pair skew issue for at least 10 us
@ -425,6 +437,9 @@ static int analogix_dp_init_training(struct analogix_dp_device *dp,
if (dp->link_train.link_rate > max_rate) if (dp->link_train.link_rate > max_rate)
dp->link_train.link_rate = max_rate; dp->link_train.link_rate = max_rate;
analogix_dp_read_byte_from_dpcd(dp, DP_MAX_DOWNSPREAD, &dpcd);
dp->link_train.ssc = !!(dpcd & DP_MAX_DOWNSPREAD_0_5);
/* All DP analog module power up */ /* All DP analog module power up */
analogix_dp_set_analog_power_down(dp, POWER_ALL, 0); analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
@ -721,14 +736,15 @@ static int analogix_dp_connector_init(struct display_state *state)
struct analogix_dp_device *dp = dev_get_priv(conn_state->dev); struct analogix_dp_device *dp = dev_get_priv(conn_state->dev);
conn_state->type = DRM_MODE_CONNECTOR_eDP; conn_state->type = DRM_MODE_CONNECTOR_eDP;
conn_state->output_if |= VOP_OUTPUT_IF_eDP0;
conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA; conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
conn_state->color_space = V4L2_COLORSPACE_DEFAULT; conn_state->color_space = V4L2_COLORSPACE_DEFAULT;
/* eDP software reset request */ reset_assert_bulk(&dp->resets);
reset_assert(&dp->reset);
udelay(1); udelay(1);
reset_deassert(&dp->reset); reset_deassert_bulk(&dp->resets);
generic_phy_power_on(&dp->phy);
analogix_dp_init_dp(dp); analogix_dp_init_dp(dp);
return 0; return 0;
@ -762,13 +778,13 @@ static int analogix_dp_connector_enable(struct display_state *state)
u32 val; u32 val;
int ret; int ret;
if (pdata->has_vop_sel) { if (pdata->lcdsel_grf_reg) {
if (crtc_state->crtc_id) if (crtc_state->crtc_id)
val = pdata->lcdsel_lit; val = pdata->lcdsel_lit;
else else
val = pdata->lcdsel_big; val = pdata->lcdsel_big;
writel(val, dp->grf + pdata->lcdsel_grf_reg); writel(val, syscon_get_first_range(ROCKCHIP_SYSCON_GRF) + pdata->lcdsel_grf_reg);
} }
switch (conn_state->bpc) { switch (conn_state->bpc) {
@ -840,11 +856,8 @@ static int analogix_dp_probe(struct udevice *dev)
int ret; int ret;
dp->reg_base = dev_read_addr_ptr(dev); dp->reg_base = dev_read_addr_ptr(dev);
dp->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
if (IS_ERR(dp->grf))
return PTR_ERR(dp->grf);
ret = reset_get_by_name(dev, "dp", &dp->reset); ret = reset_get_bulk(dev, &dp->resets);
if (ret) { if (ret) {
dev_err(dev, "failed to get reset control: %d\n", ret); dev_err(dev, "failed to get reset control: %d\n", ret);
return ret; return ret;
@ -857,10 +870,13 @@ static int analogix_dp_probe(struct udevice *dev)
return ret; return ret;
} }
generic_phy_get_by_name(dev, "dp", &dp->phy);
dp->force_hpd = dev_read_bool(dev, "force-hpd"); dp->force_hpd = dev_read_bool(dev, "force-hpd");
dp->plat_data.dev_type = ROCKCHIP_DP; dp->plat_data.dev_type = ROCKCHIP_DP;
dp->plat_data.subdev_type = pdata->chip_type; dp->plat_data.subdev_type = pdata->chip_type;
dp->plat_data.ssc = pdata->ssc;
/* /*
* Like Rockchip DisplayPort TRM indicate that "Main link * Like Rockchip DisplayPort TRM indicate that "Main link
* containing 4 physical lanes of 2.7/1.62 Gbps/lane". * containing 4 physical lanes of 2.7/1.62 Gbps/lane".
@ -878,7 +894,6 @@ static const struct rockchip_dp_chip_data rk3288_edp_platform_data = {
.lcdsel_big = 0 | BIT(21), .lcdsel_big = 0 | BIT(21),
.lcdsel_lit = BIT(5) | BIT(21), .lcdsel_lit = BIT(5) | BIT(21),
.chip_type = RK3288_DP, .chip_type = RK3288_DP,
.has_vop_sel = true,
}; };
static const struct rockchip_connector rk3288_edp_driver_data = { static const struct rockchip_connector rk3288_edp_driver_data = {
@ -888,7 +903,6 @@ static const struct rockchip_connector rk3288_edp_driver_data = {
static const struct rockchip_dp_chip_data rk3368_edp_platform_data = { static const struct rockchip_dp_chip_data rk3368_edp_platform_data = {
.chip_type = RK3368_EDP, .chip_type = RK3368_EDP,
.has_vop_sel = false,
}; };
static const struct rockchip_connector rk3368_edp_driver_data = { static const struct rockchip_connector rk3368_edp_driver_data = {
@ -901,7 +915,6 @@ static const struct rockchip_dp_chip_data rk3399_edp_platform_data = {
.lcdsel_big = 0 | BIT(21), .lcdsel_big = 0 | BIT(21),
.lcdsel_lit = BIT(5) | BIT(21), .lcdsel_lit = BIT(5) | BIT(21),
.chip_type = RK3399_EDP, .chip_type = RK3399_EDP,
.has_vop_sel = true,
}; };
static const struct rockchip_connector rk3399_edp_driver_data = { static const struct rockchip_connector rk3399_edp_driver_data = {
@ -909,6 +922,16 @@ static const struct rockchip_connector rk3399_edp_driver_data = {
.data = &rk3399_edp_platform_data, .data = &rk3399_edp_platform_data,
}; };
static const struct rockchip_dp_chip_data rk3568_edp_platform_data = {
.chip_type = RK3568_EDP,
.ssc = true,
};
static const struct rockchip_connector rk3568_edp_driver_data = {
.funcs = &analogix_dp_connector_funcs,
.data = &rk3568_edp_platform_data,
};
static const struct udevice_id analogix_dp_ids[] = { static const struct udevice_id analogix_dp_ids[] = {
{ {
.compatible = "rockchip,rk3288-dp", .compatible = "rockchip,rk3288-dp",
@ -919,6 +942,9 @@ static const struct udevice_id analogix_dp_ids[] = {
}, { }, {
.compatible = "rockchip,rk3399-edp", .compatible = "rockchip,rk3399-edp",
.data = (ulong)&rk3399_edp_driver_data, .data = (ulong)&rk3399_edp_driver_data,
}, {
.compatible = "rockchip,rk3568-edp",
.data = (ulong)&rk3568_edp_driver_data,
}, },
{} {}
}; };

View File

@ -7,6 +7,7 @@
#ifndef __DRM_ANALOGIX_DP_H__ #ifndef __DRM_ANALOGIX_DP_H__
#define __DRM_ANALOGIX_DP_H__ #define __DRM_ANALOGIX_DP_H__
#include <generic-phy.h>
#include <reset.h> #include <reset.h>
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
@ -515,6 +516,7 @@ struct link_train {
u8 link_rate; u8 link_rate;
u8 lane_count; u8 lane_count;
u8 training_lane[4]; u8 training_lane[4];
bool ssc;
enum link_training_state lt_state; enum link_training_state lt_state;
}; };
@ -528,18 +530,20 @@ enum analogix_dp_sub_devtype {
RK3288_DP, RK3288_DP,
RK3368_EDP, RK3368_EDP,
RK3399_EDP, RK3399_EDP,
RK3568_EDP,
}; };
struct analogix_dp_plat_data { struct analogix_dp_plat_data {
enum analogix_dp_devtype dev_type; enum analogix_dp_devtype dev_type;
enum analogix_dp_sub_devtype subdev_type; enum analogix_dp_sub_devtype subdev_type;
bool ssc;
}; };
struct analogix_dp_device { struct analogix_dp_device {
struct udevice *dev; struct udevice *dev;
void *reg_base; void *reg_base;
void *grf; struct phy phy;
struct reset_ctl reset; struct reset_ctl_bulk resets;
struct gpio_desc hpd_gpio; struct gpio_desc hpd_gpio;
bool force_hpd; bool force_hpd;
struct video_info video_info; struct video_info video_info;
@ -628,5 +632,6 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp); void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
void analogix_dp_enable_scrambling(struct analogix_dp_device *dp); void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
void analogix_dp_disable_scrambling(struct analogix_dp_device *dp); void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
bool analogix_dp_ssc_supported(struct analogix_dp_device *dp);
#endif /* __DRM_ANALOGIX_DP__ */ #endif /* __DRM_ANALOGIX_DP__ */

View File

@ -906,8 +906,15 @@ int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
return retval; return retval;
} }
bool analogix_dp_ssc_supported(struct analogix_dp_device *dp)
{
/* Check if SSC is supported by both sides */
return dp->plat_data.ssc && dp->link_train.ssc;
}
void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype) void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
{ {
union phy_configure_opts phy_cfg;
u32 reg, status; u32 reg, status;
int ret; int ret;
@ -915,6 +922,20 @@ void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62)) if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62))
analogix_dp_write(dp, ANALOGIX_DP_LINK_BW_SET, reg); analogix_dp_write(dp, ANALOGIX_DP_LINK_BW_SET, reg);
phy_cfg.dp.lanes = dp->link_train.lane_count;
phy_cfg.dp.link_rate =
drm_dp_bw_code_to_link_rate(dp->link_train.link_rate) / 100;
phy_cfg.dp.ssc = analogix_dp_ssc_supported(dp);
phy_cfg.dp.set_lanes = false;
phy_cfg.dp.set_rate = true;
phy_cfg.dp.set_voltages = false;
ret = generic_phy_configure(&dp->phy, &phy_cfg);
if (ret) {
dev_err(dp->dev, "%s: phy_configure() failed: %d\n",
__func__, ret);
return;
}
ret = readx_poll_timeout(analogix_dp_get_pll_lock_status, dp, status, ret = readx_poll_timeout(analogix_dp_get_pll_lock_status, dp, status,
status != PLL_UNLOCKED, status != PLL_UNLOCKED,
120 * DP_TIMEOUT_LOOP_COUNT); 120 * DP_TIMEOUT_LOOP_COUNT);
@ -934,10 +955,23 @@ void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype)
void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count) void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count)
{ {
union phy_configure_opts phy_cfg;
u32 reg; u32 reg;
int ret;
reg = count; reg = count;
analogix_dp_write(dp, ANALOGIX_DP_LANE_COUNT_SET, reg); analogix_dp_write(dp, ANALOGIX_DP_LANE_COUNT_SET, reg);
phy_cfg.dp.lanes = dp->link_train.lane_count;
phy_cfg.dp.set_lanes = true;
phy_cfg.dp.set_rate = false;
phy_cfg.dp.set_voltages = false;
ret = generic_phy_configure(&dp->phy, &phy_cfg);
if (ret) {
dev_err(dp->dev, "%s: phy_configure() failed: %d\n",
__func__, ret);
return;
}
} }
void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count) void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
@ -950,12 +984,36 @@ void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp) void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp)
{ {
union phy_configure_opts phy_cfg;
u8 lane; u8 lane;
int ret;
for (lane = 0; lane < dp->link_train.lane_count; lane++) {
u8 training_lane = dp->link_train.training_lane[lane];
u8 vs, pe;
for (lane = 0; lane < dp->link_train.lane_count; lane++)
analogix_dp_write(dp, analogix_dp_write(dp,
ANALOGIX_DP_LN0_LINK_TRAINING_CTL + 4 * lane, ANALOGIX_DP_LN0_LINK_TRAINING_CTL + 4 * lane,
dp->link_train.training_lane[lane]); dp->link_train.training_lane[lane]);
vs = (training_lane >> DP_TRAIN_VOLTAGE_SWING_SHIFT) &
DP_TRAIN_VOLTAGE_SWING_MASK;
pe = (training_lane >> DP_TRAIN_PRE_EMPHASIS_SHIFT) &
DP_TRAIN_PRE_EMPHASIS_MASK;
phy_cfg.dp.voltage[lane] = vs;
phy_cfg.dp.pre[lane] = pe;
}
phy_cfg.dp.lanes = dp->link_train.lane_count;
phy_cfg.dp.set_lanes = false;
phy_cfg.dp.set_rate = false;
phy_cfg.dp.set_voltages = true;
ret = generic_phy_configure(&dp->phy, &phy_cfg);
if (ret) {
dev_err(dp->dev, "%s: phy_configure() failed: %d\n",
__func__, ret);
return;
}
} }
u32 analogix_dp_get_lane_link_training(struct analogix_dp_device *dp, u8 lane) u32 analogix_dp_get_lane_link_training(struct analogix_dp_device *dp, u8 lane)