diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/analogix')
-rw-r--r-- | drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 252 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 38 |
3 files changed, 196 insertions, 105 deletions
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index a8905049b9da..5c52307146c7 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_gpio.h> @@ -35,6 +36,8 @@ #define to_dp(nm) container_of(nm, struct analogix_dp_device, nm) +static const bool verify_fast_training; + struct bridge_init { struct i2c_client *client; struct device_node *node; @@ -98,20 +101,18 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp) return 0; } -int analogix_dp_psr_supported(struct device *dev) +int analogix_dp_psr_enabled(struct analogix_dp_device *dp) { - struct analogix_dp_device *dp = dev_get_drvdata(dev); - return dp->psr_support; + return dp->psr_enable; } -EXPORT_SYMBOL_GPL(analogix_dp_psr_supported); +EXPORT_SYMBOL_GPL(analogix_dp_psr_enabled); -int analogix_dp_enable_psr(struct device *dev) +int analogix_dp_enable_psr(struct analogix_dp_device *dp) { - struct analogix_dp_device *dp = dev_get_drvdata(dev); struct edp_vsc_psr psr_vsc; - if (!dp->psr_support) + if (!dp->psr_enable) return 0; /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ @@ -124,18 +125,16 @@ int analogix_dp_enable_psr(struct device *dev) psr_vsc.DB0 = 0; psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID; - analogix_dp_send_psr_spd(dp, &psr_vsc); - return 0; + return analogix_dp_send_psr_spd(dp, &psr_vsc, true); } EXPORT_SYMBOL_GPL(analogix_dp_enable_psr); -int analogix_dp_disable_psr(struct device *dev) +int analogix_dp_disable_psr(struct analogix_dp_device *dp) { - struct analogix_dp_device *dp = dev_get_drvdata(dev); struct edp_vsc_psr psr_vsc; int ret; - if (!dp->psr_support) + if (!dp->psr_enable) return 0; /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ @@ -152,8 +151,7 @@ int analogix_dp_disable_psr(struct device *dev) if (ret != 1) dev_err(dp->dev, "Failed to set DP Power0 %d\n", ret); - analogix_dp_send_psr_spd(dp, &psr_vsc); - return 0; + return analogix_dp_send_psr_spd(dp, &psr_vsc, false); } EXPORT_SYMBOL_GPL(analogix_dp_disable_psr); @@ -533,7 +531,7 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) { int lane, lane_count, retval; u32 reg; - u8 link_align, link_status[2], adjust_request[2]; + u8 link_align, link_status[2], adjust_request[2], spread; usleep_range(400, 401); @@ -576,6 +574,20 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) dev_dbg(dp->dev, "final lane count = %.2x\n", dp->link_train.lane_count); + retval = drm_dp_dpcd_readb(&dp->aux, DP_MAX_DOWNSPREAD, + &spread); + if (retval != 1) { + dev_err(dp->dev, "failed to read downspread %d\n", + retval); + dp->fast_train_support = false; + } else { + dp->fast_train_support = + (spread & DP_NO_AUX_HANDSHAKE_LINK_TRAINING) ? + true : false; + } + dev_dbg(dp->dev, "fast link training %s\n", + dp->fast_train_support ? "supported" : "unsupported"); + /* set enhanced mode if available */ analogix_dp_set_enhanced_mode(dp); dp->link_train.lt_state = FINISHED; @@ -632,10 +644,12 @@ static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp, *lane_count = DPCD_MAX_LANE_COUNT(data); } -static void analogix_dp_init_training(struct analogix_dp_device *dp, - enum link_lane_count_type max_lane, - int max_rate) +static int analogix_dp_full_link_train(struct analogix_dp_device *dp, + u32 max_lanes, u32 max_rate) { + int retval = 0; + bool training_finished = false; + /* * MACRO_RST must be applied after the PLL_LOCK to avoid * the DP inter pair skew issue for at least 10 us @@ -661,18 +675,13 @@ static void analogix_dp_init_training(struct analogix_dp_device *dp, } /* Setup TX lane count & rate */ - if (dp->link_train.lane_count > max_lane) - dp->link_train.lane_count = max_lane; + if (dp->link_train.lane_count > max_lanes) + dp->link_train.lane_count = max_lanes; if (dp->link_train.link_rate > max_rate) dp->link_train.link_rate = max_rate; /* All DP analog module power up */ analogix_dp_set_analog_power_down(dp, POWER_ALL, 0); -} - -static int analogix_dp_sw_link_training(struct analogix_dp_device *dp) -{ - int retval = 0, training_finished = 0; dp->link_train.lt_state = START; @@ -707,27 +716,92 @@ static int analogix_dp_sw_link_training(struct analogix_dp_device *dp) return retval; } -static int analogix_dp_set_link_train(struct analogix_dp_device *dp, - u32 count, u32 bwtype) +static int analogix_dp_fast_link_train(struct analogix_dp_device *dp) { - int i; - int retval; + int i, ret; + u8 link_align, link_status[2]; + enum pll_status status; - for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) { - analogix_dp_init_training(dp, count, bwtype); - retval = analogix_dp_sw_link_training(dp); - if (retval == 0) - break; + analogix_dp_reset_macro(dp); + + analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate); + analogix_dp_set_lane_count(dp, dp->link_train.lane_count); - usleep_range(100, 110); + for (i = 0; i < dp->link_train.lane_count; i++) { + analogix_dp_set_lane_link_training(dp, + dp->link_train.training_lane[i], i); } - return retval; + ret = readx_poll_timeout(analogix_dp_get_pll_lock_status, dp, status, + status != PLL_UNLOCKED, 120, + 120 * DP_TIMEOUT_LOOP_COUNT); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Wait for pll lock failed %d\n", ret); + return ret; + } + + /* source Set training pattern 1 */ + analogix_dp_set_training_pattern(dp, TRAINING_PTN1); + /* From DP spec, pattern must be on-screen for a minimum 500us */ + usleep_range(500, 600); + + analogix_dp_set_training_pattern(dp, TRAINING_PTN2); + /* From DP spec, pattern must be on-screen for a minimum 500us */ + usleep_range(500, 600); + + /* TODO: enhanced_mode?*/ + analogix_dp_set_training_pattern(dp, DP_NONE); + + /* + * Useful for debugging issues with fast link training, disable for more + * speed + */ + if (verify_fast_training) { + ret = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED, + &link_align); + if (ret < 0) { + DRM_DEV_ERROR(dp->dev, "Read align status failed %d\n", + ret); + return ret; + } + + ret = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, + 2); + if (ret < 0) { + DRM_DEV_ERROR(dp->dev, "Read link status failed %d\n", + ret); + return ret; + } + + if (analogix_dp_clock_recovery_ok(link_status, + dp->link_train.lane_count)) { + DRM_DEV_ERROR(dp->dev, "Clock recovery failed\n"); + analogix_dp_reduce_link_rate(dp); + return -EIO; + } + + if (analogix_dp_channel_eq_ok(link_status, link_align, + dp->link_train.lane_count)) { + DRM_DEV_ERROR(dp->dev, "Channel EQ failed\n"); + analogix_dp_reduce_link_rate(dp); + return -EIO; + } + } + + return 0; +} + +static int analogix_dp_train_link(struct analogix_dp_device *dp) +{ + if (dp->fast_train_support) + return analogix_dp_fast_link_train(dp); + + return analogix_dp_full_link_train(dp, dp->video_info.max_lane_count, + dp->video_info.max_link_rate); } static int analogix_dp_config_video(struct analogix_dp_device *dp) { - int retval = 0; int timeout_loop = 0; int done_count = 0; @@ -783,10 +857,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp) usleep_range(1000, 1001); } - if (retval != 0) - dev_err(dp->dev, "Video stream is not detected!\n"); - - return retval; + return 0; } static void analogix_dp_enable_scramble(struct analogix_dp_device *dp, @@ -855,10 +926,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp) DRM_ERROR("failed to disable the panel\n"); } - ret = analogix_dp_set_link_train(dp, dp->video_info.max_lane_count, - dp->video_info.max_link_rate); + ret = readx_poll_timeout(analogix_dp_train_link, dp, ret, !ret, 100, + DP_TIMEOUT_TRAINING_US * 5); if (ret) { - dev_err(dp->dev, "unable to do link train\n"); + dev_err(dp->dev, "unable to do link train, ret=%d\n", ret); return; } @@ -880,8 +951,8 @@ static void analogix_dp_commit(struct analogix_dp_device *dp) /* Enable video */ analogix_dp_start_video(dp); - dp->psr_support = analogix_dp_detect_sink_psr(dp); - if (dp->psr_support) + dp->psr_enable = analogix_dp_detect_sink_psr(dp); + if (dp->psr_enable) analogix_dp_enable_sink_psr(dp); } @@ -1019,27 +1090,30 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge) { struct analogix_dp_device *dp = bridge->driver_private; struct drm_encoder *encoder = dp->encoder; - struct drm_connector *connector = &dp->connector; - int ret; + struct drm_connector *connector = NULL; + int ret = 0; if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; } - connector->polled = DRM_CONNECTOR_POLL_HPD; + if (!dp->plat_data->skip_connector) { + connector = &dp->connector; + connector->polled = DRM_CONNECTOR_POLL_HPD; - ret = drm_connector_init(dp->drm_dev, connector, - &analogix_dp_connector_funcs, - DRM_MODE_CONNECTOR_eDP); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; - } + ret = drm_connector_init(dp->drm_dev, connector, + &analogix_dp_connector_funcs, + DRM_MODE_CONNECTOR_eDP); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } - drm_connector_helper_add(connector, - &analogix_dp_connector_helper_funcs); - drm_mode_connector_attach_encoder(connector, encoder); + drm_connector_helper_add(connector, + &analogix_dp_connector_helper_funcs); + drm_mode_connector_attach_encoder(connector, encoder); + } /* * NOTE: the connector registration is implemented in analogix @@ -1123,6 +1197,7 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) if (ret) DRM_ERROR("failed to setup the panel ret = %d\n", ret); + dp->psr_enable = false; dp->dpms_mode = DRM_MODE_DPMS_OFF; } @@ -1283,8 +1358,9 @@ static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux, return analogix_dp_transfer(dp, msg); } -int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, - struct analogix_dp_plat_data *plat_data) +struct analogix_dp_device * +analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, + struct analogix_dp_plat_data *plat_data) { struct platform_device *pdev = to_platform_device(dev); struct analogix_dp_device *dp; @@ -1294,14 +1370,12 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, if (!plat_data) { dev_err(dev, "Invalided input plat_data\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } dp = devm_kzalloc(dev, sizeof(struct analogix_dp_device), GFP_KERNEL); if (!dp) - return -ENOMEM; - - dev_set_drvdata(dev, dp); + return ERR_PTR(-ENOMEM); dp->dev = &pdev->dev; dp->dpms_mode = DRM_MODE_DPMS_OFF; @@ -1318,7 +1392,7 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, ret = analogix_dp_dt_parse_pdata(dp); if (ret) - return ret; + return ERR_PTR(ret); dp->phy = devm_phy_get(dp->dev, "dp"); if (IS_ERR(dp->phy)) { @@ -1332,14 +1406,14 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, if (ret == -ENOSYS || ret == -ENODEV) dp->phy = NULL; else - return ret; + return ERR_PTR(ret); } } dp->clock = devm_clk_get(&pdev->dev, "dp"); if (IS_ERR(dp->clock)) { dev_err(&pdev->dev, "failed to get clock\n"); - return PTR_ERR(dp->clock); + return ERR_CAST(dp->clock); } clk_prepare_enable(dp->clock); @@ -1348,7 +1422,7 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, dp->reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(dp->reg_base)) - return PTR_ERR(dp->reg_base); + return ERR_CAST(dp->reg_base); dp->force_hpd = of_property_read_bool(dev->of_node, "force-hpd"); @@ -1369,7 +1443,7 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, "hpd_gpio"); if (ret) { dev_err(&pdev->dev, "failed to get hpd gpio\n"); - return ret; + return ERR_PTR(ret); } dp->irq = gpio_to_irq(dp->hpd_gpio); irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; @@ -1381,16 +1455,9 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, if (dp->irq == -ENXIO) { dev_err(&pdev->dev, "failed to get irq\n"); - return -ENODEV; + return ERR_PTR(-ENODEV); } - pm_runtime_enable(dev); - - pm_runtime_get_sync(dev); - phy_power_on(dp->phy); - - analogix_dp_init_dp(dp); - ret = devm_request_threaded_irq(&pdev->dev, dp->irq, analogix_dp_hardirq, analogix_dp_irq_thread, @@ -1410,38 +1477,30 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, ret = drm_dp_aux_register(&dp->aux); if (ret) - goto err_disable_pm_runtime; + return ERR_PTR(ret); + + pm_runtime_enable(dev); ret = analogix_dp_create_bridge(drm_dev, dp); if (ret) { DRM_ERROR("failed to create bridge (%d)\n", ret); - drm_encoder_cleanup(dp->encoder); goto err_disable_pm_runtime; } - phy_power_off(dp->phy); - pm_runtime_put(dev); - - return 0; + return dp; err_disable_pm_runtime: - phy_power_off(dp->phy); - pm_runtime_put(dev); pm_runtime_disable(dev); - return ret; + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(analogix_dp_bind); -void analogix_dp_unbind(struct device *dev, struct device *master, - void *data) +void analogix_dp_unbind(struct analogix_dp_device *dp) { - struct analogix_dp_device *dp = dev_get_drvdata(dev); - analogix_dp_bridge_disable(dp->bridge); dp->connector.funcs->destroy(&dp->connector); - dp->encoder->funcs->destroy(dp->encoder); if (dp->plat_data->panel) { if (drm_panel_unprepare(dp->plat_data->panel)) @@ -1451,16 +1510,14 @@ void analogix_dp_unbind(struct device *dev, struct device *master, } drm_dp_aux_unregister(&dp->aux); - pm_runtime_disable(dev); + pm_runtime_disable(dp->dev); clk_disable_unprepare(dp->clock); } EXPORT_SYMBOL_GPL(analogix_dp_unbind); #ifdef CONFIG_PM -int analogix_dp_suspend(struct device *dev) +int analogix_dp_suspend(struct analogix_dp_device *dp) { - struct analogix_dp_device *dp = dev_get_drvdata(dev); - clk_disable_unprepare(dp->clock); if (dp->plat_data->panel) { @@ -1472,9 +1529,8 @@ int analogix_dp_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(analogix_dp_suspend); -int analogix_dp_resume(struct device *dev) +int analogix_dp_resume(struct analogix_dp_device *dp) { - struct analogix_dp_device *dp = dev_get_drvdata(dev); int ret; ret = clk_prepare_enable(dp->clock); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 5c6a28806129..6a96ef7e6934 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -20,6 +20,10 @@ #define MAX_CR_LOOP 5 #define MAX_EQ_LOOP 5 +/* Training takes 22ms if AUX channel comm fails. Use this as retry interval */ +#define DP_TIMEOUT_TRAINING_US 22000 +#define DP_TIMEOUT_PSR_LOOP_MS 300 + /* DP_MAX_LANE_COUNT */ #define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) #define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) @@ -168,7 +172,8 @@ struct analogix_dp_device { int dpms_mode; int hpd_gpio; bool force_hpd; - bool psr_support; + bool psr_enable; + bool fast_train_support; struct mutex panel_lock; bool panel_is_modeset; @@ -247,8 +252,8 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp); void analogix_dp_enable_scrambling(struct analogix_dp_device *dp); void analogix_dp_disable_scrambling(struct analogix_dp_device *dp); void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp); -void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, - struct edp_vsc_psr *vsc); +int analogix_dp_send_psr_spd(struct analogix_dp_device *dp, + struct edp_vsc_psr *vsc, bool blocking); ssize_t analogix_dp_transfer(struct analogix_dp_device *dp, struct drm_dp_aux_msg *msg); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 303083ad28e3..9df2f3ef000c 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -10,10 +10,11 @@ * option) any later version. */ -#include <linux/device.h> -#include <linux/io.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/gpio.h> +#include <linux/io.h> +#include <linux/iopoll.h> #include <drm/bridge/analogix_dp.h> @@ -992,10 +993,25 @@ void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp) writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON); } -void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, - struct edp_vsc_psr *vsc) +static ssize_t analogix_dp_get_psr_status(struct analogix_dp_device *dp) +{ + ssize_t val; + u8 status; + + val = drm_dp_dpcd_readb(&dp->aux, DP_PSR_STATUS, &status); + if (val < 0) { + dev_err(dp->dev, "PSR_STATUS read failed ret=%zd", val); + return val; + } + return status; +} + +int analogix_dp_send_psr_spd(struct analogix_dp_device *dp, + struct edp_vsc_psr *vsc, bool blocking) { unsigned int val; + int ret; + ssize_t psr_status; /* don't send info frame */ val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); @@ -1036,6 +1052,20 @@ void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); val |= IF_EN; writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + + if (!blocking) + return 0; + + ret = readx_poll_timeout(analogix_dp_get_psr_status, dp, psr_status, + psr_status >= 0 && + ((vsc->DB1 && psr_status == DP_PSR_SINK_ACTIVE_RFB) || + (!vsc->DB1 && psr_status == DP_PSR_SINK_INACTIVE)), 1500, + DP_TIMEOUT_PSR_LOOP_MS * 1000); + if (ret) { + dev_warn(dp->dev, "Failed to apply PSR %d\n", ret); + return ret; + } + return 0; } ssize_t analogix_dp_transfer(struct analogix_dp_device *dp, |