mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
drm/rockchip: dw-dp: Add support for split mode
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com> Change-Id: I707d1a84d232562338b2676a7037dc0d6ec49be4
This commit is contained in:
@@ -251,6 +251,10 @@ struct dw_dp {
|
||||
struct dw_dp_audio audio;
|
||||
|
||||
DECLARE_BITMAP(sdp_reg_bank, SDP_REG_BANK_SIZE);
|
||||
|
||||
bool split_mode;
|
||||
struct dw_dp *left;
|
||||
struct dw_dp *right;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -312,6 +316,26 @@ static inline struct dw_dp *bridge_to_dp(struct drm_bridge *b)
|
||||
return container_of(b, struct dw_dp, bridge);
|
||||
}
|
||||
|
||||
static int dw_dp_match_by_id(struct device *dev, const void *data)
|
||||
{
|
||||
struct dw_dp *dp = dev_get_drvdata(dev);
|
||||
const unsigned int *id = data;
|
||||
|
||||
return dp->id == *id;
|
||||
}
|
||||
|
||||
static struct dw_dp *dw_dp_find_by_id(struct device_driver *drv,
|
||||
unsigned int id)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = driver_find_device(drv, NULL, &id, dw_dp_match_by_id);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
return dev_get_drvdata(dev);
|
||||
}
|
||||
|
||||
static void dw_dp_phy_set_pattern(struct dw_dp *dp, u32 pattern)
|
||||
{
|
||||
regmap_update_bits(dp->regmap, DPTX_PHYIF_CTRL, TPS_SEL,
|
||||
@@ -400,6 +424,9 @@ dw_dp_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct dw_dp *dp = connector_to_dp(connector);
|
||||
|
||||
if (dp->right && drm_bridge_detect(&dp->right->bridge) != connector_status_connected)
|
||||
return connector_status_disconnected;
|
||||
|
||||
return drm_bridge_detect(&dp->bridge);
|
||||
}
|
||||
|
||||
@@ -428,6 +455,13 @@ static int dw_dp_connector_get_modes(struct drm_connector *connector)
|
||||
num_modes = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
if (num_modes > 0 && dp->split_mode) {
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
list_for_each_entry(mode, &connector->probed_modes, head)
|
||||
drm_mode_convert_to_split_mode(mode);
|
||||
}
|
||||
|
||||
return num_modes;
|
||||
}
|
||||
|
||||
@@ -1468,7 +1502,13 @@ static int dw_dp_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
|
||||
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
|
||||
s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
|
||||
s->output_if = dp->id ? VOP_OUTPUT_IF_DP1 : VOP_OUTPUT_IF_DP0;
|
||||
if (dp->split_mode) {
|
||||
s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
|
||||
s->output_flags |= dp->id ? ROCKCHIP_OUTPUT_DATA_SWAP : 0;
|
||||
s->output_if |= VOP_OUTPUT_IF_DP0 | VOP_OUTPUT_IF_DP1;
|
||||
} else {
|
||||
s->output_if = dp->id ? VOP_OUTPUT_IF_DP1 : VOP_OUTPUT_IF_DP0;
|
||||
}
|
||||
s->output_bpc = di->bpc;
|
||||
s->bus_flags = di->bus_flags;
|
||||
s->tv_state = &conn_state->tv;
|
||||
@@ -1587,8 +1627,14 @@ static int dw_dp_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
{
|
||||
struct dw_dp *dp = bridge_to_dp(bridge);
|
||||
struct dw_dp_link *link = &dp->link;
|
||||
struct drm_display_mode m;
|
||||
|
||||
if (!dw_dp_bandwidth_ok(dp, mode, link->lanes, link->rate))
|
||||
drm_mode_copy(&m, mode);
|
||||
|
||||
if (dp->split_mode)
|
||||
drm_mode_convert_to_origin_mode(&m);
|
||||
|
||||
if (!dw_dp_bandwidth_ok(dp, &m, link->lanes, link->rate))
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
return MODE_OK;
|
||||
@@ -1624,11 +1670,6 @@ static int dw_dp_bridge_attach(struct drm_bridge *bridge,
|
||||
|
||||
drm_connector_attach_encoder(connector, bridge->encoder);
|
||||
|
||||
pm_runtime_enable(dp->dev);
|
||||
pm_runtime_get_sync(dp->dev);
|
||||
dw_dp_init(dp);
|
||||
enable_irq(dp->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1636,9 +1677,6 @@ static void dw_dp_bridge_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct dw_dp *dp = bridge_to_dp(bridge);
|
||||
|
||||
disable_irq(dp->irq);
|
||||
pm_runtime_put(dp->dev);
|
||||
pm_runtime_disable(dp->dev);
|
||||
drm_connector_cleanup(&dp->connector);
|
||||
}
|
||||
|
||||
@@ -1648,8 +1686,12 @@ static void dw_dp_bridge_mode_set(struct drm_bridge *bridge,
|
||||
{
|
||||
struct dw_dp *dp = bridge_to_dp(bridge);
|
||||
struct dw_dp_video *video = &dp->video;
|
||||
struct drm_display_mode *m = &video->mode;
|
||||
|
||||
drm_mode_copy(&video->mode, adjusted_mode);
|
||||
drm_mode_copy(m, adjusted_mode);
|
||||
|
||||
if (dp->split_mode)
|
||||
drm_mode_convert_to_origin_mode(m);
|
||||
}
|
||||
|
||||
static bool dw_dp_needs_link_retrain(struct dw_dp *dp)
|
||||
@@ -1734,29 +1776,29 @@ static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
video->stream_on = false;
|
||||
}
|
||||
|
||||
static enum drm_connector_status dw_dp_detect(struct dw_dp *dp)
|
||||
static bool dw_dp_detect(struct dw_dp *dp)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
if (dp->hpd_gpio) {
|
||||
if (gpiod_get_value_cansleep(dp->hpd_gpio))
|
||||
return connector_status_connected;
|
||||
else
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
if (dp->hpd_gpio)
|
||||
return gpiod_get_value_cansleep(dp->hpd_gpio);
|
||||
|
||||
regmap_read(dp->regmap, DPTX_HPD_STATUS, &value);
|
||||
if (value & HPD_STATUS)
|
||||
return connector_status_connected;
|
||||
else
|
||||
return connector_status_disconnected;
|
||||
|
||||
return !!(value & HPD_STATUS);
|
||||
}
|
||||
|
||||
static enum drm_connector_status dw_dp_bridge_detect(struct drm_bridge *bridge)
|
||||
{
|
||||
struct dw_dp *dp = bridge_to_dp(bridge);
|
||||
enum drm_connector_status status;
|
||||
|
||||
return dw_dp_detect(dp);
|
||||
if (dw_dp_detect(dp))
|
||||
status = connector_status_connected;
|
||||
else
|
||||
status = connector_status_disconnected;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct edid *dw_dp_bridge_get_edid(struct drm_bridge *bridge,
|
||||
@@ -1790,7 +1832,7 @@ static void dw_dp_hpd_work(struct work_struct *work)
|
||||
|
||||
mutex_lock(&dp->bridge.dev->mode_config.mutex);
|
||||
|
||||
if (dw_dp_detect(dp) == connector_status_connected) {
|
||||
if (dw_dp_detect(dp)) {
|
||||
if (dw_dp_needs_link_retrain(dp)) {
|
||||
if (video->stream_on)
|
||||
dw_dp_video_disable(dp);
|
||||
@@ -2038,18 +2080,34 @@ static int dw_dp_bind(struct device *dev, struct device *master, void *data)
|
||||
struct drm_bridge *bridge = &dp->bridge;
|
||||
int ret;
|
||||
|
||||
drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
|
||||
drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
|
||||
if (!dp->left) {
|
||||
drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
|
||||
drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
|
||||
|
||||
encoder->possible_crtcs =
|
||||
rockchip_drm_of_find_possible_crtcs(drm_dev, dev->of_node);
|
||||
encoder->possible_crtcs =
|
||||
rockchip_drm_of_find_possible_crtcs(drm_dev, dev->of_node);
|
||||
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to attach bridge: %d\n", ret);
|
||||
return ret;
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to attach bridge: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (dp->right) {
|
||||
struct dw_dp *secondary = dp->right;
|
||||
|
||||
ret = drm_bridge_attach(encoder, &secondary->bridge, bridge,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dp->dev);
|
||||
pm_runtime_get_sync(dp->dev);
|
||||
dw_dp_init(dp);
|
||||
enable_irq(dp->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2057,6 +2115,10 @@ static void dw_dp_unbind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct dw_dp *dp = dev_get_drvdata(dev);
|
||||
|
||||
disable_irq(dp->irq);
|
||||
pm_runtime_put(dp->dev);
|
||||
pm_runtime_disable(dp->dev);
|
||||
|
||||
drm_encoder_cleanup(&dp->encoder);
|
||||
}
|
||||
|
||||
@@ -2185,10 +2247,21 @@ static int dw_dp_probe(struct platform_device *pdev)
|
||||
dp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
|
||||
DRM_BRIDGE_OP_HPD;
|
||||
dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
|
||||
drm_bridge_add(&dp->bridge);
|
||||
|
||||
platform_set_drvdata(pdev, dp);
|
||||
|
||||
if (device_property_read_bool(dev, "split-mode")) {
|
||||
struct dw_dp *secondary = dw_dp_find_by_id(dev->driver, !dp->id);
|
||||
|
||||
if (!secondary)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dp->right = secondary;
|
||||
dp->split_mode = true;
|
||||
secondary->left = dp;
|
||||
secondary->split_mode = true;
|
||||
}
|
||||
|
||||
return component_add(dev, &dw_dp_component_ops);
|
||||
}
|
||||
|
||||
@@ -2197,7 +2270,6 @@ static int dw_dp_remove(struct platform_device *pdev)
|
||||
struct dw_dp *dp = platform_get_drvdata(pdev);
|
||||
|
||||
component_del(dp->dev, &dw_dp_component_ops);
|
||||
drm_bridge_remove(&dp->bridge);
|
||||
cancel_delayed_work(&dp->hpd_work);
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user