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>
18 #include <video/omap-panel-data.h>
20 #include "../dss/omapdss.h"
22 static const struct videomode dvic_default_vm
= {
26 .pixelclock
= 23500000,
36 .flags
= DISPLAY_FLAGS_HSYNC_HIGH
| DISPLAY_FLAGS_VSYNC_HIGH
|
37 DISPLAY_FLAGS_SYNC_NEGEDGE
| DISPLAY_FLAGS_DE_HIGH
|
38 DISPLAY_FLAGS_PIXDATA_POSEDGE
,
41 struct panel_drv_data
{
42 struct omap_dss_device dssdev
;
43 struct omap_dss_device
*in
;
47 struct i2c_adapter
*i2c_adapter
;
50 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
52 static int dvic_connect(struct omap_dss_device
*dssdev
)
54 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
55 struct omap_dss_device
*in
= ddata
->in
;
58 if (omapdss_device_is_connected(dssdev
))
61 r
= in
->ops
.dvi
->connect(in
, dssdev
);
68 static void dvic_disconnect(struct omap_dss_device
*dssdev
)
70 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
71 struct omap_dss_device
*in
= ddata
->in
;
73 if (!omapdss_device_is_connected(dssdev
))
76 in
->ops
.dvi
->disconnect(in
, dssdev
);
79 static int dvic_enable(struct omap_dss_device
*dssdev
)
81 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
82 struct omap_dss_device
*in
= ddata
->in
;
85 if (!omapdss_device_is_connected(dssdev
))
88 if (omapdss_device_is_enabled(dssdev
))
91 in
->ops
.dvi
->set_timings(in
, &ddata
->vm
);
93 r
= in
->ops
.dvi
->enable(in
);
97 dssdev
->state
= OMAP_DSS_DISPLAY_ACTIVE
;
102 static void dvic_disable(struct omap_dss_device
*dssdev
)
104 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
105 struct omap_dss_device
*in
= ddata
->in
;
107 if (!omapdss_device_is_enabled(dssdev
))
110 in
->ops
.dvi
->disable(in
);
112 dssdev
->state
= OMAP_DSS_DISPLAY_DISABLED
;
115 static void dvic_set_timings(struct omap_dss_device
*dssdev
,
116 struct videomode
*vm
)
118 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
119 struct omap_dss_device
*in
= ddata
->in
;
122 dssdev
->panel
.vm
= *vm
;
124 in
->ops
.dvi
->set_timings(in
, vm
);
127 static void dvic_get_timings(struct omap_dss_device
*dssdev
,
128 struct videomode
*vm
)
130 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
135 static int dvic_check_timings(struct omap_dss_device
*dssdev
,
136 struct videomode
*vm
)
138 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
139 struct omap_dss_device
*in
= ddata
->in
;
141 return in
->ops
.dvi
->check_timings(in
, vm
);
144 static int dvic_ddc_read(struct i2c_adapter
*adapter
,
145 unsigned char *buf
, u16 count
, u8 offset
)
149 for (retries
= 3; retries
> 0; retries
--) {
150 struct i2c_msg msgs
[] = {
164 r
= i2c_transfer(adapter
, msgs
, 2);
172 return r
< 0 ? r
: -EIO
;
175 static int dvic_read_edid(struct omap_dss_device
*dssdev
,
178 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
179 int r
, l
, bytes_read
;
181 if (!ddata
->i2c_adapter
)
184 l
= min(EDID_LENGTH
, len
);
185 r
= dvic_ddc_read(ddata
->i2c_adapter
, edid
, l
, 0);
191 /* if there are extensions, read second block */
192 if (len
> EDID_LENGTH
&& edid
[0x7e] > 0) {
193 l
= min(EDID_LENGTH
, len
- EDID_LENGTH
);
195 r
= dvic_ddc_read(ddata
->i2c_adapter
, edid
+ EDID_LENGTH
,
206 static bool dvic_detect(struct omap_dss_device
*dssdev
)
208 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
212 if (!ddata
->i2c_adapter
)
215 r
= dvic_ddc_read(ddata
->i2c_adapter
, &out
, 1, 0);
220 static struct omap_dss_driver dvic_driver
= {
221 .connect
= dvic_connect
,
222 .disconnect
= dvic_disconnect
,
224 .enable
= dvic_enable
,
225 .disable
= dvic_disable
,
227 .set_timings
= dvic_set_timings
,
228 .get_timings
= dvic_get_timings
,
229 .check_timings
= dvic_check_timings
,
231 .get_resolution
= omapdss_default_get_resolution
,
233 .read_edid
= dvic_read_edid
,
234 .detect
= dvic_detect
,
237 static int dvic_probe_of(struct platform_device
*pdev
)
239 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
240 struct device_node
*node
= pdev
->dev
.of_node
;
241 struct omap_dss_device
*in
;
242 struct device_node
*adapter_node
;
243 struct i2c_adapter
*adapter
;
245 in
= omapdss_of_find_source_for_first_ep(node
);
247 dev_err(&pdev
->dev
, "failed to find video source\n");
253 adapter_node
= of_parse_phandle(node
, "ddc-i2c-bus", 0);
255 adapter
= of_get_i2c_adapter_by_node(adapter_node
);
256 of_node_put(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 ddata
= devm_kzalloc(&pdev
->dev
, sizeof(*ddata
), GFP_KERNEL
);
279 platform_set_drvdata(pdev
, ddata
);
281 if (!pdev
->dev
.of_node
)
284 r
= dvic_probe_of(pdev
);
288 ddata
->vm
= dvic_default_vm
;
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
.vm
= dvic_default_vm
;
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");