diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 322cd9fdaa71..7d98c0c3e4e3 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1116,6 +1116,13 @@ static int analogix_dp_get_modes(struct drm_connector *connector) if (dp->plat_data->get_modes) num_modes += dp->plat_data->get_modes(dp->plat_data, connector); + if (num_modes > 0 && dp->plat_data->split_mode) { + struct drm_display_mode *mode; + + list_for_each_entry(mode, &connector->probed_modes, head) + dp->plat_data->convert_to_split_mode(mode); + } + return num_modes; } @@ -1161,9 +1168,8 @@ static const struct drm_connector_helper_funcs analogix_dp_connector_helper_func }; static enum drm_connector_status -analogix_dp_detect(struct drm_connector *connector, bool force) +analogix_dp_detect(struct analogix_dp_device *dp) { - struct analogix_dp_device *dp = to_dp(connector); enum drm_connector_status status = connector_status_disconnected; if (dp->plat_data->panel) @@ -1171,17 +1177,32 @@ analogix_dp_detect(struct drm_connector *connector, bool force) pm_runtime_get_sync(dp->dev); - if (!analogix_dp_detect_hpd(dp)) + if (!analogix_dp_detect_hpd(dp)) { status = connector_status_connected; + analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); + analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count); + } + pm_runtime_put(dp->dev); return status; } +static enum drm_connector_status +analogix_dp_connector_detect(struct drm_connector *connector, bool force) +{ + struct analogix_dp_device *dp = to_dp(connector); + + if (dp->plat_data->right && analogix_dp_detect(dp->plat_data->right) != connector_status_connected) + return connector_status_disconnected; + + return analogix_dp_detect(dp); +} + static const struct drm_connector_funcs analogix_dp_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, - .detect = analogix_dp_detect, + .detect = analogix_dp_connector_detect, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -1196,10 +1217,8 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge, struct drm_connector *connector = NULL; int ret = 0; - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); @@ -1276,14 +1295,16 @@ analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge, struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; - crtc = analogix_dp_get_new_crtc(dp, old_state); - if (!crtc) - return; + if (dp->psr_supported) { + crtc = analogix_dp_get_new_crtc(dp, old_state); + if (!crtc) + return; - old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); - /* Don't touch the panel if we're coming back from PSR */ - if (old_crtc_state && old_crtc_state->self_refresh_active) - return; + old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); + /* Don't touch the panel if we're coming back from PSR */ + if (old_crtc_state && old_crtc_state->self_refresh_active) + return; + } if (dp->plat_data->panel) analogix_dp_panel_prepare(dp); @@ -1350,17 +1371,19 @@ analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge, int timeout_loop = 0; int ret; - crtc = analogix_dp_get_new_crtc(dp, old_state); - if (!crtc) - return; + if (dp->psr_supported) { + crtc = analogix_dp_get_new_crtc(dp, old_state); + if (!crtc) + return; - old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); - /* Not a full enable, just disable PSR and continue */ - if (old_crtc_state && old_crtc_state->self_refresh_active) { - ret = analogix_dp_disable_psr(dp); - if (ret) - DRM_ERROR("Failed to disable psr %d\n", ret); - return; + old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); + /* Not a full enable, just disable PSR and continue */ + if (old_crtc_state && old_crtc_state->self_refresh_active) { + ret = analogix_dp_disable_psr(dp); + if (ret) + DRM_ERROR("Failed to disable psr %d\n", ret); + return; + } } if (dp->dpms_mode == DRM_MODE_DPMS_ON) @@ -1462,15 +1485,18 @@ analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *orig_mode, - const struct drm_display_mode *mode) + const struct drm_display_mode *adj_mode) { struct analogix_dp_device *dp = bridge->driver_private; struct drm_display_info *display_info = &dp->connector.display_info; struct video_info *video = &dp->video_info; + struct drm_display_mode *mode = &video->mode; struct device_node *dp_node = dp->dev->of_node; int vic; - drm_mode_copy(&video->mode, mode); + drm_mode_copy(mode, adj_mode); + if (dp->plat_data->split_mode) + dp->plat_data->convert_to_origin_mode(mode); /* Input video interlaces & hsync pol & vsync pol */ video->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); @@ -1545,10 +1571,16 @@ analogix_dp_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_mode *mode) { struct analogix_dp_device *dp = bridge->driver_private; + struct drm_display_mode m; - if (!analogix_dp_bandwidth_ok(dp, mode, - drm_dp_bw_code_to_link_rate(dp->video_info.max_link_rate), - dp->video_info.max_lane_count)) + drm_mode_copy(&m, mode); + + if (dp->plat_data->split_mode) + dp->plat_data->convert_to_origin_mode(&m); + + if (!analogix_dp_bandwidth_ok(dp, &m, + drm_dp_bw_code_to_link_rate(dp->link_train.link_rate), + dp->link_train.lane_count)) return MODE_BAD; return MODE_OK; @@ -1568,27 +1600,26 @@ static const struct drm_bridge_funcs analogix_dp_bridge_funcs = { .mode_valid = analogix_dp_bridge_mode_valid, }; -static int analogix_dp_create_bridge(struct drm_device *drm_dev, - struct analogix_dp_device *dp) +static int analogix_dp_bridge_init(struct analogix_dp_device *dp) { - struct drm_bridge *bridge; + struct drm_bridge *bridge = &dp->bridge; int ret; - bridge = devm_kzalloc(drm_dev->dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) { - DRM_ERROR("failed to allocate for drm bridge\n"); - return -ENOMEM; + if (!dp->plat_data->left) { + ret = drm_bridge_attach(dp->encoder, bridge, NULL, 0); + if (ret) { + DRM_ERROR("failed to attach drm bridge\n"); + return ret; + } } - dp->bridge = bridge; + if (dp->plat_data->right) { + struct analogix_dp_device *secondary = dp->plat_data->right; - bridge->driver_private = dp; - bridge->funcs = &analogix_dp_bridge_funcs; - - ret = drm_bridge_attach(dp->encoder, bridge, NULL, 0); - if (ret) { - DRM_ERROR("failed to attach drm bridge\n"); - return -EINVAL; + ret = drm_bridge_attach(dp->encoder, &secondary->bridge, bridge, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) + return ret; } return 0; @@ -1806,6 +1837,9 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) return ERR_PTR(ret); } + dp->bridge.driver_private = dp; + dp->bridge.funcs = &analogix_dp_bridge_funcs; + return dp; } EXPORT_SYMBOL_GPL(analogix_dp_probe); @@ -1827,9 +1861,9 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) pm_runtime_enable(dp->dev); - ret = analogix_dp_create_bridge(drm_dev, dp); + ret = analogix_dp_bridge_init(dp); if (ret) { - DRM_ERROR("failed to create bridge (%d)\n", ret); + DRM_ERROR("failed to init bridge (%d)\n", ret); goto err_disable_pm_runtime; } @@ -1844,14 +1878,7 @@ EXPORT_SYMBOL_GPL(analogix_dp_bind); void analogix_dp_unbind(struct analogix_dp_device *dp) { - analogix_dp_bridge_disable(dp->bridge); dp->connector.funcs->destroy(&dp->connector); - - if (dp->plat_data->panel) { - if (drm_panel_unprepare(dp->plat_data->panel)) - DRM_ERROR("failed to turnoff the panel\n"); - } - drm_dp_aux_unregister(&dp->aux); pm_runtime_disable(dp->dev); } diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index a1f6ca4439de..28f2aaa144ef 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -10,6 +10,7 @@ #define _ANALOGIX_DP_CORE_H #include +#include #include #define DP_TIMEOUT_LOOP_COUNT 100 @@ -165,7 +166,7 @@ struct analogix_dp_device { struct device *dev; struct drm_device *drm_dev; struct drm_connector connector; - struct drm_bridge *bridge; + struct drm_bridge bridge; struct drm_dp_aux aux; struct clk_bulk_data *clks; int nr_clks; diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 513383e1c054..4726b16a36ec 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -59,6 +59,7 @@ struct rockchip_grf_reg_field { * @chip_type: specific chip type * @ssc: check if SSC is supported by source * @audio: check if audio is supported by source + * @split_mode: check if split mode is supported */ struct rockchip_dp_chip_data { const struct rockchip_grf_reg_field lcdc_sel; @@ -68,6 +69,7 @@ struct rockchip_dp_chip_data { u32 chip_type; bool ssc; bool audio; + bool split_mode; }; struct rockchip_dp_device { @@ -157,6 +159,26 @@ static const struct hdmi_codec_ops rockchip_dp_audio_codec_ops = { .get_eld = rockchip_dp_audio_get_eld, }; +static int rockchip_dp_match_by_id(struct device *dev, const void *data) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + const unsigned int *id = data; + + return dp->id == *id; +} + +static struct rockchip_dp_device * +rockchip_dp_find_by_id(struct device_driver *drv, unsigned int id) +{ + struct device *dev; + + dev = driver_find_device(drv, NULL, &id, rockchip_dp_match_by_id); + if (!dev) + return NULL; + + return dev_get_drvdata(dev); +} + static int rockchip_dp_pre_init(struct rockchip_dp_device *dp) { reset_control_assert(dp->rst); @@ -367,7 +389,13 @@ 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 |= dp->id ? VOP_OUTPUT_IF_eDP1 : VOP_OUTPUT_IF_eDP0; + if (dp->plat_data.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_eDP0 | VOP_OUTPUT_IF_eDP1; + } else { + s->output_if |= dp->id ? VOP_OUTPUT_IF_eDP1 : VOP_OUTPUT_IF_eDP0; + } s->output_bpc = di->bpc; s->bus_flags = di->bus_flags; s->tv_state = &conn_state->tv; @@ -445,13 +473,15 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, dp->drm_dev = drm_dev; - ret = rockchip_dp_drm_create_encoder(dp); - if (ret) { - DRM_ERROR("failed to create drm encoder\n"); - return ret; - } + if (!dp->plat_data.left) { + ret = rockchip_dp_drm_create_encoder(dp); + if (ret) { + DRM_ERROR("failed to create drm encoder\n"); + return ret; + } - dp->plat_data.encoder = &dp->encoder; + dp->plat_data.encoder = &dp->encoder; + } if (dp->data->audio) { struct hdmi_codec_pdata codec_data = { @@ -548,6 +578,8 @@ static int rockchip_dp_probe(struct platform_device *pdev) dp->plat_data.get_modes = rockchip_dp_get_modes; dp->plat_data.attach = rockchip_dp_bridge_attach; dp->plat_data.detach = rockchip_dp_bridge_detach; + dp->plat_data.convert_to_split_mode = drm_mode_convert_to_split_mode; + dp->plat_data.convert_to_origin_mode = drm_mode_convert_to_origin_mode; dp->plat_data.skip_connector = !!bridge; dp->bridge = bridge; @@ -561,6 +593,18 @@ static int rockchip_dp_probe(struct platform_device *pdev) if (IS_ERR(dp->adp)) return PTR_ERR(dp->adp); + if (dp->data->split_mode && device_property_read_bool(dev, "split-mode")) { + struct rockchip_dp_device *secondary = + rockchip_dp_find_by_id(dev->driver, !dp->id); + if (!secondary) + return -EPROBE_DEFER; + + dp->plat_data.right = secondary->adp; + dp->plat_data.split_mode = true; + secondary->plat_data.left = dp->adp; + secondary->plat_data.split_mode = true; + } + return component_add(dev, &rockchip_dp_component_ops); } @@ -634,6 +678,7 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = { .edp_mode = GRF_REG_FIELD(0x0000, 0, 0), .ssc = true, .audio = true, + .split_mode = true, }, { .chip_type = RK3588_EDP, @@ -642,6 +687,7 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = { .edp_mode = GRF_REG_FIELD(0x0004, 0, 0), .ssc = true, .audio = true, + .split_mode = true, }, { /* sentinel */ } }; diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 03c11cae0b3b..26b4246cbe6b 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -41,6 +41,10 @@ struct analogix_dp_plat_data { bool skip_connector; bool ssc; + bool split_mode; + struct analogix_dp_device *left; + struct analogix_dp_device *right; + int (*power_on_start)(struct analogix_dp_plat_data *); int (*power_on_end)(struct analogix_dp_plat_data *); int (*power_off)(struct analogix_dp_plat_data *); @@ -49,6 +53,8 @@ struct analogix_dp_plat_data { void (*detach)(struct analogix_dp_plat_data *, struct drm_bridge *); int (*get_modes)(struct analogix_dp_plat_data *, struct drm_connector *); + void (*convert_to_split_mode)(struct drm_display_mode *); + void (*convert_to_origin_mode)(struct drm_display_mode *); }; int analogix_dp_runtime_resume(struct analogix_dp_device *dp);