diff options
-rw-r--r-- | drivers/video/omap2/displays/Kconfig | 7 | ||||
-rw-r--r-- | drivers/video/omap2/displays/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/omap2/displays/panel-dvi.c | 363 | ||||
-rw-r--r-- | include/video/omap-panel-dvi.h | 37 |
4 files changed, 408 insertions, 0 deletions
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index 6ddb401552cd..16e3eab444c4 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig | |||
@@ -10,6 +10,13 @@ config PANEL_GENERIC_DPI | |||
10 | Supports LCD Panel used in TI SDP3430 and EVM boards, | 10 | Supports LCD Panel used in TI SDP3430 and EVM boards, |
11 | OMAP3517 EVM boards and CM-T35. | 11 | OMAP3517 EVM boards and CM-T35. |
12 | 12 | ||
13 | config PANEL_DVI | ||
14 | tristate "DVI output" | ||
15 | depends on OMAP2_DSS_DPI | ||
16 | help | ||
17 | Driver for external monitors, connected via DVI. The driver uses i2c | ||
18 | to read EDID information from the monitor. | ||
19 | |||
13 | config PANEL_LGPHILIPS_LB035Q02 | 20 | config PANEL_LGPHILIPS_LB035Q02 |
14 | tristate "LG.Philips LB035Q02 LCD Panel" | 21 | tristate "LG.Philips LB035Q02 LCD Panel" |
15 | depends on OMAP2_DSS_DPI && SPI | 22 | depends on OMAP2_DSS_DPI && SPI |
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index d90f73c8716a..abfda35d82d4 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o | 1 | obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o |
2 | obj-$(CONFIG_PANEL_DVI) += panel-dvi.o | ||
2 | obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o | 3 | obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o |
3 | obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o | 4 | obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o |
4 | obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o | 5 | obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o |
diff --git a/drivers/video/omap2/displays/panel-dvi.c b/drivers/video/omap2/displays/panel-dvi.c new file mode 100644 index 000000000000..03eb14af33e0 --- /dev/null +++ b/drivers/video/omap2/displays/panel-dvi.c | |||
@@ -0,0 +1,363 @@ | |||
1 | /* | ||
2 | * DVI output support | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Inc | ||
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 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along with | ||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <video/omapdss.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <drm/drm_edid.h> | ||
25 | |||
26 | #include <video/omap-panel-dvi.h> | ||
27 | |||
28 | static const struct omap_video_timings panel_dvi_default_timings = { | ||
29 | .x_res = 640, | ||
30 | .y_res = 480, | ||
31 | |||
32 | .pixel_clock = 23500, | ||
33 | |||
34 | .hfp = 48, | ||
35 | .hsw = 32, | ||
36 | .hbp = 80, | ||
37 | |||
38 | .vfp = 3, | ||
39 | .vsw = 4, | ||
40 | .vbp = 7, | ||
41 | }; | ||
42 | |||
43 | struct panel_drv_data { | ||
44 | struct omap_dss_device *dssdev; | ||
45 | |||
46 | struct mutex lock; | ||
47 | }; | ||
48 | |||
49 | static inline struct panel_dvi_platform_data | ||
50 | *get_pdata(const struct omap_dss_device *dssdev) | ||
51 | { | ||
52 | return dssdev->data; | ||
53 | } | ||
54 | |||
55 | static int panel_dvi_power_on(struct omap_dss_device *dssdev) | ||
56 | { | ||
57 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
58 | int r; | ||
59 | |||
60 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | ||
61 | return 0; | ||
62 | |||
63 | r = omapdss_dpi_display_enable(dssdev); | ||
64 | if (r) | ||
65 | goto err0; | ||
66 | |||
67 | if (pdata->platform_enable) { | ||
68 | r = pdata->platform_enable(dssdev); | ||
69 | if (r) | ||
70 | goto err1; | ||
71 | } | ||
72 | |||
73 | return 0; | ||
74 | err1: | ||
75 | omapdss_dpi_display_disable(dssdev); | ||
76 | err0: | ||
77 | return r; | ||
78 | } | ||
79 | |||
80 | static void panel_dvi_power_off(struct omap_dss_device *dssdev) | ||
81 | { | ||
82 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
83 | |||
84 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) | ||
85 | return; | ||
86 | |||
87 | if (pdata->platform_disable) | ||
88 | pdata->platform_disable(dssdev); | ||
89 | |||
90 | omapdss_dpi_display_disable(dssdev); | ||
91 | } | ||
92 | |||
93 | static int panel_dvi_probe(struct omap_dss_device *dssdev) | ||
94 | { | ||
95 | struct panel_drv_data *ddata; | ||
96 | |||
97 | ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); | ||
98 | if (!ddata) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | dssdev->panel.timings = panel_dvi_default_timings; | ||
102 | dssdev->panel.config = OMAP_DSS_LCD_TFT; | ||
103 | |||
104 | ddata->dssdev = dssdev; | ||
105 | mutex_init(&ddata->lock); | ||
106 | |||
107 | dev_set_drvdata(&dssdev->dev, ddata); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static void __exit panel_dvi_remove(struct omap_dss_device *dssdev) | ||
113 | { | ||
114 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
115 | |||
116 | mutex_lock(&ddata->lock); | ||
117 | |||
118 | dev_set_drvdata(&dssdev->dev, NULL); | ||
119 | |||
120 | mutex_unlock(&ddata->lock); | ||
121 | |||
122 | kfree(ddata); | ||
123 | } | ||
124 | |||
125 | static int panel_dvi_enable(struct omap_dss_device *dssdev) | ||
126 | { | ||
127 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
128 | int r; | ||
129 | |||
130 | mutex_lock(&ddata->lock); | ||
131 | |||
132 | r = panel_dvi_power_on(dssdev); | ||
133 | if (r == 0) | ||
134 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
135 | |||
136 | mutex_unlock(&ddata->lock); | ||
137 | |||
138 | return r; | ||
139 | } | ||
140 | |||
141 | static void panel_dvi_disable(struct omap_dss_device *dssdev) | ||
142 | { | ||
143 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
144 | |||
145 | mutex_lock(&ddata->lock); | ||
146 | |||
147 | panel_dvi_power_off(dssdev); | ||
148 | |||
149 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
150 | |||
151 | mutex_unlock(&ddata->lock); | ||
152 | } | ||
153 | |||
154 | static int panel_dvi_suspend(struct omap_dss_device *dssdev) | ||
155 | { | ||
156 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
157 | |||
158 | mutex_lock(&ddata->lock); | ||
159 | |||
160 | panel_dvi_power_off(dssdev); | ||
161 | |||
162 | dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; | ||
163 | |||
164 | mutex_unlock(&ddata->lock); | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int panel_dvi_resume(struct omap_dss_device *dssdev) | ||
170 | { | ||
171 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
172 | int r; | ||
173 | |||
174 | mutex_lock(&ddata->lock); | ||
175 | |||
176 | r = panel_dvi_power_on(dssdev); | ||
177 | if (r == 0) | ||
178 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
179 | |||
180 | mutex_unlock(&ddata->lock); | ||
181 | |||
182 | return r; | ||
183 | } | ||
184 | |||
185 | static void panel_dvi_set_timings(struct omap_dss_device *dssdev, | ||
186 | struct omap_video_timings *timings) | ||
187 | { | ||
188 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
189 | |||
190 | mutex_lock(&ddata->lock); | ||
191 | dpi_set_timings(dssdev, timings); | ||
192 | mutex_unlock(&ddata->lock); | ||
193 | } | ||
194 | |||
195 | static void panel_dvi_get_timings(struct omap_dss_device *dssdev, | ||
196 | struct omap_video_timings *timings) | ||
197 | { | ||
198 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
199 | |||
200 | mutex_lock(&ddata->lock); | ||
201 | *timings = dssdev->panel.timings; | ||
202 | mutex_unlock(&ddata->lock); | ||
203 | } | ||
204 | |||
205 | static int panel_dvi_check_timings(struct omap_dss_device *dssdev, | ||
206 | struct omap_video_timings *timings) | ||
207 | { | ||
208 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
209 | int r; | ||
210 | |||
211 | mutex_lock(&ddata->lock); | ||
212 | r = dpi_check_timings(dssdev, timings); | ||
213 | mutex_unlock(&ddata->lock); | ||
214 | |||
215 | return r; | ||
216 | } | ||
217 | |||
218 | |||
219 | static int panel_dvi_ddc_read(struct i2c_adapter *adapter, | ||
220 | unsigned char *buf, u16 count, u8 offset) | ||
221 | { | ||
222 | int r, retries; | ||
223 | |||
224 | for (retries = 3; retries > 0; retries--) { | ||
225 | struct i2c_msg msgs[] = { | ||
226 | { | ||
227 | .addr = DDC_ADDR, | ||
228 | .flags = 0, | ||
229 | .len = 1, | ||
230 | .buf = &offset, | ||
231 | }, { | ||
232 | .addr = DDC_ADDR, | ||
233 | .flags = I2C_M_RD, | ||
234 | .len = count, | ||
235 | .buf = buf, | ||
236 | } | ||
237 | }; | ||
238 | |||
239 | r = i2c_transfer(adapter, msgs, 2); | ||
240 | if (r == 2) | ||
241 | return 0; | ||
242 | |||
243 | if (r != -EAGAIN) | ||
244 | break; | ||
245 | } | ||
246 | |||
247 | return r < 0 ? r : -EIO; | ||
248 | } | ||
249 | |||
250 | static int panel_dvi_read_edid(struct omap_dss_device *dssdev, | ||
251 | u8 *edid, int len) | ||
252 | { | ||
253 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
254 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
255 | struct i2c_adapter *adapter; | ||
256 | int r, l, bytes_read; | ||
257 | |||
258 | mutex_lock(&ddata->lock); | ||
259 | |||
260 | if (pdata->i2c_bus_num == 0) { | ||
261 | r = -ENODEV; | ||
262 | goto err; | ||
263 | } | ||
264 | |||
265 | adapter = i2c_get_adapter(pdata->i2c_bus_num); | ||
266 | if (!adapter) { | ||
267 | dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", | ||
268 | pdata->i2c_bus_num); | ||
269 | r = -EINVAL; | ||
270 | goto err; | ||
271 | } | ||
272 | |||
273 | l = min(EDID_LENGTH, len); | ||
274 | r = panel_dvi_ddc_read(adapter, edid, l, 0); | ||
275 | if (r) | ||
276 | goto err; | ||
277 | |||
278 | bytes_read = l; | ||
279 | |||
280 | /* if there are extensions, read second block */ | ||
281 | if (len > EDID_LENGTH && edid[0x7e] > 0) { | ||
282 | l = min(EDID_LENGTH, len - EDID_LENGTH); | ||
283 | |||
284 | r = panel_dvi_ddc_read(adapter, edid + EDID_LENGTH, | ||
285 | l, EDID_LENGTH); | ||
286 | if (r) | ||
287 | goto err; | ||
288 | |||
289 | bytes_read += l; | ||
290 | } | ||
291 | |||
292 | mutex_unlock(&ddata->lock); | ||
293 | |||
294 | return bytes_read; | ||
295 | |||
296 | err: | ||
297 | mutex_unlock(&ddata->lock); | ||
298 | return r; | ||
299 | } | ||
300 | |||
301 | static bool panel_dvi_detect(struct omap_dss_device *dssdev) | ||
302 | { | ||
303 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
304 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
305 | struct i2c_adapter *adapter; | ||
306 | unsigned char out; | ||
307 | int r; | ||
308 | |||
309 | mutex_lock(&ddata->lock); | ||
310 | |||
311 | if (pdata->i2c_bus_num == 0) | ||
312 | goto out; | ||
313 | |||
314 | adapter = i2c_get_adapter(pdata->i2c_bus_num); | ||
315 | if (!adapter) | ||
316 | goto out; | ||
317 | |||
318 | r = panel_dvi_ddc_read(adapter, &out, 1, 0); | ||
319 | |||
320 | mutex_unlock(&ddata->lock); | ||
321 | |||
322 | return r == 0; | ||
323 | |||
324 | out: | ||
325 | mutex_unlock(&ddata->lock); | ||
326 | return true; | ||
327 | } | ||
328 | |||
329 | static struct omap_dss_driver panel_dvi_driver = { | ||
330 | .probe = panel_dvi_probe, | ||
331 | .remove = __exit_p(panel_dvi_remove), | ||
332 | |||
333 | .enable = panel_dvi_enable, | ||
334 | .disable = panel_dvi_disable, | ||
335 | .suspend = panel_dvi_suspend, | ||
336 | .resume = panel_dvi_resume, | ||
337 | |||
338 | .set_timings = panel_dvi_set_timings, | ||
339 | .get_timings = panel_dvi_get_timings, | ||
340 | .check_timings = panel_dvi_check_timings, | ||
341 | |||
342 | .read_edid = panel_dvi_read_edid, | ||
343 | .detect = panel_dvi_detect, | ||
344 | |||
345 | .driver = { | ||
346 | .name = "dvi", | ||
347 | .owner = THIS_MODULE, | ||
348 | }, | ||
349 | }; | ||
350 | |||
351 | static int __init panel_dvi_init(void) | ||
352 | { | ||
353 | return omap_dss_register_driver(&panel_dvi_driver); | ||
354 | } | ||
355 | |||
356 | static void __exit panel_dvi_exit(void) | ||
357 | { | ||
358 | omap_dss_unregister_driver(&panel_dvi_driver); | ||
359 | } | ||
360 | |||
361 | module_init(panel_dvi_init); | ||
362 | module_exit(panel_dvi_exit); | ||
363 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/video/omap-panel-dvi.h b/include/video/omap-panel-dvi.h new file mode 100644 index 000000000000..87ad567b4229 --- /dev/null +++ b/include/video/omap-panel-dvi.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Header for DVI output driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Inc | ||
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 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along with | ||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #ifndef __OMAP_PANEL_DVI_H | ||
21 | #define __OMAP_PANEL_DVI_H | ||
22 | |||
23 | struct omap_dss_device; | ||
24 | |||
25 | /** | ||
26 | * struct panel_dvi_platform_data - panel driver configuration data | ||
27 | * @platform_enable: platform specific panel enable function | ||
28 | * @platform_disable: platform specific panel disable function | ||
29 | * @i2c_bus_num: i2c bus id for the panel | ||
30 | */ | ||
31 | struct panel_dvi_platform_data { | ||
32 | int (*platform_enable)(struct omap_dss_device *dssdev); | ||
33 | void (*platform_disable)(struct omap_dss_device *dssdev); | ||
34 | u16 i2c_bus_num; | ||
35 | }; | ||
36 | |||
37 | #endif /* __OMAP_PANEL_DVI_H */ | ||