]> Git Repo - linux.git/blob - drivers/gpu/drm/omapdrm/displays/connector-dvi.c
Merge tag 'char-misc-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregk...
[linux.git] / drivers / gpu / drm / omapdrm / displays / connector-dvi.c
1 /*
2  * Generic DVI Connector driver
3  *
4  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
5  * Author: Tomi Valkeinen <[email protected]>
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 struct panel_drv_data {
23         struct omap_dss_device dssdev;
24
25         struct i2c_adapter *i2c_adapter;
26
27         struct gpio_desc *hpd_gpio;
28
29         void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
30         void *hpd_cb_data;
31         bool hpd_enabled;
32         /* mutex for hpd fields above */
33         struct mutex hpd_lock;
34 };
35
36 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
37
38 static int dvic_connect(struct omap_dss_device *src,
39                         struct omap_dss_device *dst)
40 {
41         return 0;
42 }
43
44 static void dvic_disconnect(struct omap_dss_device *src,
45                             struct omap_dss_device *dst)
46 {
47 }
48
49 static int dvic_enable(struct omap_dss_device *dssdev)
50 {
51         struct omap_dss_device *src = dssdev->src;
52         int r;
53
54         if (!omapdss_device_is_connected(dssdev))
55                 return -ENODEV;
56
57         if (omapdss_device_is_enabled(dssdev))
58                 return 0;
59
60         r = src->ops->enable(src);
61         if (r)
62                 return r;
63
64         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
65
66         return 0;
67 }
68
69 static void dvic_disable(struct omap_dss_device *dssdev)
70 {
71         struct omap_dss_device *src = dssdev->src;
72
73         if (!omapdss_device_is_enabled(dssdev))
74                 return;
75
76         src->ops->disable(src);
77
78         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
79 }
80
81 static int dvic_ddc_read(struct i2c_adapter *adapter,
82                 unsigned char *buf, u16 count, u8 offset)
83 {
84         int r, retries;
85
86         for (retries = 3; retries > 0; retries--) {
87                 struct i2c_msg msgs[] = {
88                         {
89                                 .addr   = DDC_ADDR,
90                                 .flags  = 0,
91                                 .len    = 1,
92                                 .buf    = &offset,
93                         }, {
94                                 .addr   = DDC_ADDR,
95                                 .flags  = I2C_M_RD,
96                                 .len    = count,
97                                 .buf    = buf,
98                         }
99                 };
100
101                 r = i2c_transfer(adapter, msgs, 2);
102                 if (r == 2)
103                         return 0;
104
105                 if (r != -EAGAIN)
106                         break;
107         }
108
109         return r < 0 ? r : -EIO;
110 }
111
112 static int dvic_read_edid(struct omap_dss_device *dssdev,
113                 u8 *edid, int len)
114 {
115         struct panel_drv_data *ddata = to_panel_data(dssdev);
116         int r, l, bytes_read;
117
118         l = min(EDID_LENGTH, len);
119         r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
120         if (r)
121                 return r;
122
123         bytes_read = l;
124
125         /* if there are extensions, read second block */
126         if (len > EDID_LENGTH && edid[0x7e] > 0) {
127                 l = min(EDID_LENGTH, len - EDID_LENGTH);
128
129                 r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
130                                 l, EDID_LENGTH);
131                 if (r)
132                         return r;
133
134                 bytes_read += l;
135         }
136
137         return bytes_read;
138 }
139
140 static bool dvic_detect(struct omap_dss_device *dssdev)
141 {
142         struct panel_drv_data *ddata = to_panel_data(dssdev);
143         unsigned char out;
144         int r;
145
146         if (ddata->hpd_gpio)
147                 return gpiod_get_value_cansleep(ddata->hpd_gpio);
148
149         if (!ddata->i2c_adapter)
150                 return true;
151
152         r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
153
154         return r == 0;
155 }
156
157 static void dvic_register_hpd_cb(struct omap_dss_device *dssdev,
158                                  void (*cb)(void *cb_data,
159                                             enum drm_connector_status status),
160                                  void *cb_data)
161 {
162         struct panel_drv_data *ddata = to_panel_data(dssdev);
163
164         mutex_lock(&ddata->hpd_lock);
165         ddata->hpd_cb = cb;
166         ddata->hpd_cb_data = cb_data;
167         mutex_unlock(&ddata->hpd_lock);
168 }
169
170 static void dvic_unregister_hpd_cb(struct omap_dss_device *dssdev)
171 {
172         struct panel_drv_data *ddata = to_panel_data(dssdev);
173
174         mutex_lock(&ddata->hpd_lock);
175         ddata->hpd_cb = NULL;
176         ddata->hpd_cb_data = NULL;
177         mutex_unlock(&ddata->hpd_lock);
178 }
179
180 static const struct omap_dss_device_ops dvic_ops = {
181         .connect        = dvic_connect,
182         .disconnect     = dvic_disconnect,
183
184         .enable         = dvic_enable,
185         .disable        = dvic_disable,
186
187         .read_edid      = dvic_read_edid,
188         .detect         = dvic_detect,
189
190         .register_hpd_cb        = dvic_register_hpd_cb,
191         .unregister_hpd_cb      = dvic_unregister_hpd_cb,
192 };
193
194 static irqreturn_t dvic_hpd_isr(int irq, void *data)
195 {
196         struct panel_drv_data *ddata = data;
197
198         mutex_lock(&ddata->hpd_lock);
199         if (ddata->hpd_enabled && ddata->hpd_cb) {
200                 enum drm_connector_status status;
201
202                 if (dvic_detect(&ddata->dssdev))
203                         status = connector_status_connected;
204                 else
205                         status = connector_status_disconnected;
206
207                 ddata->hpd_cb(ddata->hpd_cb_data, status);
208         }
209         mutex_unlock(&ddata->hpd_lock);
210
211         return IRQ_HANDLED;
212 }
213
214 static int dvic_probe_of(struct platform_device *pdev)
215 {
216         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
217         struct device_node *node = pdev->dev.of_node;
218         struct device_node *adapter_node;
219         struct i2c_adapter *adapter;
220         struct gpio_desc *gpio;
221         int r;
222
223         gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN);
224         if (IS_ERR(gpio)) {
225                 dev_err(&pdev->dev, "failed to parse HPD gpio\n");
226                 return PTR_ERR(gpio);
227         }
228
229         ddata->hpd_gpio = gpio;
230
231         mutex_init(&ddata->hpd_lock);
232
233         if (ddata->hpd_gpio) {
234                 r = devm_request_threaded_irq(&pdev->dev,
235                         gpiod_to_irq(ddata->hpd_gpio), NULL, dvic_hpd_isr,
236                         IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
237                         "DVI HPD", ddata);
238                 if (r)
239                         return r;
240         }
241
242         adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
243         if (adapter_node) {
244                 adapter = of_get_i2c_adapter_by_node(adapter_node);
245                 of_node_put(adapter_node);
246                 if (adapter == NULL) {
247                         dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
248                         return -EPROBE_DEFER;
249                 }
250
251                 ddata->i2c_adapter = adapter;
252         }
253
254         return 0;
255 }
256
257 static int dvic_probe(struct platform_device *pdev)
258 {
259         struct panel_drv_data *ddata;
260         struct omap_dss_device *dssdev;
261         int r;
262
263         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
264         if (!ddata)
265                 return -ENOMEM;
266
267         platform_set_drvdata(pdev, ddata);
268
269         r = dvic_probe_of(pdev);
270         if (r)
271                 return r;
272
273         dssdev = &ddata->dssdev;
274         dssdev->ops = &dvic_ops;
275         dssdev->dev = &pdev->dev;
276         dssdev->type = OMAP_DISPLAY_TYPE_DVI;
277         dssdev->owner = THIS_MODULE;
278         dssdev->of_ports = BIT(0);
279
280         if (ddata->hpd_gpio)
281                 dssdev->ops_flags |= OMAP_DSS_DEVICE_OP_DETECT
282                                   |  OMAP_DSS_DEVICE_OP_HPD;
283         if (ddata->i2c_adapter)
284                 dssdev->ops_flags |= OMAP_DSS_DEVICE_OP_DETECT
285                                   |  OMAP_DSS_DEVICE_OP_EDID;
286
287         omapdss_display_init(dssdev);
288         omapdss_device_register(dssdev);
289
290         return 0;
291 }
292
293 static int __exit dvic_remove(struct platform_device *pdev)
294 {
295         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
296         struct omap_dss_device *dssdev = &ddata->dssdev;
297
298         omapdss_device_unregister(&ddata->dssdev);
299
300         dvic_disable(dssdev);
301
302         i2c_put_adapter(ddata->i2c_adapter);
303
304         mutex_destroy(&ddata->hpd_lock);
305
306         return 0;
307 }
308
309 static const struct of_device_id dvic_of_match[] = {
310         { .compatible = "omapdss,dvi-connector", },
311         {},
312 };
313
314 MODULE_DEVICE_TABLE(of, dvic_of_match);
315
316 static struct platform_driver dvi_connector_driver = {
317         .probe  = dvic_probe,
318         .remove = __exit_p(dvic_remove),
319         .driver = {
320                 .name   = "connector-dvi",
321                 .of_match_table = dvic_of_match,
322                 .suppress_bind_attrs = true,
323         },
324 };
325
326 module_platform_driver(dvi_connector_driver);
327
328 MODULE_AUTHOR("Tomi Valkeinen <[email protected]>");
329 MODULE_DESCRIPTION("Generic DVI Connector driver");
330 MODULE_LICENSE("GPL");
This page took 0.051524 seconds and 4 git commands to generate.