mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
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:
@@ -341,6 +341,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) {
|
||||
@@ -643,6 +651,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
|
||||
@@ -668,6 +677,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;
|
||||
@@ -1419,6 +1431,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
|
||||
case RK3288_DP:
|
||||
case RK3368_EDP:
|
||||
case RK3399_EDP:
|
||||
case RK3568_EDP:
|
||||
video_info->max_link_rate = 0x0A;
|
||||
video_info->max_lane_count = 0x04;
|
||||
break;
|
||||
|
||||
@@ -157,6 +157,7 @@ struct link_train {
|
||||
u8 link_rate;
|
||||
u8 lane_count;
|
||||
u8 training_lane[4];
|
||||
bool ssc;
|
||||
|
||||
enum link_training_state lt_state;
|
||||
};
|
||||
@@ -246,5 +247,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 */
|
||||
|
||||
@@ -15,6 +15,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>
|
||||
|
||||
@@ -617,8 +618,15 @@ int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
|
||||
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)
|
||||
{
|
||||
union phy_configure_opts phy_cfg;
|
||||
u32 reg, status;
|
||||
int ret;
|
||||
|
||||
@@ -626,6 +634,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))
|
||||
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 = 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);
|
||||
@@ -645,10 +667,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)
|
||||
{
|
||||
union phy_configure_opts phy_cfg;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
reg = count;
|
||||
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 = 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)
|
||||
@@ -661,12 +696,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)
|
||||
{
|
||||
union phy_configure_opts phy_cfg;
|
||||
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_LN0_LINK_TRAINING_CTL + 4 * lane,
|
||||
dp->link_train.training_lane[lane]);
|
||||
training_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 = 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)
|
||||
|
||||
@@ -62,6 +62,7 @@ struct rockchip_dp_chip_data {
|
||||
u32 lcdsel_big;
|
||||
u32 lcdsel_lit;
|
||||
u32 chip_type;
|
||||
bool ssc;
|
||||
};
|
||||
|
||||
struct rockchip_dp_device {
|
||||
@@ -74,6 +75,7 @@ struct rockchip_dp_device {
|
||||
struct clk *grfclk;
|
||||
struct regmap *grf;
|
||||
struct reset_control *rst;
|
||||
struct reset_control *apb_reset;
|
||||
struct regulator *vcc_supply;
|
||||
struct regulator *vccio_supply;
|
||||
|
||||
@@ -113,6 +115,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;
|
||||
}
|
||||
|
||||
@@ -256,6 +262,7 @@ 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;
|
||||
if (di->num_bus_formats)
|
||||
s->bus_format = di->bus_formats[0];
|
||||
@@ -323,10 +330,12 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret = 0;
|
||||
|
||||
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->grfclk = devm_clk_get(dev, "grf");
|
||||
@@ -351,6 +360,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);
|
||||
}
|
||||
|
||||
dp->vcc_supply = devm_regulator_get_optional(dev, "vcc");
|
||||
if (IS_ERR(dp->vcc_supply)) {
|
||||
if (PTR_ERR(dp->vcc_supply) != -ENODEV) {
|
||||
@@ -422,7 +437,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
|
||||
}
|
||||
|
||||
dp->plat_data.encoder = &dp->encoder;
|
||||
|
||||
dp->plat_data.ssc = dp->data->ssc;
|
||||
dp->plat_data.dev_type = dp->data->chip_type;
|
||||
dp->plat_data.power_on_start = rockchip_dp_poweron_start;
|
||||
dp->plat_data.power_on_end = rockchip_dp_poweron_end;
|
||||
@@ -571,10 +586,16 @@ 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,rk3368-edp", .data = &rk3368_edp },
|
||||
{.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp },
|
||||
{.compatible = "rockchip,rk3568-edp", .data = &rk3568_edp },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids);
|
||||
|
||||
@@ -20,11 +20,20 @@ enum analogix_dp_devtype {
|
||||
RK3288_DP,
|
||||
RK3368_EDP,
|
||||
RK3399_EDP,
|
||||
RK3568_EDP,
|
||||
};
|
||||
|
||||
static inline bool is_rockchip(enum analogix_dp_devtype type)
|
||||
{
|
||||
return type == RK3288_DP || type == RK3399_EDP || type == RK3368_EDP;
|
||||
switch (type) {
|
||||
case RK3288_DP:
|
||||
case RK3368_EDP:
|
||||
case RK3399_EDP:
|
||||
case RK3568_EDP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct analogix_dp_plat_data {
|
||||
@@ -33,6 +42,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 *);
|
||||
|
||||
Reference in New Issue
Block a user