2 * Generic DVI Connector driver
4 * Copyright (C) 2013 Texas Instruments
5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
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.
12 #include <linux/i2c.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
17 #include <drm/drm_edid.h>
19 #include <video/omapdss.h>
20 #include <video/omap-panel-data.h>
22 static const struct omap_video_timings dvic_default_timings
= {
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
,
43 struct panel_drv_data
{
44 struct omap_dss_device dssdev
;
45 struct omap_dss_device
*in
;
47 struct omap_video_timings timings
;
49 struct i2c_adapter
*i2c_adapter
;
52 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
54 static int dvic_connect(struct omap_dss_device
*dssdev
)
56 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
57 struct omap_dss_device
*in
= ddata
->in
;
60 if (omapdss_device_is_connected(dssdev
))
63 r
= in
->ops
.dvi
->connect(in
, dssdev
);
70 static void dvic_disconnect(struct omap_dss_device
*dssdev
)
72 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
73 struct omap_dss_device
*in
= ddata
->in
;
75 if (!omapdss_device_is_connected(dssdev
))
78 in
->ops
.dvi
->disconnect(in
, dssdev
);
81 static int dvic_enable(struct omap_dss_device
*dssdev
)
83 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
84 struct omap_dss_device
*in
= ddata
->in
;
87 if (!omapdss_device_is_connected(dssdev
))
90 if (omapdss_device_is_enabled(dssdev
))
93 in
->ops
.dvi
->set_timings(in
, &ddata
->timings
);
95 r
= in
->ops
.dvi
->enable(in
);
99 dssdev
->state
= OMAP_DSS_DISPLAY_ACTIVE
;
104 static void dvic_disable(struct omap_dss_device
*dssdev
)
106 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
107 struct omap_dss_device
*in
= ddata
->in
;
109 if (!omapdss_device_is_enabled(dssdev
))
112 in
->ops
.dvi
->disable(in
);
114 dssdev
->state
= OMAP_DSS_DISPLAY_DISABLED
;
117 static void dvic_set_timings(struct omap_dss_device
*dssdev
,
118 struct omap_video_timings
*timings
)
120 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
121 struct omap_dss_device
*in
= ddata
->in
;
123 ddata
->timings
= *timings
;
124 dssdev
->panel
.timings
= *timings
;
126 in
->ops
.dvi
->set_timings(in
, timings
);
129 static void dvic_get_timings(struct omap_dss_device
*dssdev
,
130 struct omap_video_timings
*timings
)
132 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
134 *timings
= ddata
->timings
;
137 static int dvic_check_timings(struct omap_dss_device
*dssdev
,
138 struct omap_video_timings
*timings
)
140 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
141 struct omap_dss_device
*in
= ddata
->in
;
143 return in
->ops
.dvi
->check_timings(in
, timings
);
146 static int dvic_ddc_read(struct i2c_adapter
*adapter
,
147 unsigned char *buf
, u16 count
, u8 offset
)
151 for (retries
= 3; retries
> 0; retries
--) {
152 struct i2c_msg msgs
[] = {
166 r
= i2c_transfer(adapter
, msgs
, 2);
174 return r
< 0 ? r
: -EIO
;
177 static int dvic_read_edid(struct omap_dss_device
*dssdev
,
180 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
181 int r
, l
, bytes_read
;
183 if (!ddata
->i2c_adapter
)
186 l
= min(EDID_LENGTH
, len
);
187 r
= dvic_ddc_read(ddata
->i2c_adapter
, edid
, l
, 0);
193 /* if there are extensions, read second block */
194 if (len
> EDID_LENGTH
&& edid
[0x7e] > 0) {
195 l
= min(EDID_LENGTH
, len
- EDID_LENGTH
);
197 r
= dvic_ddc_read(ddata
->i2c_adapter
, edid
+ EDID_LENGTH
,
208 static bool dvic_detect(struct omap_dss_device
*dssdev
)
210 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
214 if (!ddata
->i2c_adapter
)
217 r
= dvic_ddc_read(ddata
->i2c_adapter
, &out
, 1, 0);
222 static struct omap_dss_driver dvic_driver
= {
223 .connect
= dvic_connect
,
224 .disconnect
= dvic_disconnect
,
226 .enable
= dvic_enable
,
227 .disable
= dvic_disable
,
229 .set_timings
= dvic_set_timings
,
230 .get_timings
= dvic_get_timings
,
231 .check_timings
= dvic_check_timings
,
233 .get_resolution
= omapdss_default_get_resolution
,
235 .read_edid
= dvic_read_edid
,
236 .detect
= dvic_detect
,
239 static int dvic_probe_pdata(struct platform_device
*pdev
)
241 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
242 struct connector_dvi_platform_data
*pdata
;
243 struct omap_dss_device
*in
, *dssdev
;
246 pdata
= dev_get_platdata(&pdev
->dev
);
247 i2c_bus_num
= pdata
->i2c_bus_num
;
249 if (i2c_bus_num
!= -1) {
250 struct i2c_adapter
*adapter
;
252 adapter
= i2c_get_adapter(i2c_bus_num
);
255 "Failed to get I2C adapter, bus %d\n",
257 return -EPROBE_DEFER
;
260 ddata
->i2c_adapter
= adapter
;
263 in
= omap_dss_find_output(pdata
->source
);
265 if (ddata
->i2c_adapter
)
266 i2c_put_adapter(ddata
->i2c_adapter
);
268 dev_err(&pdev
->dev
, "Failed to find video source\n");
269 return -EPROBE_DEFER
;
274 dssdev
= &ddata
->dssdev
;
275 dssdev
->name
= pdata
->name
;
280 static int dvic_probe(struct platform_device
*pdev
)
282 struct panel_drv_data
*ddata
;
283 struct omap_dss_device
*dssdev
;
286 ddata
= devm_kzalloc(&pdev
->dev
, sizeof(*ddata
), GFP_KERNEL
);
290 platform_set_drvdata(pdev
, ddata
);
292 if (dev_get_platdata(&pdev
->dev
)) {
293 r
= dvic_probe_pdata(pdev
);
300 ddata
->timings
= dvic_default_timings
;
302 dssdev
= &ddata
->dssdev
;
303 dssdev
->driver
= &dvic_driver
;
304 dssdev
->dev
= &pdev
->dev
;
305 dssdev
->type
= OMAP_DISPLAY_TYPE_DVI
;
306 dssdev
->owner
= THIS_MODULE
;
307 dssdev
->panel
.timings
= dvic_default_timings
;
309 r
= omapdss_register_display(dssdev
);
311 dev_err(&pdev
->dev
, "Failed to register panel\n");
318 omap_dss_put_device(ddata
->in
);
320 if (ddata
->i2c_adapter
)
321 i2c_put_adapter(ddata
->i2c_adapter
);
326 static int __exit
dvic_remove(struct platform_device
*pdev
)
328 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
329 struct omap_dss_device
*dssdev
= &ddata
->dssdev
;
330 struct omap_dss_device
*in
= ddata
->in
;
332 omapdss_unregister_display(&ddata
->dssdev
);
334 dvic_disable(dssdev
);
335 dvic_disconnect(dssdev
);
337 omap_dss_put_device(in
);
339 if (ddata
->i2c_adapter
)
340 i2c_put_adapter(ddata
->i2c_adapter
);
345 static struct platform_driver dvi_connector_driver
= {
347 .remove
= __exit_p(dvic_remove
),
349 .name
= "connector-dvi",
350 .owner
= THIS_MODULE
,
354 module_platform_driver(dvi_connector_driver
);
356 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
357 MODULE_DESCRIPTION("Generic DVI Connector driver");
358 MODULE_LICENSE("GPL");