aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/omap2/displays-new/Kconfig6
-rw-r--r--drivers/video/omap2/displays-new/Makefile1
-rw-r--r--drivers/video/omap2/displays-new/connector-dvi.c351
3 files changed, 358 insertions, 0 deletions
diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig
index 6e4af85cf182..76570fc94c51 100644
--- a/drivers/video/omap2/displays-new/Kconfig
+++ b/drivers/video/omap2/displays-new/Kconfig
@@ -12,4 +12,10 @@ config DISPLAY_ENCODER_TPD12S015
12 Driver for TPD12S015, which offers HDMI ESD protection and level 12 Driver for TPD12S015, which offers HDMI ESD protection and level
13 shifting. 13 shifting.
14 14
15config DISPLAY_CONNECTOR_DVI
16 tristate "DVI Connector"
17 depends on I2C
18 help
19 Driver for a generic DVI connector.
20
15endmenu 21endmenu
diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile
index c2ee45e2e2c3..ed7dd062dc92 100644
--- a/drivers/video/omap2/displays-new/Makefile
+++ b/drivers/video/omap2/displays-new/Makefile
@@ -1,2 +1,3 @@
1obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o 1obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o
2obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o 2obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o
3obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o
diff --git a/drivers/video/omap2/displays-new/connector-dvi.c b/drivers/video/omap2/displays-new/connector-dvi.c
new file mode 100644
index 000000000000..bc5f8ceda371
--- /dev/null
+++ b/drivers/video/omap2/displays-new/connector-dvi.c
@@ -0,0 +1,351 @@
1/*
2 * Generic DVI Connector driver
3 *
4 * Copyright (C) 2013 Texas Instruments
5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 */
11
12#include <linux/i2c.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
16
17#include <drm/drm_edid.h>
18
19#include <video/omapdss.h>
20#include <video/omap-panel-data.h>
21
22static const struct omap_video_timings dvic_default_timings = {
23 .x_res = 640,
24 .y_res = 480,
25
26 .pixel_clock = 23500,
27
28 .hfp = 48,
29 .hsw = 32,
30 .hbp = 80,
31
32 .vfp = 3,
33 .vsw = 4,
34 .vbp = 7,
35
36 .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
37 .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
38 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
39 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
40 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
41};
42
43struct panel_drv_data {
44 struct omap_dss_device dssdev;
45 struct omap_dss_device *in;
46
47 struct omap_video_timings timings;
48
49 struct i2c_adapter *i2c_adapter;
50};
51
52#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
53
54static int dvic_connect(struct omap_dss_device *dssdev)
55{
56 struct panel_drv_data *ddata = to_panel_data(dssdev);
57 struct omap_dss_device *in = ddata->in;
58 int r;
59
60 if (omapdss_device_is_connected(dssdev))
61 return 0;
62
63 r = in->ops.dvi->connect(in, dssdev);
64 if (r)
65 return r;
66
67 return 0;
68}
69
70static void dvic_disconnect(struct omap_dss_device *dssdev)
71{
72 struct panel_drv_data *ddata = to_panel_data(dssdev);
73 struct omap_dss_device *in = ddata->in;
74
75 if (!omapdss_device_is_connected(dssdev))
76 return;
77
78 in->ops.dvi->disconnect(in, dssdev);
79}
80
81static int dvic_enable(struct omap_dss_device *dssdev)
82{
83 struct panel_drv_data *ddata = to_panel_data(dssdev);
84 struct omap_dss_device *in = ddata->in;
85 int r;
86
87 if (!omapdss_device_is_connected(dssdev))
88 return -ENODEV;
89
90 if (omapdss_device_is_enabled(dssdev))
91 return 0;
92
93 in->ops.dvi->set_timings(in, &ddata->timings);
94
95 r = in->ops.dvi->enable(in);
96 if (r)
97 return r;
98
99 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
100
101 return 0;
102}
103
104static void dvic_disable(struct omap_dss_device *dssdev)
105{
106 struct panel_drv_data *ddata = to_panel_data(dssdev);
107 struct omap_dss_device *in = ddata->in;
108
109 if (!omapdss_device_is_enabled(dssdev))
110 return;
111
112 in->ops.dvi->disable(in);
113
114 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
115}
116
117static void dvic_set_timings(struct omap_dss_device *dssdev,
118 struct omap_video_timings *timings)
119{
120 struct panel_drv_data *ddata = to_panel_data(dssdev);
121 struct omap_dss_device *in = ddata->in;
122
123 ddata->timings = *timings;
124 dssdev->panel.timings = *timings;
125
126 in->ops.dvi->set_timings(in, timings);
127}
128
129static void dvic_get_timings(struct omap_dss_device *dssdev,
130 struct omap_video_timings *timings)
131{
132 struct panel_drv_data *ddata = to_panel_data(dssdev);
133
134 *timings = ddata->timings;
135}
136
137static int dvic_check_timings(struct omap_dss_device *dssdev,
138 struct omap_video_timings *timings)
139{
140 struct panel_drv_data *ddata = to_panel_data(dssdev);
141 struct omap_dss_device *in = ddata->in;
142
143 return in->ops.dvi->check_timings(in, timings);
144}
145
146static int dvic_ddc_read(struct i2c_adapter *adapter,
147 unsigned char *buf, u16 count, u8 offset)
148{
149 int r, retries;
150
151 for (retries = 3; retries > 0; retries--) {
152 struct i2c_msg msgs[] = {
153 {
154 .addr = DDC_ADDR,
155 .flags = 0,
156 .len = 1,
157 .buf = &offset,
158 }, {
159 .addr = DDC_ADDR,
160 .flags = I2C_M_RD,
161 .len = count,
162 .buf = buf,
163 }
164 };
165
166 r = i2c_transfer(adapter, msgs, 2);
167 if (r == 2)
168 return 0;
169
170 if (r != -EAGAIN)
171 break;
172 }
173
174 return r < 0 ? r : -EIO;
175}
176
177static int dvic_read_edid(struct omap_dss_device *dssdev,
178 u8 *edid, int len)
179{
180 struct panel_drv_data *ddata = to_panel_data(dssdev);
181 int r, l, bytes_read;
182
183 if (!ddata->i2c_adapter)
184 return -ENODEV;
185
186 l = min(EDID_LENGTH, len);
187 r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
188 if (r)
189 return r;
190
191 bytes_read = l;
192
193 /* if there are extensions, read second block */
194 if (len > EDID_LENGTH && edid[0x7e] > 0) {
195 l = min(EDID_LENGTH, len - EDID_LENGTH);
196
197 r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
198 l, EDID_LENGTH);
199 if (r)
200 return r;
201
202 bytes_read += l;
203 }
204
205 return bytes_read;
206}
207
208static bool dvic_detect(struct omap_dss_device *dssdev)
209{
210 struct panel_drv_data *ddata = to_panel_data(dssdev);
211 unsigned char out;
212 int r;
213
214 if (!ddata->i2c_adapter)
215 return true;
216
217 r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
218
219 return r == 0;
220}
221
222static struct omap_dss_driver dvic_driver = {
223 .connect = dvic_connect,
224 .disconnect = dvic_disconnect,
225
226 .enable = dvic_enable,
227 .disable = dvic_disable,
228
229 .set_timings = dvic_set_timings,
230 .get_timings = dvic_get_timings,
231 .check_timings = dvic_check_timings,
232
233 .get_resolution = omapdss_default_get_resolution,
234
235 .read_edid = dvic_read_edid,
236 .detect = dvic_detect,
237};
238
239static int dvic_probe_pdata(struct platform_device *pdev)
240{
241 struct panel_drv_data *ddata = platform_get_drvdata(pdev);
242 struct connector_dvi_platform_data *pdata;
243 struct omap_dss_device *in, *dssdev;
244 int i2c_bus_num;
245
246 pdata = dev_get_platdata(&pdev->dev);
247 i2c_bus_num = pdata->i2c_bus_num;
248
249 if (i2c_bus_num != -1) {
250 struct i2c_adapter *adapter;
251
252 adapter = i2c_get_adapter(i2c_bus_num);
253 if (!adapter) {
254 dev_err(&pdev->dev,
255 "Failed to get I2C adapter, bus %d\n",
256 i2c_bus_num);
257 return -EPROBE_DEFER;
258 }
259
260 ddata->i2c_adapter = adapter;
261 }
262
263 in = omap_dss_find_output(pdata->source);
264 if (in == NULL) {
265 dev_err(&pdev->dev, "Failed to find video source\n");
266 return -ENODEV;
267 }
268
269 ddata->in = in;
270
271 dssdev = &ddata->dssdev;
272 dssdev->name = pdata->name;
273
274 return 0;
275}
276
277static int dvic_probe(struct platform_device *pdev)
278{
279 struct panel_drv_data *ddata;
280 struct omap_dss_device *dssdev;
281 int r;
282
283 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
284 if (!ddata)
285 return -ENOMEM;
286
287 platform_set_drvdata(pdev, ddata);
288
289 if (dev_get_platdata(&pdev->dev)) {
290 r = dvic_probe_pdata(pdev);
291 if (r)
292 return r;
293 } else {
294 return -ENODEV;
295 }
296
297 ddata->timings = dvic_default_timings;
298
299 dssdev = &ddata->dssdev;
300 dssdev->driver = &dvic_driver;
301 dssdev->dev = &pdev->dev;
302 dssdev->type = OMAP_DISPLAY_TYPE_DVI;
303 dssdev->owner = THIS_MODULE;
304 dssdev->panel.timings = dvic_default_timings;
305
306 r = omapdss_register_display(dssdev);
307 if (r) {
308 dev_err(&pdev->dev, "Failed to register panel\n");
309 goto err_reg;
310 }
311
312 return 0;
313
314err_reg:
315 omap_dss_put_device(ddata->in);
316 return r;
317}
318
319static int __exit dvic_remove(struct platform_device *pdev)
320{
321 struct panel_drv_data *ddata = platform_get_drvdata(pdev);
322 struct omap_dss_device *dssdev = &ddata->dssdev;
323 struct omap_dss_device *in = ddata->in;
324
325 omapdss_unregister_display(&ddata->dssdev);
326
327 dvic_disable(dssdev);
328 dvic_disconnect(dssdev);
329
330 omap_dss_put_device(in);
331
332 if (ddata->i2c_adapter)
333 i2c_put_adapter(ddata->i2c_adapter);
334
335 return 0;
336}
337
338static struct platform_driver dvi_connector_driver = {
339 .probe = dvic_probe,
340 .remove = __exit_p(dvic_remove),
341 .driver = {
342 .name = "connector-dvi",
343 .owner = THIS_MODULE,
344 },
345};
346
347module_platform_driver(dvi_connector_driver);
348
349MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
350MODULE_DESCRIPTION("Generic DVI Connector driver");
351MODULE_LICENSE("GPL");