aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2016-09-30 10:37:06 -0400
committerArchit Taneja <architt@codeaurora.org>2016-10-10 01:28:44 -0400
commit56fe8b6f499167e3f9e0aafc0909efe9fb673323 (patch)
tree1cde29555ecc1adc74dc8454e8d949f551410dd6 /drivers/gpu
parenta4fce9cb782ad340ee5576a38e934e5e75832dc6 (diff)
drm/bridge: Add RGB to VGA bridge support
Some boards have an entirely passive RGB to VGA bridge, based on DACs implemented by 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> Reviewed-by: Sean Paul <seanpaul@chromium.org> Tested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Archit Taneja <architt@codeaurora.org> Link: http://patchwork.freedesktop.org/patch/msgid/20160930143709.1388-3-maxime.ripard@free-electrons.com
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/bridge/Kconfig7
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/dumb-vga-dac.c223
3 files changed, 231 insertions, 0 deletions
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index b590e678052d..10e12e74fc9f 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -17,6 +17,13 @@ config DRM_ANALOGIX_ANX78XX
17 the HDMI output of an application processor to MyDP 17 the HDMI output of an application processor to MyDP
18 or DisplayPort. 18 or DisplayPort.
19 19
20config DRM_DUMB_VGA_DAC
21 tristate "Dumb VGA DAC Bridge support"
22 depends on OF
23 select DRM_KMS_HELPER
24 help
25 Support for RGB to VGA DAC based bridges
26
20config DRM_DW_HDMI 27config DRM_DW_HDMI
21 tristate 28 tristate
22 select DRM_KMS_HELPER 29 select DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index efdb07e878f5..cdf3a3cf765d 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -1,6 +1,7 @@
1ccflags-y := -Iinclude/drm 1ccflags-y := -Iinclude/drm
2 2
3obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o 3obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
4obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
4obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o 5obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
5obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o 6obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
6obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o 7obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c
new file mode 100644
index 000000000000..afec232185a7
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c
@@ -0,0 +1,223 @@
1/*
2 * Copyright (C) 2015-2016 Free Electrons
3 * Copyright (C) 2015-2016 NextThing Co
4 *
5 * Maxime Ripard <maxime.ripard@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 */
12
13#include <linux/module.h>
14#include <linux/of_graph.h>
15
16#include <drm/drmP.h>
17#include <drm/drm_atomic_helper.h>
18#include <drm/drm_crtc.h>
19#include <drm/drm_crtc_helper.h>
20
21struct dumb_vga {
22 struct drm_bridge bridge;
23 struct drm_connector connector;
24
25 struct i2c_adapter *ddc;
26};
27
28static inline struct dumb_vga *
29drm_bridge_to_dumb_vga(struct drm_bridge *bridge)
30{
31 return container_of(bridge, struct dumb_vga, bridge);
32}
33
34static inline struct dumb_vga *
35drm_connector_to_dumb_vga(struct drm_connector *connector)
36{
37 return container_of(connector, struct dumb_vga, connector);
38}
39
40static int dumb_vga_get_modes(struct drm_connector *connector)
41{
42 struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
43 struct edid *edid;
44 int ret;
45
46 if (IS_ERR(vga->ddc))
47 goto fallback;
48
49 edid = drm_get_edid(connector, vga->ddc);
50 if (!edid) {
51 DRM_INFO("EDID readout failed, falling back to standard modes\n");
52 goto fallback;
53 }
54
55 drm_mode_connector_update_edid_property(connector, edid);
56 return drm_add_edid_modes(connector, edid);
57
58fallback:
59 /*
60 * In case we cannot retrieve the EDIDs (broken or missing i2c
61 * bus), fallback on the XGA standards
62 */
63 ret = drm_add_modes_noedid(connector, 1920, 1200);
64
65 /* And prefer a mode pretty much anyone can handle */
66 drm_set_preferred_mode(connector, 1024, 768);
67
68 return ret;
69}
70
71static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = {
72 .get_modes = dumb_vga_get_modes,
73};
74
75static enum drm_connector_status
76dumb_vga_connector_detect(struct drm_connector *connector, bool force)
77{
78 struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
79
80 /*
81 * Even if we have an I2C bus, we can't assume that the cable
82 * is disconnected if drm_probe_ddc fails. Some cables don't
83 * wire the DDC pins, or the I2C bus might not be working at
84 * all.
85 */
86 if (!IS_ERR(vga->ddc) && drm_probe_ddc(vga->ddc))
87 return connector_status_connected;
88
89 return connector_status_unknown;
90}
91
92static const struct drm_connector_funcs dumb_vga_con_funcs = {
93 .dpms = drm_atomic_helper_connector_dpms,
94 .detect = dumb_vga_connector_detect,
95 .fill_modes = drm_helper_probe_single_connector_modes,
96 .destroy = drm_connector_cleanup,
97 .reset = drm_atomic_helper_connector_reset,
98 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
99 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
100};
101
102static int dumb_vga_attach(struct drm_bridge *bridge)
103{
104 struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
105 int ret;
106
107 if (!bridge->encoder) {
108 DRM_ERROR("Missing encoder\n");
109 return -ENODEV;
110 }
111
112 drm_connector_helper_add(&vga->connector,
113 &dumb_vga_con_helper_funcs);
114 ret = drm_connector_init(bridge->dev, &vga->connector,
115 &dumb_vga_con_funcs, DRM_MODE_CONNECTOR_VGA);
116 if (ret) {
117 DRM_ERROR("Failed to initialize connector\n");
118 return ret;
119 }
120
121 drm_mode_connector_attach_encoder(&vga->connector,
122 bridge->encoder);
123
124 return 0;
125}
126
127static const struct drm_bridge_funcs dumb_vga_bridge_funcs = {
128 .attach = dumb_vga_attach,
129};
130
131static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev)
132{
133 struct device_node *end_node, *phandle, *remote;
134 struct i2c_adapter *ddc;
135
136 end_node = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
137 if (!end_node) {
138 dev_err(dev, "Missing connector endpoint\n");
139 return ERR_PTR(-ENODEV);
140 }
141
142 remote = of_graph_get_remote_port_parent(end_node);
143 of_node_put(end_node);
144 if (!remote) {
145 dev_err(dev, "Enable to parse remote node\n");
146 return ERR_PTR(-EINVAL);
147 }
148
149 phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
150 of_node_put(remote);
151 if (!phandle)
152 return ERR_PTR(-ENODEV);
153
154 ddc = of_get_i2c_adapter_by_node(phandle);
155 of_node_put(phandle);
156 if (!ddc)
157 return ERR_PTR(-EPROBE_DEFER);
158
159 return ddc;
160}
161
162static int dumb_vga_probe(struct platform_device *pdev)
163{
164 struct dumb_vga *vga;
165 int ret;
166
167 vga = devm_kzalloc(&pdev->dev, sizeof(*vga), GFP_KERNEL);
168 if (!vga)
169 return -ENOMEM;
170 platform_set_drvdata(pdev, vga);
171
172 vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev);
173 if (IS_ERR(vga->ddc)) {
174 if (PTR_ERR(vga->ddc) == -ENODEV) {
175 dev_dbg(&pdev->dev,
176 "No i2c bus specified. Disabling EDID readout\n");
177 } else {
178 dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
179 return PTR_ERR(vga->ddc);
180 }
181 }
182
183 vga->bridge.funcs = &dumb_vga_bridge_funcs;
184 vga->bridge.of_node = pdev->dev.of_node;
185
186 ret = drm_bridge_add(&vga->bridge);
187 if (ret && !IS_ERR(vga->ddc))
188 i2c_put_adapter(vga->ddc);
189
190 return ret;
191}
192
193static int dumb_vga_remove(struct platform_device *pdev)
194{
195 struct dumb_vga *vga = platform_get_drvdata(pdev);
196
197 drm_bridge_remove(&vga->bridge);
198
199 if (!IS_ERR(vga->ddc))
200 i2c_put_adapter(vga->ddc);
201
202 return 0;
203}
204
205static const struct of_device_id dumb_vga_match[] = {
206 { .compatible = "dumb-vga-dac" },
207 {},
208};
209MODULE_DEVICE_TABLE(of, dumb_vga_match);
210
211static struct platform_driver dumb_vga_driver = {
212 .probe = dumb_vga_probe,
213 .remove = dumb_vga_remove,
214 .driver = {
215 .name = "dumb-vga-dac",
216 .of_match_table = dumb_vga_match,
217 },
218};
219module_platform_driver(dumb_vga_driver);
220
221MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
222MODULE_DESCRIPTION("Dumb VGA DAC bridge driver");
223MODULE_LICENSE("GPL");