drm/bridge: dumb-vga-dac: Rename driver to simple-bridge
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Wed, 26 Feb 2020 11:24:31 +0000 (13:24 +0200)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Wed, 26 Feb 2020 11:31:31 +0000 (13:31 +0200)
The dumb-vga-dac driver can support simple DRM bridges without being
limited to VGA DACs. Rename it to simple-bridge.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Tested-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-12-laurent.pinchart@ideasonboard.com
arch/arm/configs/davinci_all_defconfig
arch/arm/configs/integrator_defconfig
arch/arm/configs/multi_v7_defconfig
arch/arm/configs/shmobile_defconfig
arch/arm/configs/sunxi_defconfig
arch/arm/configs/versatile_defconfig
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/bridge/Makefile
drivers/gpu/drm/bridge/dumb-vga-dac.c [deleted file]
drivers/gpu/drm/bridge/simple-bridge.c [new file with mode: 0644]

index b5ba8d731a25e82964267cb43d159ec314a15f59..e849367c05662c99b6618cc6468df61759e01546 100644 (file)
@@ -158,7 +158,7 @@ CONFIG_VIDEO_TVP514X=m
 CONFIG_VIDEO_ADV7343=m
 CONFIG_DRM=m
 CONFIG_DRM_TILCDC=m
-CONFIG_DRM_DUMB_VGA_DAC=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
 CONFIG_DRM_TINYDRM=m
 CONFIG_TINYDRM_ST7586=m
 CONFIG_FB=y
index 2f0a762dc3a0e3c3d8eeb3ee58695c93135d1de1..a9755c501bec405669b8213f820d58009980a2ac 100644 (file)
@@ -55,7 +55,7 @@ CONFIG_SMC91X=y
 # CONFIG_KEYBOARD_ATKBD is not set
 # CONFIG_SERIO_SERPORT is not set
 CONFIG_DRM=y
-CONFIG_DRM_DUMB_VGA_DAC=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_DRM_PL111=y
 CONFIG_FB_MODE_HELPERS=y
 CONFIG_FB_MATROX=y
index 017d65f86eba22da64d17806420bc801b78dd02e..0b020863abdb645c43fa698c1fe75b0f19d35763 100644 (file)
@@ -670,11 +670,11 @@ CONFIG_DRM_PANEL_ORISETECH_OTM8009A=m
 CONFIG_DRM_PANEL_RAYDIUM_RM68200=m
 CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=m
 CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=m
-CONFIG_DRM_DUMB_VGA_DAC=m
 CONFIG_DRM_NXP_PTN3460=m
 CONFIG_DRM_PARADE_PS8622=m
 CONFIG_DRM_SII902X=m
 CONFIG_DRM_SII9234=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
 CONFIG_DRM_TOSHIBA_TC358764=m
 CONFIG_DRM_I2C_ADV7511=m
 CONFIG_DRM_I2C_ADV7511_AUDIO=y
index 64fa849f8bbe06179372ad684d1f86c4905f9e3d..838307a9bb922c3660f48063378b7cac6732a578 100644 (file)
@@ -125,9 +125,9 @@ CONFIG_VIDEO_ML86V7667=y
 CONFIG_DRM=y
 CONFIG_DRM_RCAR_DU=y
 CONFIG_DRM_PANEL_SIMPLE=y
-CONFIG_DRM_DUMB_VGA_DAC=y
 CONFIG_DRM_LVDS_CODEC=y
 CONFIG_DRM_SII902X=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_DRM_I2C_ADV7511=y
 CONFIG_DRM_I2C_ADV7511_AUDIO=y
 CONFIG_FB_SH_MOBILE_LCDC=y
index e9fb57374b9f3ef521a45caf4a8e93d994b6f701..61b8be19e52783b9afcb88be8ce74070eef84c73 100644 (file)
@@ -101,7 +101,7 @@ CONFIG_RC_DEVICES=y
 CONFIG_IR_SUNXI=y
 CONFIG_DRM=y
 CONFIG_DRM_SUN4I=y
-CONFIG_DRM_DUMB_VGA_DAC=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_FB_SIMPLE=y
 CONFIG_SOUND=y
 CONFIG_SND=y
index fe4d4b5965859edb784b5d482ad8549ddb6180d1..767935337413403c909b8ed74e4d4ed981328527 100644 (file)
@@ -59,7 +59,7 @@ CONFIG_GPIO_PL061=y
 CONFIG_DRM=y
 CONFIG_DRM_PANEL_ARM_VERSATILE=y
 CONFIG_DRM_PANEL_SIMPLE=y
