Hi,
This serie is about adding support for the RGB to VGA bridge found in the A13-Olinuxino and the CHIP VGA adapter.
Both these boards rely on an entirely passive bridge made out of resitor ladders that do not require any initialisation. The only thing needed is to get the timings from the screen if available (and if not, fall back on XGA standards), set up the display pipeline to output on the RGB bus with the proper timings, and you're done.
This serie also fixes a bunch of bugs uncovered when trying to increase the resolution, and hence the pixel clock, of our pipeline. It also fixes a few bugs in the DRM driver itself that went unnoticed before.
Let me know what you think, Maxime
Changes from v3: - Depends on OF in Kconfig - Fixed typos in the driver comments - Removed the mention of a "passive" bridge in the bindings doc - Made the strcuture const - Removed the nops and best_encoders implementations - Removed the call to drm_bridge_enable in the sun4i driver
Changes from v2: - Changed the compatible as suggested - Rebased on top 4.8
Changes from v1: - Switch to using a vga-connector - Use drm_encoder bridge pointer instead of doing our own - Report the connector status as unknown instead of connected by default, and as connected only if we can retrieve the EDID. - Switch to of_i2c_get_adapter by node, and put the reference when done - Rebased on linux-next
Maxime Ripard (5): drm/sun4i: rgb: Remove the bridge enable/disable functions drm/bridge: Add RGB to VGA bridge support ARM: sun5i: a13-olinuxino: Enable VGA bridge ARM: multi_v7: enable VGA bridge ARM: sunxi: Enable VGA bridge
.../bindings/display/bridge/rgb-to-vga-bridge.txt | 48 +++++ arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 54 +++++ arch/arm/configs/multi_v7_defconfig | 1 + arch/arm/configs/sunxi_defconfig | 1 + drivers/gpu/drm/bridge/Kconfig | 7 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/rgb-to-vga.c | 239 +++++++++++++++++++++ drivers/gpu/drm/sun4i/sun4i_rgb.c | 6 - 8 files changed, 351 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt create mode 100644 drivers/gpu/drm/bridge/rgb-to-vga.c
The atomic helpers already call the drm_bridge_enable on our behalf, there's no need to do it a second time.
Reported-by: Sean Paul seanpaul@chromium.org Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- drivers/gpu/drm/sun4i/sun4i_rgb.c | 6 ------ 1 file changed, 6 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index 4e4bea6f395c..d198ad7e5323 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -155,9 +155,6 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder) if (!IS_ERR(tcon->panel)) drm_panel_prepare(tcon->panel);
- /* encoder->bridge can be NULL; drm_bridge_enable checks for it */ - drm_bridge_enable(encoder->bridge); - sun4i_tcon_channel_enable(tcon, 0);
if (!IS_ERR(tcon->panel)) @@ -177,9 +174,6 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
sun4i_tcon_channel_disable(tcon, 0);
- /* encoder->bridge can be NULL; drm_bridge_disable checks for it */ - drm_bridge_disable(encoder->bridge); - if (!IS_ERR(tcon->panel)) drm_panel_unprepare(tcon->panel); }
Some boards have an entirely passive RGB to VGA bridge, based on either DACs or resistor ladders.
Those might or might not have an i2c bus routed to the VGA connector in order to access the screen EDIDs.
Add a bridge that doesn't do anything but expose the modes available on the screen, either based on the EDIDs if available, or based on the XGA standards.
Acked-by: Rob Herring robh@kernel.org Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- .../bindings/display/bridge/rgb-to-vga-bridge.txt | 48 +++++ drivers/gpu/drm/bridge/Kconfig | 7 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/rgb-to-vga.c | 239 +++++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt create mode 100644 drivers/gpu/drm/bridge/rgb-to-vga.c
diff --git a/Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt b/Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt new file mode 100644 index 000000000000..a8375bc1f9cb --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt @@ -0,0 +1,48 @@ +Dumb RGB to VGA bridge +---------------------- + +This binding is aimed for dumb RGB to VGA bridges that do not require +any configuration. + +Required properties: + +- compatible: Must be "rgb-to-vga-bridge" + +Required nodes: + +This device has two video ports. Their connections are modeled using the OF +graph bindings specified in Documentation/devicetree/bindings/graph.txt. + +- Video port 0 for RGB input +- Video port 1 for VGA output + + +Example +------- + +bridge { + compatible = "rgb-to-vga-bridge"; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + vga_bridge_in: endpoint { + remote-endpoint = <&tcon0_out_vga>; + }; + }; + + port@1 { + reg = <1>; + + vga_bridge_out: endpoint { + remote-endpoint = <&vga_con_in>; + }; + }; + }; +}; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index b590e678052d..d690398c541c 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -17,6 +17,13 @@ config DRM_ANALOGIX_ANX78XX the HDMI output of an application processor to MyDP or DisplayPort.
+config DRM_RGB_TO_VGA + tristate "Dumb RGB to VGA Bridge support" + depends on OF + select DRM_KMS_HELPER + help + Support for passive RGB to VGA bridges + config DRM_DW_HDMI tristate select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index efdb07e878f5..3bb8cbe09fe9 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,6 +1,7 @@ ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o +obj-$(CONFIG_DRM_RGB_TO_VGA) += rgb-to-vga.o obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o diff --git a/drivers/gpu/drm/bridge/rgb-to-vga.c b/drivers/gpu/drm/bridge/rgb-to-vga.c new file mode 100644 index 000000000000..14843baa5d11 --- /dev/null +++ b/drivers/gpu/drm/bridge/rgb-to-vga.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2015-2016 Free Electrons + * Copyright (C) 2015-2016 NextThing Co + * + * Maxime Ripard maxime.ripard@free-electrons.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/of_graph.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + +struct dumb_vga { + struct drm_bridge bridge; + struct drm_connector connector; + + struct i2c_adapter *ddc; +}; + +static inline struct dumb_vga * +drm_bridge_to_dumb_vga(struct drm_bridge *bridge) +{ + return container_of(bridge, struct dumb_vga, bridge); +} + +static inline struct dumb_vga * +drm_connector_to_dumb_vga(struct drm_connector *connector) +{ + return container_of(connector, struct dumb_vga, connector); +} + +static int dumb_vga_get_modes(struct drm_connector *connector) +{ + struct dumb_vga *vga = drm_connector_to_dumb_vga(connector); + struct edid *edid; + int ret; + + if (IS_ERR(vga->ddc)) + goto fallback; + + edid = drm_get_edid(connector, vga->ddc); + if (!edid) { + DRM_INFO("EDID readout failed, falling back to standard modes\n"); + goto fallback; + } + + drm_mode_connector_update_edid_property(connector, edid); + return drm_add_edid_modes(connector, edid); + +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 struct drm_encoder * +dumb_vga_best_encoder(struct drm_connector *connector) +{ + struct dumb_vga *vga = drm_connector_to_dumb_vga(connector); + + return vga->bridge.encoder; +} + +static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = { + .get_modes = dumb_vga_get_modes, +}; + +static enum drm_connector_status +dumb_vga_connector_detect(struct drm_connector *connector, bool force) +{ + struct dumb_vga *vga = drm_connector_to_dumb_vga(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 (!IS_ERR(vga->ddc) && drm_probe_ddc(vga->ddc)) + return connector_status_connected; + + return connector_status_unknown; +} + +static void +dumb_vga_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs dumb_vga_con_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = dumb_vga_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = dumb_vga_connector_destroy, + .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 dumb_vga_attach(struct drm_bridge *bridge) +{ + struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge); + int ret; + + if (!bridge->encoder) { + DRM_ERROR("Missing encoder\n"); + return -ENODEV; + } + + drm_connector_helper_add(&vga->connector, + &dumb_vga_con_helper_funcs); + ret = drm_connector_init(bridge->dev, &vga->connector, + &dumb_vga_con_funcs, DRM_MODE_CONNECTOR_VGA); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + return ret; + } + + drm_mode_connector_attach_encoder(&vga->connector, + bridge->encoder); + + return 0; +} + +static void dumb_vga_nop(struct drm_bridge *bridge) {}; + +static const struct drm_bridge_funcs dumb_vga_bridge_funcs = { + .attach = dumb_vga_attach, +}; + +static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev) +{ + struct device_node *end_node, *phandle, *remote; + struct i2c_adapter *ddc; + + end_node = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); + if (!end_node) { + dev_err(dev, "Missing connector endpoint\n"); + return ERR_PTR(-ENODEV); + } + + remote = of_graph_get_remote_port_parent(end_node); + of_node_put(end_node); + if (!remote) { + dev_err(dev, "Enable to parse remote node\n"); + 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 dumb_vga_probe(struct platform_device *pdev) +{ + struct dumb_vga *vga; + int ret; + + vga = devm_kzalloc(&pdev->dev, sizeof(*vga), GFP_KERNEL); + if (!vga) + return -ENOMEM; + platform_set_drvdata(pdev, vga); + + vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev); + if (IS_ERR(vga->ddc)) { + if (PTR_ERR(vga->ddc) == -ENODEV) { + dev_info(&pdev->dev, + "No i2c bus specified... Disabling EDID readout\n"); + } else { + dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n"); + return PTR_ERR(vga->ddc); + } + } + + vga->bridge.funcs = &dumb_vga_bridge_funcs; + vga->bridge.of_node = pdev->dev.of_node; + + ret = drm_bridge_add(&vga->bridge); + if (ret && !IS_ERR(vga->ddc)) + i2c_put_adapter(vga->ddc); + + return ret; +} + +static int dumb_vga_remove(struct platform_device *pdev) +{ + struct dumb_vga *vga = platform_get_drvdata(pdev); + + drm_bridge_remove(&vga->bridge); + + if (!IS_ERR(vga->ddc)) + i2c_put_adapter(vga->ddc); + + return 0; +} + +static const struct of_device_id dumb_vga_match[] = { + { .compatible = "rgb-to-vga-bridge" }, + {}, +}; +MODULE_DEVICE_TABLE(of, dumb_vga_match); + +struct platform_driver dumb_vga_driver = { + .probe = dumb_vga_probe, + .remove = dumb_vga_remove, + .driver = { + .name = "rgb-to-vga-bridge", + .of_match_table = dumb_vga_match, + }, +}; +module_platform_driver(dumb_vga_driver); + +MODULE_AUTHOR("Maxime Ripard maxime.ripard@free-electrons.com"); +MODULE_DESCRIPTION("Dumb RGB to VGA bridge driver"); +MODULE_LICENSE("GPL");
Now that we have support for the VGA bridges using our DRM driver, enable the display engine for the Olimex A13-Olinuxino.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Acked-by: Chen-Yu Tsai wens@csie.org --- arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts index b3c234c65ea1..01ce7ea9032d 100644 --- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts @@ -72,6 +72,47 @@ default-state = "on"; }; }; + + bridge { + compatible = "rgb-to-vga-bridge"; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + vga_bridge_in: endpoint { + remote-endpoint = <&tcon0_out_vga>; + }; + }; + + port@1 { + reg = <1>; + + vga_bridge_out: endpoint { + remote-endpoint = <&vga_con_in>; + }; + }; + }; + }; + + vga { + compatible = "vga-connector"; + + port { + vga_con_in: endpoint { + remote-endpoint = <&vga_bridge_out>; + }; + }; + }; +}; + +&be0 { + status = "okay"; };
&ehci0 { @@ -211,6 +252,19 @@ status = "okay"; };
+&tcon0 { + pinctrl-names = "default"; + pinctrl-0 = <&lcd_rgb666_pins>; + status = "okay"; +}; + +&tcon0_out { + tcon0_out_vga: endpoint@0 { + reg = <0>; + remote-endpoint = <&vga_bridge_in>; + }; +}; + &uart1 { pinctrl-names = "default"; pinctrl-0 = <&uart1_pins_b>;
Enable the RGB to VGA bridge driver in the defconfig
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 2c8665cd9dc5..22ef41afc658 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -567,6 +567,7 @@ CONFIG_DRM=y CONFIG_DRM_I2C_ADV7511=m # CONFIG_DRM_I2C_CH7006 is not set # CONFIG_DRM_I2C_SIL164 is not set +CONFIG_DRM_RGB_TO_VGA=m CONFIG_DRM_NXP_PTN3460=m CONFIG_DRM_PARADE_PS8622=m CONFIG_DRM_NOUVEAU=m
Enable the VGA bridge used on the A13-Olinuxino in the sunxi defconfig
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- arch/arm/configs/sunxi_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig index 714da336ec86..d830e258db59 100644 --- a/arch/arm/configs/sunxi_defconfig +++ b/arch/arm/configs/sunxi_defconfig @@ -98,6 +98,7 @@ CONFIG_MEDIA_RC_SUPPORT=y CONFIG_RC_DEVICES=y CONFIG_IR_SUNXI=y CONFIG_DRM=y +CONFIG_DRM_RGB_TO_VGA=y CONFIG_DRM_SUN4I=y CONFIG_FB=y CONFIG_FB_SIMPLE=y
dri-devel@lists.freedesktop.org