diff options
author | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2013-05-24 07:20:45 -0400 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2013-06-17 07:30:03 -0400 |
commit | 348077b154357eec595068a3336ef6beb870e6f3 (patch) | |
tree | 081d3697a47016cc0c129d5abbdba1399c8c3b04 /drivers/video | |
parent | a0ee577fa26959d6ae8fe89ea5121ec444e8c0f1 (diff) |
OMAPDSS: Add new DVI Connector driver
Add DVI Connector driver which uses the new DSS device model and DSS
ops.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/omap2/displays-new/Kconfig | 6 | ||||
-rw-r--r-- | drivers/video/omap2/displays-new/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/omap2/displays-new/connector-dvi.c | 351 |
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 | ||
15 | config DISPLAY_CONNECTOR_DVI | ||
16 | tristate "DVI Connector" | ||
17 | depends on I2C | ||
18 | help | ||
19 | Driver for a generic DVI connector. | ||
20 | |||
15 | endmenu | 21 | endmenu |
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 @@ | |||
1 | obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o | 1 | obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o |
2 | obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o | 2 | obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o |
3 | obj-$(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 | |||
22 | static 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 | |||
43 | struct 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 | |||
54 | static 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 | |||
70 | static 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 | |||
81 | static 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 | |||
104 | static 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 | |||
117 | static 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 | |||
129 | static 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 | |||
137 | static 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 | |||
146 | static 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 | |||
177 | static 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 | |||
208 | static 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 | |||
222 | static 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 | |||
239 | static 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 | |||
277 | static 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 | |||
314 | err_reg: | ||
315 | omap_dss_put_device(ddata->in); | ||
316 | return r; | ||
317 | } | ||
318 | |||
319 | static 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 | |||
338 | static 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 | |||
347 | module_platform_driver(dvi_connector_driver); | ||
348 | |||
349 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
350 | MODULE_DESCRIPTION("Generic DVI Connector driver"); | ||
351 | MODULE_LICENSE("GPL"); | ||