summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/bridge
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-04-02 17:59:23 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-02 17:59:23 +0300
commit320b164abb32db876866a4ff8c2cb710524ac6ea (patch)
tree1f79119cde6e24c9f1d01fb1e51252bca7c4cdd5 /drivers/gpu/drm/bridge
parent0adb32858b0bddf4ada5f364a84ed60b196dbcda (diff)
parent694f54f680f7fd8e9561928fbfc537d9afbc3d79 (diff)
downloadlinux-320b164abb32db876866a4ff8c2cb710524ac6ea.tar.xz
Merge tag 'drm-for-v4.17' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "Cannonlake and Vega12 support are probably the two major things. This pull lacks nouveau, Ben had some unforseen leave and a few other blockers so we'll see how things look or maybe leave it for this merge window. core: - Device links to handle sound/gpu pm dependency - Color encoding/range properties - Plane clipping into plane check helper - Backlight helpers - DP TP4 + HBR3 helper support amdgpu: - Vega12 support - Enable DC by default on all supported GPUs - Powerplay restructuring and cleanup - DC bandwidth calc updates - DC backlight on pre-DCE11 - TTM backing store dropping support - SR-IOV fixes - Adding "wattman" like functionality - DC crc support - Improved DC dual-link handling amdkfd: - GPUVM support for dGPU - KFD events for dGPU - Enable PCIe atomics for dGPUs - HSA process eviction support - Live-lock fixes for process eviction - VM page table allocation fix for large-bar systems panel: - Raydium RM68200 - AUO G104SN02 V2 - KEO TX31D200VM0BAA - ARM Versatile panels i915: - Cannonlake support enabled - AUX-F port support added - Icelake base enabling until internal milestone of forcewake support - Query uAPI interface (used for GPU topology information currently) - Compressed framebuffer support for sprites - kmem cache shrinking when GPU is idle - Avoid boosting GPU when waited item is being processed already - Avoid retraining LSPCON link unnecessarily - Decrease request signaling latency - Deprecation of I915_SET_COLORKEY_NONE - Kerneldoc and compiler warning cleanup for upcoming CI enforcements - Full range ycbcr toggling - HDCP support i915/gvt: - Big refactor for shadow ppgtt - KBL context save/restore via LRI cmd (Weinan) - Properly unmap dma for guest page (Changbin) vmwgfx: - Lots of various improvements etnaviv: - Use the drm gpu scheduler - prep work for GC7000L support vc4: - fix alpha blending - Expose perf counters to userspace pl111: - Bandwidth checking/limiting - Versatile panel support sun4i: - A83T HDMI support - A80 support - YUV plane support - H3/H5 HDMI support omapdrm: - HPD support for DVI connector - remove lots of static variables msm: - DSI updates from 10nm / SDM845 - fix for race condition with a3xx/a4xx fence completion irq - some refactoring/prep work for eventual a6xx support (ie. when we have a userspace) - a5xx debugfs enhancements - some mdp5 fixes/cleanups to prepare for eventually merging writeback - support (ie. when we have a userspace) tegra: - mmap() fixes for fbdev devices - Overlay plane for hw cursor fix - dma-buf cache maintenance support mali-dp: - YUV->RGB conversion support rockchip: - rk3399/chromebook fixes and improvements rcar-du: - LVDS support move to drm bridge - DT bindings for R8A77995 - Driver/DT support for R8A77970 tilcdc: - DRM panel support" * tag 'drm-for-v4.17' of git://people.freedesktop.org/~airlied/linux: (1646 commits) drm/i915: Fix hibernation with ACPI S0 target state drm/i915/execlists: Use a locked clear_bit() for synchronisation with interrupt drm/i915: Specify which engines to reset following semaphore/event lockups drm/i915/dp: Write to SET_POWER dpcd to enable MST hub. drm/amdkfd: Use ordered workqueue to restore processes drm/amdgpu: Fix acquiring VM on large-BAR systems drm/amd/pp: clean header file hwmgr.h drm/amd/pp: use mlck_table.count for array loop index limit drm: Fix uabi regression by allowing garbage mode->type from userspace drm/amdgpu: Add an ATPX quirk for hybrid laptop drm/amdgpu: fix spelling mistake: "asssert" -> "assert" drm/amd/pp: Add new asic support in pp_psm.c drm/amd/pp: Clean up powerplay code on Vega12 drm/amd/pp: Add smu irq handlers for legacy asics drm/amd/pp: Fix set wrong temperature range on smu7 drm/amdgpu: Don't change preferred domian when fallback GTT v5 drm/vmwgfx: Bump version patchlevel and date drm/vmwgfx: use monotonic event timestamps drm/vmwgfx: Unpin the screen object backup buffer when not used drm/vmwgfx: Stricter count of legacy surface device resources ...
Diffstat (limited to 'drivers/gpu/drm/bridge')
-rw-r--r--drivers/gpu/drm/bridge/Kconfig3
-rw-r--r--drivers/gpu/drm/bridge/analogix-anx78xx.c3
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.c252
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.h11
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c38
-rw-r--r--drivers/gpu/drm/bridge/dumb-vga-dac.c59
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c20
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c95
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c210
9 files changed, 431 insertions, 260 deletions
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3b99d5a06c16..3aa65bdecb0e 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -30,7 +30,8 @@ config DRM_DUMB_VGA_DAC
depends on OF
select DRM_KMS_HELPER
help
- Support for RGB to VGA DAC based bridges
+ Support for non-programmable RGB to VGA DAC bridges, such as ADI
+ ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs.
config DRM_LVDS_ENCODER
tristate "Transparent parallel to LVDS encoder support"
diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c
index ed12a7ddd64a..b49043866be6 100644
--- a/drivers/gpu/drm/bridge/analogix-anx78xx.c
+++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c
@@ -1301,8 +1301,7 @@ static void unregister_i2c_dummy_clients(struct anx78xx *anx78xx)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(anx78xx->i2c_dummy); i++)
- if (anx78xx->i2c_dummy[i])
- i2c_unregister_device(anx78xx->i2c_dummy[i]);
+ i2c_unregister_device(anx78xx->i2c_dummy[i]);
}
static const struct regmap_config anx78xx_regmap_config = {
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,
diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c
index de5e7dee7ad6..498d5948d1a8 100644
--- a/drivers/gpu/drm/bridge/dumb-vga-dac.c
+++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c
@@ -11,6 +11,7 @@
*/
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
@@ -204,6 +205,7 @@ static int dumb_vga_probe(struct platform_device *pdev)
vga->bridge.funcs = &dumb_vga_bridge_funcs;
vga->bridge.of_node = pdev->dev.of_node;
+ vga->bridge.timings = of_device_get_match_data(&pdev->dev);
drm_bridge_add(&vga->bridge);
@@ -222,10 +224,61 @@ static int dumb_vga_remove(struct platform_device *pdev)
return 0;
}
+/*
+ * We assume the ADV7123 DAC is the "default" for historical reasons
+ * Information taken from the ADV7123 datasheet, revision D.
+ * NOTE: the ADV7123EP seems to have other timings and need a new timings
+ * set if used.
+ */
+static const struct drm_bridge_timings default_dac_timings = {
+ /* Timing specifications, datasheet page 7 */
+ .sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+ .setup_time_ps = 500,
+ .hold_time_ps = 1500,
+};
+
+/*
+ * Information taken from the THS8134, THS8134A, THS8134B datasheet named
+ * "SLVS205D", dated May 1990, revised March 2000.
+ */
+static const struct drm_bridge_timings ti_ths8134_dac_timings = {
+ /* From timing diagram, datasheet page 9 */
+ .sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+ /* From datasheet, page 12 */
+ .setup_time_ps = 3000,
+ /* I guess this means latched input */
+ .hold_time_ps = 0,
+};
+
+/*
+ * Information taken from the THS8135 datasheet named "SLAS343B", dated
+ * May 2001, revised April 2013.
+ */
+static const struct drm_bridge_timings ti_ths8135_dac_timings = {
+ /* From timing diagram, datasheet page 14 */
+ .sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+ /* From datasheet, page 16 */
+ .setup_time_ps = 2000,
+ .hold_time_ps = 500,
+};
+
static const struct of_device_id dumb_vga_match[] = {
- { .compatible = "dumb-vga-dac" },
- { .compatible = "adi,adv7123" },
- { .compatible = "ti,ths8135" },
+ {
+ .compatible = "dumb-vga-dac",
+ .data = NULL,
+ },
+ {
+ .compatible = "adi,adv7123",
+ .data = &default_dac_timings,
+ },
+ {
+ .compatible = "ti,ths8135",
+ .data = &ti_ths8135_dac_timings,
+ },
+ {
+ .compatible = "ti,ths8134",
+ .data = &ti_ths8134_dac_timings,
+ },
{},
};
MODULE_DEVICE_TABLE(of, dumb_vga_match);
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index b1ab4ab09532..60373d7eb220 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -137,7 +137,9 @@ static int sii902x_get_modes(struct drm_connector *connector)
struct sii902x *sii902x = connector_to_sii902x(connector);
struct regmap *regmap = sii902x->regmap;
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ struct device *dev = &sii902x->i2c->dev;
unsigned long timeout;
+ unsigned int retries;
unsigned int status;
struct edid *edid;
int num = 0;
@@ -159,7 +161,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
time_before(jiffies, timeout));
if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
- dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus\n");
+ dev_err(dev, "failed to acquire the i2c bus\n");
return -ETIMEDOUT;
}
@@ -179,9 +181,19 @@ static int sii902x_get_modes(struct drm_connector *connector)
if (ret)
return ret;
- ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
+ /*
+ * Sometimes the I2C bus can stall after failure to use the
+ * EDID channel. Retry a few times to see if things clear
+ * up, else continue anyway.
+ */
+ retries = 5;
+ do {
+ ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA,
+ &status);
+ retries--;
+ } while (ret && retries);
if (ret)
- return ret;
+ dev_err(dev, "failed to read status (%d)\n", ret);
ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
SII902X_SYS_CTRL_DDC_BUS_REQ |
@@ -201,7 +213,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
- dev_err(&sii902x->i2c->dev, "failed to release the i2c bus\n");
+ dev_err(dev, "failed to release the i2c bus\n");
return -ETIMEDOUT;
}
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index a38db40ce990..ec8d0006ef7c 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -147,7 +147,6 @@ struct dw_hdmi {
int vic;
u8 edid[HDMI_EDID_LEN];
- bool cable_plugin;
struct {
const struct dw_hdmi_phy_ops *ops;
@@ -1037,19 +1036,21 @@ static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable)
HDMI_PHY_CONF0_SVSRET_MASK);
}
-static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
+void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
{
hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_pddq);
-static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
+void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
{
hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_txpwron);
static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
{
@@ -1065,6 +1066,22 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
HDMI_PHY_CONF0_SELDIPIF_MASK);
}
+void dw_hdmi_phy_reset(struct dw_hdmi *hdmi)
+{
+ /* PHY reset. The reset signal is active high on Gen2 PHYs. */
+ hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
+ hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_reset);
+
+void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address)
+{
+ hdmi_phy_test_clear(hdmi, 1);
+ hdmi_writeb(hdmi, address, HDMI_PHY_I2CM_SLAVE_ADDR);
+ hdmi_phy_test_clear(hdmi, 0);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_set_addr);
+
static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
{
const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
@@ -1203,16 +1220,11 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
if (phy->has_svsret)
dw_hdmi_phy_enable_svsret(hdmi, 1);
- /* PHY reset. The reset signal is active high on Gen2 PHYs. */
- hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
- hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
+ dw_hdmi_phy_reset(hdmi);
hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
- hdmi_phy_test_clear(hdmi, 1);
- hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
- HDMI_PHY_I2CM_SLAVE_ADDR);
- hdmi_phy_test_clear(hdmi, 0);
+ dw_hdmi_phy_i2c_set_addr(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
/* Write to the PHY as configured by the platform */
if (pdata->configure_phy)
@@ -1251,15 +1263,16 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
dw_hdmi_phy_power_off(hdmi);
}
-static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
- void *data)
+enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
+ void *data)
{
return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
connector_status_connected : connector_status_disconnected;
}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_read_hpd);
-static void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
- bool force, bool disabled, bool rxsense)
+void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
+ bool force, bool disabled, bool rxsense)
{
u8 old_mask = hdmi->phy_mask;
@@ -1271,8 +1284,9 @@ static void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
if (old_mask != hdmi->phy_mask)
hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_update_hpd);
-static void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
+void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
{
/*
* Configure the PHY RX SENSE and HPD interrupts polarities and clear
@@ -1291,6 +1305,7 @@ static void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
HDMI_IH_MUTE_PHY_STAT0);
}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd);
static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
.init = dw_hdmi_phy_init,
@@ -1634,9 +1649,12 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
* then write one of the FC registers several times.
*
* The number of iterations matters and depends on the HDMI TX revision
- * (and possibly on the platform). So far only i.MX6Q (v1.30a) and
- * i.MX6DL (v1.31a) have been identified as needing the workaround, with
- * 4 and 1 iterations respectively.
+ * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
+ * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
+ * as needing the workaround, with 4 iterations for v1.30a and 1
+ * iteration for others.
+ * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing
+ * the workaround with a single iteration.
*/
switch (hdmi->version) {
@@ -1644,6 +1662,8 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
count = 4;
break;
case 0x131a:
+ case 0x132a:
+ case 0x201a:
count = 1;
break;
default:
@@ -1658,12 +1678,6 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
}
-static void hdmi_enable_overflow_interrupts(struct dw_hdmi *hdmi)
-{
- hdmi_writeb(hdmi, 0, HDMI_FC_MASK2);
- hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
-}
-
static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
{
hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
@@ -1753,8 +1767,6 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi_tx_hdcp_config(hdmi);
dw_hdmi_clear_overflow(hdmi);
- if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
- hdmi_enable_overflow_interrupts(hdmi);
return 0;
}
@@ -2525,8 +2537,6 @@ __dw_hdmi_probe(struct platform_device *pdev,
if (hdmi->i2c)
dw_hdmi_i2c_init(hdmi);
- platform_set_drvdata(pdev, hdmi);
-
return hdmi;
err_iahb:
@@ -2576,25 +2586,23 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
/* -----------------------------------------------------------------------------
* Probe/remove API, used from platforms based on the DRM bridge API.
*/
-int dw_hdmi_probe(struct platform_device *pdev,
- const struct dw_hdmi_plat_data *plat_data)
+struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
+ const struct dw_hdmi_plat_data *plat_data)
{
struct dw_hdmi *hdmi;
hdmi = __dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi))
- return PTR_ERR(hdmi);
+ return hdmi;
drm_bridge_add(&hdmi->bridge);
- return 0;
+ return hdmi;
}
EXPORT_SYMBOL_GPL(dw_hdmi_probe);
-void dw_hdmi_remove(struct platform_device *pdev)
+void dw_hdmi_remove(struct dw_hdmi *hdmi)
{
- struct dw_hdmi *hdmi = platform_get_drvdata(pdev);
-
drm_bridge_remove(&hdmi->bridge);
__dw_hdmi_remove(hdmi);
@@ -2604,31 +2612,30 @@ EXPORT_SYMBOL_GPL(dw_hdmi_remove);
/* -----------------------------------------------------------------------------
* Bind/unbind API, used from platforms based on the component framework.
*/
-int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
- const struct dw_hdmi_plat_data *plat_data)
+struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
+ struct drm_encoder *encoder,
+ const struct dw_hdmi_plat_data *plat_data)
{
struct dw_hdmi *hdmi;
int ret;
hdmi = __dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi))
- return PTR_ERR(hdmi);
+ return hdmi;
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL);
if (ret) {
- dw_hdmi_remove(pdev);
+ dw_hdmi_remove(hdmi);
DRM_ERROR("Failed to initialize bridge with drm\n");
- return ret;
+ return ERR_PTR(ret);
}
- return 0;
+ return hdmi;
}
EXPORT_SYMBOL_GPL(dw_hdmi_bind);
-void dw_hdmi_unbind(struct device *dev)
+void dw_hdmi_unbind(struct dw_hdmi *hdmi)
{
- struct dw_hdmi *hdmi = dev_get_drvdata(dev);
-
__dw_hdmi_remove(hdmi);
}
EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index d9cca4fd66ec..226171a3ece1 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -29,7 +29,10 @@
#include <drm/bridge/dw_mipi_dsi.h>
#include <video/mipi_display.h>
+#define HWVER_131 0x31333100 /* IP version 1.31 */
+
#define DSI_VERSION 0x00
+#define VERSION GENMASK(31, 8)
#define DSI_PWR_UP 0x04
#define RESET 0
@@ -136,10 +139,6 @@
GEN_SW_0P_TX_LP)
#define DSI_GEN_HDR 0x6c
-/* TODO These 2 defines will be reworked thanks to mipi_dsi_create_packet() */
-#define GEN_HDATA(data) (((data) & 0xffff) << 8)
-#define GEN_HTYPE(type) (((type) & 0xff) << 0)
-
#define DSI_GEN_PLD_DATA 0x70
#define DSI_CMD_PKT_STATUS 0x74
@@ -169,11 +168,12 @@
#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
-/* TODO Next register is slightly different between 1.30 & 1.31 IP version */
#define DSI_PHY_TMR_CFG 0x9c
#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0xff) << 24)
#define PHY_LP2HS_TIME(lbcc) (((lbcc) & 0xff) << 16)
#define MAX_RD_TIME(lbcc) ((lbcc) & 0x7fff)
+#define PHY_HS2LP_TIME_V131(lbcc) (((lbcc) & 0x3ff) << 16)
+#define PHY_LP2HS_TIME_V131(lbcc) ((lbcc) & 0x3ff)
#define DSI_PHY_RSTZ 0xa0
#define PHY_DISFORCEPLL 0
@@ -212,7 +212,9 @@
#define DSI_INT_ST1 0xc0
#define DSI_INT_MSK0 0xc4
#define DSI_INT_MSK1 0xc8
+
#define DSI_PHY_TMR_RD_CFG 0xf4
+#define MAX_RD_TIME_V131(lbcc) ((lbcc) & 0x7fff)
#define PHY_STATUS_TIMEOUT_US 10000
#define CMD_PKT_STATUS_TIMEOUT_US 20000
@@ -340,7 +342,7 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_CMD_FULL), 1000,
CMD_PKT_STATUS_TIMEOUT_US);
- if (ret < 0) {
+ if (ret) {
dev_err(dsi->dev, "failed to get available command FIFO\n");
return ret;
}
@@ -351,7 +353,7 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, (val & mask) == mask,
1000, CMD_PKT_STATUS_TIMEOUT_US);
- if (ret < 0) {
+ if (ret) {
dev_err(dsi->dev, "failed to write command FIFO\n");
return ret;
}
@@ -359,52 +361,23 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
return 0;
}
-static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
- const struct mipi_dsi_msg *msg)
+static int dw_mipi_dsi_write(struct dw_mipi_dsi *dsi,
+ const struct mipi_dsi_packet *packet)
{
- const u8 *tx_buf = msg->tx_buf;
- u16 data = 0;
+ const u8 *tx_buf = packet->payload;
+ int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
+ __le32 word;
u32 val;
- if (msg->tx_len > 0)
- data |= tx_buf[0];
- if (msg->tx_len > 1)
- data |= tx_buf[1] << 8;
-
- if (msg->tx_len > 2) {
- dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
- msg->tx_len);
- return -EINVAL;
- }
-
- val = GEN_HDATA(data) | GEN_HTYPE(msg->type);
- return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
-}
-
-static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
- const struct mipi_dsi_msg *msg)
-{
- const u8 *tx_buf = msg->tx_buf;
- int len = msg->tx_len, pld_data_bytes = sizeof(u32), ret;
- u32 hdr_val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
- u32 remainder;
- u32 val;
-
- if (msg->tx_len < 3) {
- dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
- msg->tx_len);
- return -EINVAL;
- }
-
- while (DIV_ROUND_UP(len, pld_data_bytes)) {
+ while (len) {
if (len < pld_data_bytes) {
- remainder = 0;
- memcpy(&remainder, tx_buf, len);
- dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
+ word = 0;
+ memcpy(&word, tx_buf, len);
+ dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
len = 0;
} else {
- memcpy(&remainder, tx_buf, pld_data_bytes);
- dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
+ memcpy(&word, tx_buf, pld_data_bytes);
+ dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
tx_buf += pld_data_bytes;
len -= pld_data_bytes;
}
@@ -412,47 +385,81 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_PLD_W_FULL), 1000,
CMD_PKT_STATUS_TIMEOUT_US);
- if (ret < 0) {
+ if (ret) {
dev_err(dsi->dev,
"failed to get available write payload FIFO\n");
return ret;
}
}
- return dw_mipi_dsi_gen_pkt_hdr_write(dsi, hdr_val);
+ word = 0;
+ memcpy(&word, packet->header, sizeof(packet->header));
+ return dw_mipi_dsi_gen_pkt_hdr_write(dsi, le32_to_cpu(word));
+}
+
+static int dw_mipi_dsi_read(struct dw_mipi_dsi *dsi,
+ const struct mipi_dsi_msg *msg)
+{
+ int i, j, ret, len = msg->rx_len;
+ u8 *buf = msg->rx_buf;
+ u32 val;
+
+ /* Wait end of the read operation */
+ ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_RD_CMD_BUSY),
+ 1000, CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret) {
+ dev_err(dsi->dev, "Timeout during read operation\n");
+ return ret;
+ }
+
+ for (i = 0; i < len; i += 4) {
+ /* Read fifo must not be empty before all bytes are read */
+ ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_PLD_R_EMPTY),
+ 1000, CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret) {
+ dev_err(dsi->dev, "Read payload FIFO is empty\n");
+ return ret;
+ }
+
+ val = dsi_read(dsi, DSI_GEN_PLD_DATA);
+ for (j = 0; j < 4 && j + i < len; j++)
+ buf[i + j] = val >> (8 * j);
+ }
+
+ return ret;
}
static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
- int ret;
+ struct mipi_dsi_packet packet;
+ int ret, nb_bytes;
+
+ ret = mipi_dsi_create_packet(&packet, msg);
+ if (ret) {
+ dev_err(dsi->dev, "failed to create packet: %d\n", ret);
+ return ret;
+ }
- /*
- * TODO dw drv improvements
- * use mipi_dsi_create_packet() instead of all following
- * functions and code (no switch cases, no
- * dw_mipi_dsi_dcs_short_write(), only the loop in long_write...)
- * and use packet.header...
- */
dw_mipi_message_config(dsi, msg);
- switch (msg->type) {
- case MIPI_DSI_DCS_SHORT_WRITE:
- case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
- case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
- ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
- break;
- case MIPI_DSI_DCS_LONG_WRITE:
- ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
- break;
- default:
- dev_err(dsi->dev, "unsupported message type 0x%02x\n",
- msg->type);
- ret = -EINVAL;
+ ret = dw_mipi_dsi_write(dsi, &packet);
+ if (ret)
+ return ret;
+
+ if (msg->rx_buf && msg->rx_len) {
+ ret = dw_mipi_dsi_read(dsi, msg);
+ if (ret)
+ return ret;
+ nb_bytes = msg->rx_len;
+ } else {
+ nb_bytes = packet.size;
}
- return ret;
+ return nb_bytes;
}
static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
@@ -658,6 +665,8 @@ static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi,
static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
{
+ u32 hw_version;
+
/*
* TODO dw drv improvements
* data & clock lane timers should be computed according to panel
@@ -665,8 +674,17 @@ static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
* note: DSI_PHY_TMR_CFG.MAX_RD_TIME should be in line with
* DSI_CMD_MODE_CFG.MAX_RD_PKT_SIZE_LP (see CMD_MODE_ALL_LP)
*/
- dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
- | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
+
+ hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
+
+ if (hw_version >= HWVER_131) {
+ dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME_V131(0x40) |
+ PHY_LP2HS_TIME_V131(0x40));
+ dsi_write(dsi, DSI_PHY_TMR_RD_CFG, MAX_RD_TIME_V131(10000));
+ } else {
+ dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40) |
+ PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
+ }
dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
| PHY_CLKLP2HS_TIME(0x40));
@@ -703,13 +721,13 @@ static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
- if (ret < 0)
+ if (ret)
DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
val, val & PHY_STOP_STATE_CLK_LANE, 1000,
PHY_STATUS_TIMEOUT_US);
- if (ret < 0)
+ if (ret)
DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
}
@@ -746,9 +764,9 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
pm_runtime_put(dsi->dev);
}
-void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
@@ -922,8 +940,6 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
dsi->bridge.of_node = pdev->dev.of_node;
#endif
- dev_set_drvdata(dev, dsi);
-
return dsi;
}
@@ -935,23 +951,16 @@ static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
/*
* Probe/remove API, used from platforms based on the DRM bridge API.
*/
-int dw_mipi_dsi_probe(struct platform_device *pdev,
- const struct dw_mipi_dsi_plat_data *plat_data)
+struct dw_mipi_dsi *
+dw_mipi_dsi_probe(struct platform_device *pdev,
+ const struct dw_mipi_dsi_plat_data *plat_data)
{
- struct dw_mipi_dsi *dsi;
-
- dsi = __dw_mipi_dsi_probe(pdev, plat_data);
- if (IS_ERR(dsi))
- return PTR_ERR(dsi);
-
- return 0;
+ return __dw_mipi_dsi_probe(pdev, plat_data);
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
-void dw_mipi_dsi_remove(struct platform_device *pdev)
+void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
{
- struct dw_mipi_dsi *dsi = platform_get_drvdata(pdev);
-
mipi_dsi_host_unregister(&dsi->dsi_host);
__dw_mipi_dsi_remove(dsi);
@@ -961,31 +970,30 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
/*
* Bind/unbind API, used from platforms based on the component framework.
*/
-int dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
- const struct dw_mipi_dsi_plat_data *plat_data)
+struct dw_mipi_dsi *
+dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
+ const struct dw_mipi_dsi_plat_data *plat_data)
{
struct dw_mipi_dsi *dsi;
int ret;
dsi = __dw_mipi_dsi_probe(pdev, plat_data);
if (IS_ERR(dsi))
- return PTR_ERR(dsi);
+ return dsi;
ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
if (ret) {
- dw_mipi_dsi_remove(pdev);
+ dw_mipi_dsi_remove(dsi);
DRM_ERROR("Failed to initialize bridge with drm\n");
- return ret;
+ return ERR_PTR(ret);
}
- return 0;
+ return dsi;
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
-void dw_mipi_dsi_unbind(struct device *dev)
+void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
{
- struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
-
__dw_mipi_dsi_remove(dsi);
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);