-CONFIG_DRM_DUMB_VGA_DAC=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_DRM_PL111=y
 CONFIG_FB_MODE_HELPERS=y
 CONFIG_BACKLIGHT_CLASS_DEVICE=y
index 20a439199cb833d2838b41cf96e1ce9fc3e3ca1b..10073ad882839ad518e63268ac9082da9a7c9378 100644 (file)
@@ -27,14 +27,6 @@ config DRM_CDNS_DSI
          Support Cadence DPI to DSI bridge. This is an internal
          bridge and is meant to be directly embedded in a SoC.
 
-config DRM_DUMB_VGA_DAC
-       tristate "Dumb VGA DAC Bridge support"
-       depends on OF
-       select DRM_KMS_HELPER
-       help
-         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_CODEC
        tristate "Transparent LVDS encoders and decoders support"
        depends on OF
@@ -110,6 +102,14 @@ config DRM_SII9234
          It is an I2C driver, that detects connection of MHL bridge
          and starts encapsulation of HDMI signal.
 
+config DRM_SIMPLE_BRIDGE
+       tristate "Simple DRM bridge support"
+       depends on OF
+       select DRM_KMS_HELPER
+       help
+         Support for non-programmable DRM bridges, such as ADI ADV7123, TI
+         THS8134 and THS8135 or passive resistor ladder DACs.
+
 config DRM_THINE_THC63LVD1024
        tristate "Thine THC63LVD1024 LVDS decoder bridge"
        depends on OF
index b0d5c3af0b5ab5dbda9c046e4f85dea6f63d79c6..b6b2e7029a78578cbc79f80043e5fad103205cb0 100644 (file)
@@ -1,6 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
-obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
 obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
 obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
@@ -9,6 +8,7 @@ obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
 obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
 obj-$(CONFIG_DRM_SII9234) += sii9234.o
+obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o
 obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
 obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o
 obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c
