drm/rockchip: analogix_dp: Add support for rk3568

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

Change-Id: Ieb89906cba5bc569ed8c476fecd00f6035a7f582
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
Wyon Bi
2020-09-23 14:25:25 +08:00
committed by Tao Huang
parent 6d45fa9b4d
commit 0173eec295
5 changed files with 134 additions and 5 deletions

View File

@@ -257,6 +257,14 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2);
if (retval < 0)
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 = drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, buf, 2);
if (retval < 0)
return retval;
/* set enhanced mode if available */
retval = analogix_dp_set_enhanced_mode(dp);
if (retval < 0) {
@@ -559,6 +567,7 @@ static int analogix_dp_full_link_train(struct analogix_dp_device *dp,
{
int retval = 0;
bool training_finished = false;
u8 dpcd;
/*
* MACRO_RST must be applied after the PLL_LOCK to avoid
@@ -584,6 +593,9 @@ static int analogix_dp_full_link_train(struct analogix_dp_device *dp,
dp->link_train.lane_count = (u8)LANE_COUNT1;
}
drm_dp_dpcd_readb(&dp->aux, DP_MAX_DOWNSPREAD, &dpcd);
dp->link_train.ssc = !!(dpcd & DP_MAX_DOWNSPREAD_0_5);
/* Setup TX lane count & rate */
if (dp->link_train.lane_count > max_lanes)
dp->link_train.lane_count = max_lanes;
@@ -1515,6 +1527,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
switch (dp->plat_data->dev_type) {
case RK3288_DP:
case RK3399_EDP:
case RK3568_EDP:
/*
* Like Rk3288 DisplayPort TRM indicate that "Main link
* containing 4 physical lanes of 2.7/1.62 Gbps/lane".

View File

@@ -153,6 +153,7 @@ struct link_train {
u8 link_rate;
u8 lane_count;
u8 training_lane[4];
bool ssc;
enum link_training_state lt_state;
};
@@ -243,5 +244,6 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
struct drm_dp_aux_msg *msg);
void analogix_dp_set_video_format(struct analogix_dp_device *dp);
void analogix_dp_video_bist_enable(struct analogix_dp_device *dp);
bool analogix_dp_ssc_supported(struct analogix_dp_device *dp);
#endif /* _ANALOGIX_DP_CORE_H */

View File

@@ -11,6 +11,7 @@
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <drm/bridge/analogix_dp.h>
@@ -530,6 +531,12 @@ void analogix_dp_enable_sw_function(struct analogix_dp_device *dp)
analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg);
}
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)
{
u32 reg, status;
@@ -539,6 +546,24 @@ 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))
analogix_dp_write(dp, ANALOGIX_DP_LINK_BW_SET, reg);
if (dp->phy) {
union phy_configure_opts phy_cfg = {0};
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 = phy_configure(dp->phy, &phy_cfg);
if (ret && ret != -EOPNOTSUPP) {
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,
status != PLL_UNLOCKED, 120,
120 * DP_TIMEOUT_LOOP_COUNT);
@@ -559,9 +584,25 @@ 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)
{
u32 reg;
int ret;
reg = count;
analogix_dp_write(dp, ANALOGIX_DP_LANE_COUNT_SET, reg);
if (dp->phy) {
union phy_configure_opts phy_cfg = {0};
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 = phy_configure(dp->phy, &phy_cfg);
if (ret && ret != -EOPNOTSUPP) {
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)
@@ -575,11 +616,39 @@ 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)
{
u8 lane;
int ret;
for (lane = 0; lane < dp->link_train.lane_count; lane++)
analogix_dp_write(dp,
ANALOGIX_DP_LN0_LINK_TRAINING_CTL + 4 * lane,
dp->link_train.training_lane[lane]);
if (dp->phy) {
union phy_configure_opts phy_cfg = {0};
for (lane = 0; lane < dp->link_train.lane_count; lane++) {
u8 training_lane = dp->link_train.training_lane[lane];
u8 vs, pe;
vs = (training_lane & DP_TRAIN_VOLTAGE_SWING_MASK) >>
DP_TRAIN_VOLTAGE_SWING_SHIFT;
pe = (training_lane & DP_TRAIN_PRE_EMPHASIS_MASK) >>
DP_TRAIN_PRE_EMPHASIS_SHIFT;
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 = phy_configure(dp->phy, &phy_cfg);
if (ret && ret != -EOPNOTSUPP) {
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)

View File

@@ -16,6 +16,7 @@
#include <linux/reset.h>
#include <linux/clk.h>
#include <uapi/linux/videodev2.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
@@ -48,12 +49,14 @@
* @lcdsel_big: reg value of selecting vop big 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 {
u32 lcdsel_grf_reg;
u32 lcdsel_big;
u32 lcdsel_lit;
u32 chip_type;
bool ssc;
};
struct rockchip_dp_device {
@@ -64,6 +67,7 @@ struct rockchip_dp_device {
struct regmap *grf;
struct reset_control *rst;
struct reset_control *apb_reset;
const struct rockchip_dp_chip_data *data;
@@ -77,6 +81,10 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
usleep_range(10, 20);
reset_control_deassert(dp->rst);
reset_control_assert(dp->apb_reset);
usleep_range(10, 20);
reset_control_deassert(dp->apb_reset);
return 0;
}
@@ -168,6 +176,9 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
if (old_crtc_state && old_crtc_state->self_refresh_active)
return;
if (!dp->data->lcdsel_grf_reg)
return;
ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
if (ret < 0)
return;
@@ -215,6 +226,11 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct drm_display_info *di = &conn_state->connector->display_info;
if (di->num_bus_formats)
s->bus_format = di->bus_formats[0];
else
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
/*
* The hardware IC designed that VOP must output the RGB10 video
* format to eDP controller, and if eDP panel only support RGB8,
@@ -225,7 +241,12 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->output_type = DRM_MODE_CONNECTOR_eDP;
s->output_if |= VOP_OUTPUT_IF_eDP0;
s->output_bpc = di->bpc;
s->bus_flags = di->bus_flags;
s->tv_state = &conn_state->tv;
s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
s->color_space = V4L2_COLORSPACE_DEFAULT;
return 0;
}
@@ -243,10 +264,12 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
struct device *dev = dp->dev;
struct device_node *np = dev->of_node;
dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(dp->grf)) {
DRM_DEV_ERROR(dev, "failed to get rockchip,grf property\n");
return PTR_ERR(dp->grf);
if (of_property_read_bool(np, "rockchip,grf")) {
dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(dp->grf)) {
DRM_DEV_ERROR(dev, "failed to get rockchip,grf\n");
return PTR_ERR(dp->grf);
}
}
dp->rst = devm_reset_control_get(dev, "dp");
@@ -255,6 +278,12 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
return PTR_ERR(dp->rst);
}
dp->apb_reset = devm_reset_control_get_optional(dev, "apb");
if (IS_ERR(dp->apb_reset)) {
DRM_DEV_ERROR(dev, "failed to get apb reset control\n");
return PTR_ERR(dp->apb_reset);
}
return 0;
}
@@ -345,6 +374,7 @@ static int rockchip_dp_probe(struct platform_device *pdev)
dp->dev = dev;
dp->adp = ERR_PTR(-ENODEV);
dp->data = dp_data;
dp->plat_data.ssc = dp->data->ssc;
dp->plat_data.panel = panel;
dp->plat_data.dev_type = dp->data->chip_type;
dp->plat_data.power_on_start = rockchip_dp_poweron_start;
@@ -413,9 +443,15 @@ static const struct rockchip_dp_chip_data rk3288_dp = {
.chip_type = RK3288_DP,
};
static const struct rockchip_dp_chip_data rk3568_edp = {
.chip_type = RK3568_EDP,
.ssc = true,
};
static const struct of_device_id rockchip_dp_dt_ids[] = {
{.compatible = "rockchip,rk3288-dp", .data = &rk3288_dp },
{.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp },
{.compatible = "rockchip,rk3568-edp", .data = &rk3568_edp },
{}
};
MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids);

View File

@@ -15,11 +15,19 @@ enum analogix_dp_devtype {
EXYNOS_DP,
RK3288_DP,
RK3399_EDP,
RK3568_EDP,
};
static inline bool is_rockchip(enum analogix_dp_devtype type)
{
return type == RK3288_DP || type == RK3399_EDP;
switch (type) {
case RK3288_DP:
case RK3399_EDP:
case RK3568_EDP:
return true;
default:
return false;
}
}
struct analogix_dp_plat_data {
@@ -28,6 +36,7 @@ struct analogix_dp_plat_data {
struct drm_encoder *encoder;
struct drm_connector *connector;
bool skip_connector;
bool ssc;
int (*power_on_start)(struct analogix_dp_plat_data *);
int (*power_on_end)(struct analogix_dp_plat_data *);