mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +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:
@@ -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".
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
Reference in New Issue
Block a user