deleted file mode 100644 (file)
index 7287be2..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2015-2016 Free Electrons
- * Copyright (C) 2015-2016 NextThing Co
- *
- * Maxime Ripard <maxime.ripard@free-electrons.com>
- */
-
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_graph.h>
-#include <linux/regulator/consumer.h>
-
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_print.h>
-#include <drm/drm_probe_helper.h>
-
-struct simple_bridge {
-       struct drm_bridge       bridge;
-       struct drm_connector    connector;
-
-       struct i2c_adapter      *ddc;
-       struct regulator        *vdd;
-};
-
-static inline struct simple_bridge *
-drm_bridge_to_simple_bridge(struct drm_bridge *bridge)
-{
-       return container_of(bridge, struct simple_bridge, bridge);
-}
-
-static inline struct simple_bridge *
-drm_connector_to_simple_bridge(struct drm_connector *connector)
-{
-       return container_of(connector, struct simple_bridge, connector);
-}
-
-static int simple_bridge_get_modes(struct drm_connector *connector)
-{
-       struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
-       struct edid *edid;
-       int ret;
-
-       if (!sbridge->ddc)
-               goto fallback;
-
-       edid = drm_get_edid(connector, sbridge->ddc);
-       if (!edid) {
-               DRM_INFO("EDID readout failed, falling back to standard modes\n");
-               goto fallback;
-       }
-
-       drm_connector_update_edid_property(connector, edid);
-       ret = drm_add_edid_modes(connector, edid);
-       kfree(edid);
-       return ret;
-
-fallback:
-       /*
-        * In case we cannot retrieve the EDIDs (broken or missing i2c
-        * bus), fallback on the XGA standards
-        */
-       ret = drm_add_modes_noedid(connector, 1920, 1200);
-
-       /* And prefer a mode pretty much anyone can handle */
-       drm_set_preferred_mode(connector, 1024, 768);
-
-       return ret;
-}
-
-static const struct drm_connector_helper_funcs simple_bridge_con_helper_funcs = {
-       .get_modes      = simple_bridge_get_modes,
-};
-
-static enum drm_connector_status
-simple_bridge_connector_detect(struct drm_connector *connector, bool force)
-{
-       struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
-
-       /*
-        * Even if we have an I2C bus, we can't assume that the cable
-        * is disconnected if drm_probe_ddc fails. Some cables don't
-        * wire the DDC pins, or the I2C bus might not be working at
-        * all.
-        */
-       if (sbridge->ddc && drm_probe_ddc(sbridge->ddc))
-               return connector_status_connected;
-
-       return connector_status_unknown;
-}
-
-static const struct drm_connector_funcs simple_bridge_con_funcs = {
-       .detect                 = simple_bridge_connector_detect,
-       .fill_modes             = drm_helper_probe_single_connector_modes,
-       .destroy                = drm_connector_cleanup,
-       .reset                  = drm_atomic_helper_connector_reset,
-       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-       .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
-};
-
-static int simple_bridge_attach(struct drm_bridge *bridge,
-                               enum drm_bridge_attach_flags flags)
-{
-       struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
-       int ret;
-
-       if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
-               DRM_ERROR("Fix bridge driver to make connector optional!");
-               return -EINVAL;
-       }
-
-       if (!bridge->encoder) {
-               DRM_ERROR("Missing encoder\n");
-               return -ENODEV;
-       }
-
-       drm_connector_helper_add(&sbridge->connector,
-                                &simple_bridge_con_helper_funcs);
-       ret = drm_connector_init_with_ddc(bridge->dev, &sbridge->connector,
-                                         &simple_bridge_con_funcs,
-                                         DRM_MODE_CONNECTOR_VGA,
-                                         sbridge->ddc);
-       if (ret) {
-               DRM_ERROR("Failed to initialize connector\n");
-               return ret;
-       }
-
-       drm_connector_attach_encoder(&sbridge->connector,
-                                         bridge->encoder);
-
-       return 0;
-}
-
-static void simple_bridge_enable(struct drm_bridge *bridge)
-{
-       struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
-       int ret = 0;
-
-       if (sbridge->vdd)
-               ret = regulator_enable(sbridge->vdd);
-
-       if (ret)
-               DRM_ERROR("Failed to enable vdd regulator: %d\n", ret);
-}
-
-static void simple_bridge_disable(struct drm_bridge *bridge)
-{
-       struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
-
-       if (sbridge->vdd)
-               regulator_disable(sbridge->vdd);
-}
-
-static const struct drm_bridge_funcs simple_bridge_bridge_funcs = {
-       .attach         = simple_bridge_attach,
-       .enable         = simple_bridge_enable,
-       .disable        = simple_bridge_disable,
-};
-
-static struct i2c_adapter *simple_bridge_retrieve_ddc(struct device *dev)
-{
-       struct device_node *phandle, *remote;
-       struct i2c_adapter *ddc;
-
-       remote = of_graph_get_remote_node(dev->of_node, 1, -1);
-       if (!remote)
-               return ERR_PTR(-EINVAL);
-
-       phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
-       of_node_put(remote);
-       if (!phandle)
-               return ERR_PTR(-ENODEV);
-
-       ddc = of_get_i2c_adapter_by_node(phandle);
-       of_node_put(phandle);
-       if (!ddc)
-               return ERR_PTR(-EPROBE_DEFER);
-
-       return ddc;
-}
-
-static int simple_bridge_probe(struct platform_device *pdev)
-{
-       struct simple_bridge *sbridge;
-
-       sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL);
-       if (!sbridge)
-               return -ENOMEM;
-       platform_set_drvdata(pdev, sbridge);
-
-       sbridge->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
-       if (IS_ERR(sbridge->vdd)) {
-               int ret = PTR_ERR(sbridge->vdd);
-               if (ret == -EPROBE_DEFER)
-                       return -EPROBE_DEFER;
-               sbridge->vdd = NULL;
-               dev_dbg(&pdev->dev, "No vdd regulator found: %d\n", ret);
-       }
-
-       sbridge->ddc = simple_bridge_retrieve_ddc(&pdev->dev);
-       if (IS_ERR(sbridge->ddc)) {
-               if (PTR_ERR(sbridge->ddc) == -ENODEV) {
-                       dev_dbg(&pdev->dev,
-                               "No i2c bus specified. Disabling EDID readout\n");
-                       sbridge->ddc = NULL;
-               } else {
-                       dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
-                       return PTR_ERR(sbridge->ddc);
-               }
-       }
-
-       sbridge->bridge.funcs = &simple_bridge_bridge_funcs;
-       sbridge->bridge.of_node = pdev->dev.of_node;
-       sbridge->bridge.timings = of_device_get_match_data(&pdev->dev);
-
-       drm_bridge_add(&sbridge->bridge);
-
-       return 0;
-}
-
-static int simple_bridge_remove(struct platform_device *pdev)
-{
-       struct simple_bridge *sbridge = platform_get_drvdata(pdev);
-
-       drm_bridge_remove(&sbridge->bridge);
-
-       if (sbridge->ddc)
-               i2c_put_adapter(sbridge->ddc);
-
-       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_bridge_timings = {
-       /* Timing specifications, datasheet page 7 */
-       .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_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_bridge_timings = {
-       /* From timing diagram, datasheet page 9 */
-       .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_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_bridge_timings = {
-       /* From timing diagram, datasheet page 14 */
-       .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
-       /* From datasheet, page 16 */
-       .setup_time_ps = 2000,
-       .hold_time_ps = 500,
-};
-
-static const struct of_device_id simple_bridge_match[] = {
-       {
-               .compatible = "dumb-vga-dac",
-               .data = NULL,
-       },
-       {
-               .compatible = "adi,adv7123",
-               .data = &default_bridge_timings,
-       },
-       {
-               .compatible = "ti,ths8135",
-               .data = &ti_ths8135_bridge_timings,
-       },
-       {
-               .compatible = "ti,ths8134",
-               .data = &ti_ths8134_bridge_timings,
-       },
-       {},
-};
-MODULE_DEVICE_TABLE(of, simple_bridge_match);
-
-static struct platform_driver simple_bridge_driver = {
-       .probe  = simple_bridge_probe,
-       .remove = simple_bridge_remove,
-       .driver         = {
-               .name           = "dumb-vga-dac",
-               .of_match_table = simple_bridge_match,
-       },
-};
-module_platform_driver(simple_bridge_driver);
-
-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-MODULE_DESCRIPTION("Simple DRM bridge driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
new file mode 100644 (file)
index 0000000..00d810c
--- /dev/null
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2015-2016 Free Electrons
+ * Copyright (C) 2015-2016 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+struct simple_bridge {
+       struct drm_bridge       bridge;
+       struct drm_connector    connector;
+
+       struct i2c_adapter      *ddc;
+       struct regulator        *vdd;
+};
+
+static inline struct simple_bridge *
+drm_bridge_to_simple_bridge(struct drm_bridge *bridge)
+{
+       return container_of(bridge, struct simple_bridge, bridge);
+}
+
+static inline struct simple_bridge *
+drm_connector_to_simple_bridge(struct drm_connector *connector)
+{
+       return container_of(connector, struct simple_bridge, connector);
+}
+
+static int simple_bridge_get_modes(struct drm_connector *connector)
+{
+       struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
+       struct edid *edid;
+       int ret;
+
+       if (!sbridge->ddc)
+               goto fallback;
+
+       edid = drm_get_edid(connector, sbridge->ddc);
+       if (!edid) {
+               DRM_INFO("EDID readout failed, falling back to standard modes\n");
+               goto fallback;
+       }
+
+       drm_connector_update_edid_property(connector, edid);
+       ret = drm_add_edid_modes(connector, edid);
+       kfree(edid);
+       return ret;
+
+fallback:
+       /*
+        * In case we cannot retrieve the EDIDs (broken or missing i2c
+        * bus), fallback on the XGA standards
+        */
+       ret = drm_add_modes_noedid(connector, 1920, 1200);
+
+       /* And prefer a mode pretty much anyone can handle */
+       drm_set_preferred_mode(connector, 1024, 768);
+
+       return ret;
+}
+
+static const struct drm_connector_helper_funcs simple_bridge_con_helper_funcs = {
+       .get_modes      = simple_bridge_get_modes,
+};
+
+static enum drm_connector_status
+simple_bridge_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
+
+       /*
+        * Even if we have an I2C bus, we can't assume that the cable
+        * is disconnected if drm_probe_ddc fails. Some cables don't
+        * wire the DDC pins, or the I2C bus might not be working at
+        * all.
+        */
+       if (sbridge->ddc && drm_probe_ddc(sbridge->ddc))
+               return connector_status_connected;
+
+       return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs simple_bridge_con_funcs = {
+       .detect                 = simple_bridge_connector_detect,
+       .fill_modes             = drm_helper_probe_single_connector_modes,
+       .destroy                = drm_connector_cleanup,
+       .reset                  = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
+};
+
+static int simple_bridge_attach(struct drm_bridge *bridge,
+                               enum drm_bridge_attach_flags flags)
+{
+       struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
+       int ret;
+
+       if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
+               DRM_ERROR("Fix bridge driver to make connector optional!");
+               return -EINVAL;
+       }
+
+       if (!bridge->encoder) {
+               DRM_ERROR("Missing encoder\n");
+               return -ENODEV;
+       }
+
+       drm_connector_helper_add(&sbridge->connector,
+                                &simple_bridge_con_helper_funcs);
+       ret = drm_connector_init_with_ddc(bridge->dev, &sbridge->connector,
+                                         &simple_bridge_con_funcs,
+                                         DRM_MODE_CONNECTOR_VGA,
+                                         sbridge->ddc);
+       if (ret) {
+               DRM_ERROR("Failed to initialize connector\n");
+               return ret;
+       }
+
+       drm_connector_attach_encoder(&sbridge->connector,
+                                         bridge->encoder);
+
+       return 0;
+}
+
+static void simple_bridge_enable(struct drm_bridge *bridge)
+{
+       struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
+       int ret = 0;
+
+       if (sbridge->vdd)
+               ret = regulator_enable(sbridge->vdd);
+
+       if (ret)
+               DRM_ERROR("Failed to enable vdd regulator: %d\n", ret);
+}
+
+static void simple_bridge_disable(struct drm_bridge *bridge)
+{
+       struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
+
+       if (sbridge->vdd)
+               regulator_disable(sbridge->vdd);
+}
+
+static const struct drm_bridge_funcs simple_bridge_bridge_funcs = {
+       .attach         = simple_bridge_attach,
+       .enable         = simple_bridge_enable,
+       .disable        = simple_bridge_disable,
+};
+
+static struct i2c_adapter *simple_bridge_retrieve_ddc(struct device *dev)
+{
+       struct device_node *phandle, *remote;
+       struct i2c_adapter *ddc;
+
+       remote = of_graph_get_remote_node(dev->of_node, 1, -1);
+       if (!remote)
+               return ERR_PTR(-EINVAL);
+
+       phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
+       of_node_put(remote);
+       if (!phandle)
+               return ERR_PTR(-ENODEV);
+
+       ddc = of_get_i2c_adapter_by_node(phandle);
+       of_node_put(phandle);
+       if (!ddc)
+               return ERR_PTR(-EPROBE_DEFER);
+
+       return ddc;
+}
+
+static int simple_bridge_probe(struct platform_device *pdev)
+{
+       struct simple_bridge *sbridge;
+
+       sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL);
+       if (!sbridge)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, sbridge);
+
+       sbridge->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
+       if (IS_ERR(sbridge->vdd)) {
+               int ret = PTR_ERR(sbridge->vdd);
+               if (ret == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               sbridge->vdd = NULL;
+               dev_dbg(&pdev->dev, "No vdd regulator found: %d\n", ret);
+       }
+
+       sbridge->ddc = simple_bridge_retrieve_ddc(&pdev->dev);
+       if (IS_ERR(sbridge->ddc)) {
+               if (PTR_ERR(sbridge->ddc) == -ENODEV) {
+                       dev_dbg(&pdev->dev,
+                               "No i2c bus specified. Disabling EDID readout\n");
+                       sbridge->ddc = NULL;
+               } else {
+                       dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
+                       return PTR_ERR(sbridge->ddc);
+               }
+       }
+
+       sbridge->bridge.funcs = &simple_bridge_bridge_funcs;
+       sbridge->bridge.of_node = pdev->dev.of_node;
+       sbridge->bridge.timings = of_device_get_match_data(&pdev->dev);
+
+       drm_bridge_add(&sbridge->bridge);
+
+       return 0;
+}
+
+static int simple_bridge_remove(struct platform_device *pdev)
+{
+       struct simple_bridge *sbridge = platform_get_drvdata(pdev);
+
+       drm_bridge_remove(&sbridge->bridge);
+
+       if (sbridge->ddc)
+               i2c_put_adapter(sbridge->ddc);
+
+       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_bridge_timings = {
+       /* Timing specifications, datasheet page 7 */
+       .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_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_bridge_timings = {
+       /* From timing diagram, datasheet page 9 */
+       .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_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_bridge_timings = {
+       /* From timing diagram, datasheet page 14 */
+       .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
+       /* From datasheet, page 16 */
+       .setup_time_ps = 2000,
+       .hold_time_ps = 500,
+};
+
+static const struct of_device_id simple_bridge_match[] = {
+       {
+               .compatible = "dumb-vga-dac",
+               .data = NULL,
+       },
+       {
+               .compatible = "adi,adv7123",
+               .data = &default_bridge_timings,
+       },
+       {
+               .compatible = "ti,ths8135",
+               .data = &ti_ths8135_bridge_timings,
+       },
+       {
+               .compatible = "ti,ths8134",
+               .data = &ti_ths8134_bridge_timings,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, simple_bridge_match);
+
+static struct platform_driver simple_bridge_driver = {
+       .probe  = simple_bridge_probe,
+       .remove = simple_bridge_remove,
+       .driver         = {
+               .name           = "simple-bridge",
+               .of_match_table = simple_bridge_match,
+       },
+};
+module_platform_driver(simple_bridge_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Simple DRM bridge driver");
+MODULE_LICENSE("GPL");