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/omapfb_dss.h>
21 static const struct omap_video_timings dvic_default_timings
= {
25 .pixelclock
= 23500000,
35 .vsync_level
= OMAPDSS_SIG_ACTIVE_HIGH
,
36 .hsync_level
= OMAPDSS_SIG_ACTIVE_HIGH
,
37 .data_pclk_edge
= OMAPDSS_DRIVE_SIG_RISING_EDGE
,
38 .de_level
= OMAPDSS_SIG_ACTIVE_HIGH
,
39 .sync_pclk_edge
= OMAPDSS_DRIVE_SIG_FALLING_EDGE
,
42 struct panel_drv_data
{
43 struct omap_dss_device dssdev
;
44 struct omap_dss_device
*in
;
46 struct omap_video_timings timings
;
48 struct i2c_adapter
*i2c_adapter
;
51 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
53 static int dvic_connect(struct omap_dss_device
*dssdev
)
55 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
56 struct omap_dss_device
*in
= ddata
->in
;
59 if (omapdss_device_is_connected(dssdev
))
62 r
= in
->ops
.dvi
->connect(in
, dssdev
);
69 static void dvic_disconnect(struct omap_dss_device
*dssdev
)
71 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
72 struct omap_dss_device
*in
= ddata
->in
;
74 if (!omapdss_device_is_connected(dssdev
))
77 in
->ops
.dvi
->disconnect(in
, dssdev
);
80 static int dvic_enable(struct omap_dss_device
*dssdev
)
82 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
83 struct omap_dss_device
*in
= ddata
->in
;
86 if (!omapdss_device_is_connected(dssdev
))
89 if (omapdss_device_is_enabled(dssdev
))
92 in
->ops
.dvi
->set_timings(in
, &ddata
->timings
);
94 r
= in
->ops
.dvi
->enable(in
);
98 dssdev
->state
= OMAP_DSS_DISPLAY_ACTIVE
;
103 static void dvic_disable(struct omap_dss_device
*dssdev
)
105 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
106 struct omap_dss_device
*in
= ddata
->in
;
108 if (!omapdss_device_is_enabled(dssdev
))
111 in
->ops
.dvi
->disable(in
);
113 dssdev
->state
= OMAP_DSS_DISPLAY_DISABLED
;
116 static void dvic_set_timings(struct omap_dss_device
*dssdev
,
117 struct omap_video_timings
*timings
)
119 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
120 struct omap_dss_device
*in
= ddata
->in
;
122 ddata
->timings
= *timings
;
123 dssdev
->panel
.timings
= *timings
;
125 in
->ops
.dvi
->set_timings(in
, timings
);
128 static void dvic_get_timings(struct omap_dss_device
*dssdev
,
129 struct omap_video_timings
*timings
)
131 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
133 *timings
= ddata
->timings
;
136 static int dvic_check_timings(struct omap_dss_device
*dssdev
,
137 struct omap_video_timings
*timings
)
139 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
140 struct omap_dss_device
*in
= ddata
->in
;
142 return in
->ops
.dvi
->check_timings(in
, timings
);
145 static int dvic_ddc_read(struct i2c_adapter
*adapter
,
146 unsigned char *buf
, u16 count
, u8 offset
)
150 for (retries
= 3; retries
> 0; retries
--) {
151 struct i2c_msg msgs
[] = {
165 r
= i2c_transfer(adapter
, msgs
, 2);
173 return r
< 0 ? r
: -EIO
;
176 static int dvic_read_edid(struct omap_dss_device
*dssdev
,
179 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
180 int r
, l
, bytes_read
;
182 if (!ddata
->i2c_adapter
)
185 l
= min(EDID_LENGTH
, len
);
186 r
= dvic_ddc_read(ddata
->i2c_adapter
, edid
, l
, 0);
192 /* if there are extensions, read second block */
193 if (len
> EDID_LENGTH
&& edid
[0x7e] > 0) {
194 l
= min(EDID_LENGTH
, len
- EDID_LENGTH
);
196 r
= dvic_ddc_read(ddata
->i2c_adapter
, edid
+ EDID_LENGTH
,
207 static bool dvic_detect(struct omap_dss_device
*dssdev
)
209 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
213 if (!ddata
->i2c_adapter
)
216 r
= dvic_ddc_read(ddata
->i2c_adapter
, &out
, 1, 0);
221 static struct omap_dss_driver dvic_driver
= {
222 .connect
= dvic_connect
,
223 .disconnect
= dvic_disconnect
,
225 .enable
= dvic_enable
,
226 .disable
= dvic_disable
,
228 .set_timings
= dvic_set_timings
,
229 .get_timings
= dvic_get_timings
,
230 .check_timings
= dvic_check_timings
,
232 .get_resolution
= omapdss_default_get_resolution
,
234 .read_edid
= dvic_read_edid
,
235 .detect
= dvic_detect
,
238 static int dvic_probe_of(struct platform_device
*pdev
)
240 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
241 struct device_node
*node
= pdev
->dev
.of_node
;
242 struct omap_dss_device
*in
;
243 struct device_node
*adapter_node
;
244 struct i2c_adapter
*adapter
;
246 in
= omapdss_of_find_source_for_first_ep(node
);
248 dev_err(&pdev
->dev
, "failed to find video source\n");
254 adapter_node
= of_parse_phandle(node
, "ddc-i2c-bus", 0);
256 adapter
= of_get_i2c_adapter_by_node(adapter_node
);
257 if (adapter
== NULL
) {
258 dev_err(&pdev
->dev
, "failed to parse ddc-i2c-bus\n");
259 omap_dss_put_device(ddata
->in
);
260 return -EPROBE_DEFER
;
263 ddata
->i2c_adapter
= adapter
;
269 static int dvic_probe(struct platform_device
*pdev
)
271 struct panel_drv_data
*ddata
;
272 struct omap_dss_device
*dssdev
;
275 if (!pdev
->dev
.of_node
)
278 ddata
= devm_kzalloc(&pdev
->dev
, sizeof(*ddata
), GFP_KERNEL
);
282 platform_set_drvdata(pdev
, ddata
);
284 r
= dvic_probe_of(pdev
);
288 ddata
->timings
= dvic_default_timings
;
290 dssdev
= &ddata
->dssdev
;
291 dssdev
->driver
= &dvic_driver
;
292 dssdev
->dev
= &pdev
->dev
;
293 dssdev
->type
= OMAP_DISPLAY_TYPE_DVI
;
294 dssdev
->owner
= THIS_MODULE
;
295 dssdev
->panel
.timings
= dvic_default_timings
;
297 r
= omapdss_register_display(dssdev
);
299 dev_err(&pdev
->dev
, "Failed to register panel\n");
306 omap_dss_put_device(ddata
->in
);
308 i2c_put_adapter(ddata
->i2c_adapter
);
313 static int __exit
dvic_remove(struct platform_device
*pdev
)
315 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
316 struct omap_dss_device
*dssdev
= &ddata
->dssdev
;
317 struct omap_dss_device
*in
= ddata
->in
;
319 omapdss_unregister_display(&ddata
->dssdev
);
321 dvic_disable(dssdev
);
322 dvic_disconnect(dssdev
);
324 omap_dss_put_device(in
);
326 i2c_put_adapter(ddata
->i2c_adapter
);
331 static const struct of_device_id dvic_of_match
[] = {
332 { .compatible
= "omapdss,dvi-connector", },
336 MODULE_DEVICE_TABLE(of
, dvic_of_match
);
338 static struct platform_driver dvi_connector_driver
= {
340 .remove
= __exit_p(dvic_remove
),
342 .name
= "connector-dvi",
343 .of_match_table
= dvic_of_match
,
344 .suppress_bind_attrs
= true,
348 module_platform_driver(dvi_connector_driver
);
350 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
351 MODULE_DESCRIPTION("Generic DVI Connector driver");
352 MODULE_LICENSE("GPL");