• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Generic DVI Connector driver
3  *
4  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
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/gpio/consumer.h>
13 #include <linux/i2c.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17 
18 #include <drm/drm_edid.h>
19 
20 #include "../dss/omapdss.h"
21 
22 static const struct videomode dvic_default_vm = {
23 	.hactive	= 640,
24 	.vactive	= 480,
25 
26 	.pixelclock	= 23500000,
27 
28 	.hfront_porch	= 48,
29 	.hsync_len	= 32,
30 	.hback_porch	= 80,
31 
32 	.vfront_porch	= 3,
33 	.vsync_len	= 4,
34 	.vback_porch	= 7,
35 
36 	.flags		= DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH |
37 			  DISPLAY_FLAGS_SYNC_NEGEDGE | DISPLAY_FLAGS_DE_HIGH |
38 			  DISPLAY_FLAGS_PIXDATA_POSEDGE,
39 };
40 
41 struct panel_drv_data {
42 	struct omap_dss_device dssdev;
43 	struct omap_dss_device *in;
44 
45 	struct videomode vm;
46 
47 	struct i2c_adapter *i2c_adapter;
48 
49 	struct gpio_desc *hpd_gpio;
50 
51 	void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
52 	void *hpd_cb_data;
53 	bool hpd_enabled;
54 	/* mutex for hpd fields above */
55 	struct mutex hpd_lock;
56 };
57 
58 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
59 
dvic_connect(struct omap_dss_device * dssdev)60 static int dvic_connect(struct omap_dss_device *dssdev)
61 {
62 	struct panel_drv_data *ddata = to_panel_data(dssdev);
63 	struct omap_dss_device *in;
64 	int r;
65 
66 	if (omapdss_device_is_connected(dssdev))
67 		return 0;
68 
69 	in = omapdss_of_find_source_for_first_ep(dssdev->dev->of_node);
70 	if (IS_ERR(in)) {
71 		dev_err(dssdev->dev, "failed to find video source\n");
72 		return PTR_ERR(in);
73 	}
74 
75 	r = in->ops.dvi->connect(in, dssdev);
76 	if (r) {
77 		omap_dss_put_device(in);
78 		return r;
79 	}
80 
81 	ddata->in = in;
82 	return 0;
83 }
84 
dvic_disconnect(struct omap_dss_device * dssdev)85 static void dvic_disconnect(struct omap_dss_device *dssdev)
86 {
87 	struct panel_drv_data *ddata = to_panel_data(dssdev);
88 	struct omap_dss_device *in = ddata->in;
89 
90 	if (!omapdss_device_is_connected(dssdev))
91 		return;
92 
93 	in->ops.dvi->disconnect(in, dssdev);
94 
95 	omap_dss_put_device(in);
96 	ddata->in = NULL;
97 }
98 
dvic_enable(struct omap_dss_device * dssdev)99 static int dvic_enable(struct omap_dss_device *dssdev)
100 {
101 	struct panel_drv_data *ddata = to_panel_data(dssdev);
102 	struct omap_dss_device *in = ddata->in;
103 	int r;
104 
105 	if (!omapdss_device_is_connected(dssdev))
106 		return -ENODEV;
107 
108 	if (omapdss_device_is_enabled(dssdev))
109 		return 0;
110 
111 	in->ops.dvi->set_timings(in, &ddata->vm);
112 
113 	r = in->ops.dvi->enable(in);
114 	if (r)
115 		return r;
116 
117 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
118 
119 	return 0;
120 }
121 
dvic_disable(struct omap_dss_device * dssdev)122 static void dvic_disable(struct omap_dss_device *dssdev)
123 {
124 	struct panel_drv_data *ddata = to_panel_data(dssdev);
125 	struct omap_dss_device *in = ddata->in;
126 
127 	if (!omapdss_device_is_enabled(dssdev))
128 		return;
129 
130 	in->ops.dvi->disable(in);
131 
132 	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
133 }
134 
dvic_set_timings(struct omap_dss_device * dssdev,struct videomode * vm)135 static void dvic_set_timings(struct omap_dss_device *dssdev,
136 			     struct videomode *vm)
137 {
138 	struct panel_drv_data *ddata = to_panel_data(dssdev);
139 	struct omap_dss_device *in = ddata->in;
140 
141 	ddata->vm = *vm;
142 	dssdev->panel.vm = *vm;
143 
144 	in->ops.dvi->set_timings(in, vm);
145 }
146 
dvic_get_timings(struct omap_dss_device * dssdev,struct videomode * vm)147 static void dvic_get_timings(struct omap_dss_device *dssdev,
148 			     struct videomode *vm)
149 {
150 	struct panel_drv_data *ddata = to_panel_data(dssdev);
151 
152 	*vm = ddata->vm;
153 }
154 
dvic_check_timings(struct omap_dss_device * dssdev,struct videomode * vm)155 static int dvic_check_timings(struct omap_dss_device *dssdev,
156 			      struct videomode *vm)
157 {
158 	struct panel_drv_data *ddata = to_panel_data(dssdev);
159 	struct omap_dss_device *in = ddata->in;
160 
161 	return in->ops.dvi->check_timings(in, vm);
162 }
163 
dvic_ddc_read(struct i2c_adapter * adapter,unsigned char * buf,u16 count,u8 offset)164 static int dvic_ddc_read(struct i2c_adapter *adapter,
165 		unsigned char *buf, u16 count, u8 offset)
166 {
167 	int r, retries;
168 
169 	for (retries = 3; retries > 0; retries--) {
170 		struct i2c_msg msgs[] = {
171 			{
172 				.addr   = DDC_ADDR,
173 				.flags  = 0,
174 				.len    = 1,
175 				.buf    = &offset,
176 			}, {
177 				.addr   = DDC_ADDR,
178 				.flags  = I2C_M_RD,
179 				.len    = count,
180 				.buf    = buf,
181 			}
182 		};
183 
184 		r = i2c_transfer(adapter, msgs, 2);
185 		if (r == 2)
186 			return 0;
187 
188 		if (r != -EAGAIN)
189 			break;
190 	}
191 
192 	return r < 0 ? r : -EIO;
193 }
194 
dvic_read_edid(struct omap_dss_device * dssdev,u8 * edid,int len)195 static int dvic_read_edid(struct omap_dss_device *dssdev,
196 		u8 *edid, int len)
197 {
198 	struct panel_drv_data *ddata = to_panel_data(dssdev);
199 	int r, l, bytes_read;
200 
201 	if (ddata->hpd_gpio && !gpiod_get_value_cansleep(ddata->hpd_gpio))
202 		return -ENODEV;
203 
204 	if (!ddata->i2c_adapter)
205 		return -ENODEV;
206 
207 	l = min(EDID_LENGTH, len);
208 	r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
209 	if (r)
210 		return r;
211 
212 	bytes_read = l;
213 
214 	/* if there are extensions, read second block */
215 	if (len > EDID_LENGTH && edid[0x7e] > 0) {
216 		l = min(EDID_LENGTH, len - EDID_LENGTH);
217 
218 		r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
219 				l, EDID_LENGTH);
220 		if (r)
221 			return r;
222 
223 		bytes_read += l;
224 	}
225 
226 	return bytes_read;
227 }
228 
dvic_detect(struct omap_dss_device * dssdev)229 static bool dvic_detect(struct omap_dss_device *dssdev)
230 {
231 	struct panel_drv_data *ddata = to_panel_data(dssdev);
232 	unsigned char out;
233 	int r;
234 
235 	if (ddata->hpd_gpio)
236 		return gpiod_get_value_cansleep(ddata->hpd_gpio);
237 
238 	if (!ddata->i2c_adapter)
239 		return true;
240 
241 	r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
242 
243 	return r == 0;
244 }
245 
dvic_register_hpd_cb(struct omap_dss_device * dssdev,void (* cb)(void * cb_data,enum drm_connector_status status),void * cb_data)246 static int dvic_register_hpd_cb(struct omap_dss_device *dssdev,
247 				 void (*cb)(void *cb_data,
248 					    enum drm_connector_status status),
249 				 void *cb_data)
250 {
251 	struct panel_drv_data *ddata = to_panel_data(dssdev);
252 
253 	if (!ddata->hpd_gpio)
254 		return -ENOTSUPP;
255 
256 	mutex_lock(&ddata->hpd_lock);
257 	ddata->hpd_cb = cb;
258 	ddata->hpd_cb_data = cb_data;
259 	mutex_unlock(&ddata->hpd_lock);
260 	return 0;
261 }
262 
dvic_unregister_hpd_cb(struct omap_dss_device * dssdev)263 static void dvic_unregister_hpd_cb(struct omap_dss_device *dssdev)
264 {
265 	struct panel_drv_data *ddata = to_panel_data(dssdev);
266 
267 	if (!ddata->hpd_gpio)
268 		return;
269 
270 	mutex_lock(&ddata->hpd_lock);
271 	ddata->hpd_cb = NULL;
272 	ddata->hpd_cb_data = NULL;
273 	mutex_unlock(&ddata->hpd_lock);
274 }
275 
dvic_enable_hpd(struct omap_dss_device * dssdev)276 static void dvic_enable_hpd(struct omap_dss_device *dssdev)
277 {
278 	struct panel_drv_data *ddata = to_panel_data(dssdev);
279 
280 	if (!ddata->hpd_gpio)
281 		return;
282 
283 	mutex_lock(&ddata->hpd_lock);
284 	ddata->hpd_enabled = true;
285 	mutex_unlock(&ddata->hpd_lock);
286 }
287 
dvic_disable_hpd(struct omap_dss_device * dssdev)288 static void dvic_disable_hpd(struct omap_dss_device *dssdev)
289 {
290 	struct panel_drv_data *ddata = to_panel_data(dssdev);
291 
292 	if (!ddata->hpd_gpio)
293 		return;
294 
295 	mutex_lock(&ddata->hpd_lock);
296 	ddata->hpd_enabled = false;
297 	mutex_unlock(&ddata->hpd_lock);
298 }
299 
300 static struct omap_dss_driver dvic_driver = {
301 	.connect	= dvic_connect,
302 	.disconnect	= dvic_disconnect,
303 
304 	.enable		= dvic_enable,
305 	.disable	= dvic_disable,
306 
307 	.set_timings	= dvic_set_timings,
308 	.get_timings	= dvic_get_timings,
309 	.check_timings	= dvic_check_timings,
310 
311 	.read_edid	= dvic_read_edid,
312 	.detect		= dvic_detect,
313 
314 	.register_hpd_cb	= dvic_register_hpd_cb,
315 	.unregister_hpd_cb	= dvic_unregister_hpd_cb,
316 	.enable_hpd		= dvic_enable_hpd,
317 	.disable_hpd		= dvic_disable_hpd,
318 };
319 
dvic_hpd_isr(int irq,void * data)320 static irqreturn_t dvic_hpd_isr(int irq, void *data)
321 {
322 	struct panel_drv_data *ddata = data;
323 
324 	mutex_lock(&ddata->hpd_lock);
325 	if (ddata->hpd_enabled && ddata->hpd_cb) {
326 		enum drm_connector_status status;
327 
328 		if (dvic_detect(&ddata->dssdev))
329 			status = connector_status_connected;
330 		else
331 			status = connector_status_disconnected;
332 
333 		ddata->hpd_cb(ddata->hpd_cb_data, status);
334 	}
335 	mutex_unlock(&ddata->hpd_lock);
336 
337 	return IRQ_HANDLED;
338 }
339 
dvic_probe_of(struct platform_device * pdev)340 static int dvic_probe_of(struct platform_device *pdev)
341 {
342 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
343 	struct device_node *node = pdev->dev.of_node;
344 	struct device_node *adapter_node;
345 	struct i2c_adapter *adapter;
346 	struct gpio_desc *gpio;
347 	int r;
348 
349 	gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN);
350 	if (IS_ERR(gpio)) {
351 		dev_err(&pdev->dev, "failed to parse HPD gpio\n");
352 		return PTR_ERR(gpio);
353 	}
354 
355 	ddata->hpd_gpio = gpio;
356 
357 	mutex_init(&ddata->hpd_lock);
358 
359 	if (ddata->hpd_gpio) {
360 		r = devm_request_threaded_irq(&pdev->dev,
361 			gpiod_to_irq(ddata->hpd_gpio), NULL, dvic_hpd_isr,
362 			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
363 			"DVI HPD", ddata);
364 		if (r)
365 			return r;
366 	}
367 
368 	adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
369 	if (adapter_node) {
370 		adapter = of_get_i2c_adapter_by_node(adapter_node);
371 		of_node_put(adapter_node);
372 		if (adapter == NULL) {
373 			dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
374 			return -EPROBE_DEFER;
375 		}
376 
377 		ddata->i2c_adapter = adapter;
378 	}
379 
380 	return 0;
381 }
382 
dvic_probe(struct platform_device * pdev)383 static int dvic_probe(struct platform_device *pdev)
384 {
385 	struct panel_drv_data *ddata;
386 	struct omap_dss_device *dssdev;
387 	int r;
388 
389 	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
390 	if (!ddata)
391 		return -ENOMEM;
392 
393 	platform_set_drvdata(pdev, ddata);
394 
395 	r = dvic_probe_of(pdev);
396 	if (r)
397 		return r;
398 
399 	ddata->vm = dvic_default_vm;
400 
401 	dssdev = &ddata->dssdev;
402 	dssdev->driver = &dvic_driver;
403 	dssdev->dev = &pdev->dev;
404 	dssdev->type = OMAP_DISPLAY_TYPE_DVI;
405 	dssdev->owner = THIS_MODULE;
406 	dssdev->panel.vm = dvic_default_vm;
407 
408 	r = omapdss_register_display(dssdev);
409 	if (r) {
410 		dev_err(&pdev->dev, "Failed to register panel\n");
411 		goto err_reg;
412 	}
413 
414 	return 0;
415 
416 err_reg:
417 	i2c_put_adapter(ddata->i2c_adapter);
418 	mutex_destroy(&ddata->hpd_lock);
419 
420 	return r;
421 }
422 
dvic_remove(struct platform_device * pdev)423 static int __exit dvic_remove(struct platform_device *pdev)
424 {
425 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
426 	struct omap_dss_device *dssdev = &ddata->dssdev;
427 
428 	omapdss_unregister_display(&ddata->dssdev);
429 
430 	dvic_disable(dssdev);
431 	dvic_disconnect(dssdev);
432 
433 	i2c_put_adapter(ddata->i2c_adapter);
434 
435 	mutex_destroy(&ddata->hpd_lock);
436 
437 	return 0;
438 }
439 
440 static const struct of_device_id dvic_of_match[] = {
441 	{ .compatible = "omapdss,dvi-connector", },
442 	{},
443 };
444 
445 MODULE_DEVICE_TABLE(of, dvic_of_match);
446 
447 static struct platform_driver dvi_connector_driver = {
448 	.probe	= dvic_probe,
449 	.remove	= __exit_p(dvic_remove),
450 	.driver	= {
451 		.name	= "connector-dvi",
452 		.of_match_table = dvic_of_match,
453 		.suppress_bind_attrs = true,
454 	},
455 };
456 
457 module_platform_driver(dvi_connector_driver);
458 
459 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
460 MODULE_DESCRIPTION("Generic DVI Connector driver");
461 MODULE_LICENSE("GPL");
462