Merge tag 'for-5.2-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / omapdrm / displays / connector-hdmi.c
1 /*
2  * HDMI 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/module.h>
14 #include <linux/mutex.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17
18 #include "../dss/omapdss.h"
19
20 struct panel_drv_data {
21         struct omap_dss_device dssdev;
22         void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
23         void *hpd_cb_data;
24         struct mutex hpd_lock;
25
26         struct device *dev;
27
28         struct gpio_desc *hpd_gpio;
29 };
30
31 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
32
33 static int hdmic_connect(struct omap_dss_device *src,
34                          struct omap_dss_device *dst)
35 {
36         return 0;
37 }
38
39 static void hdmic_disconnect(struct omap_dss_device *src,
40                              struct omap_dss_device *dst)
41 {
42 }
43
44 static bool hdmic_detect(struct omap_dss_device *dssdev)
45 {
46         struct panel_drv_data *ddata = to_panel_data(dssdev);
47
48         return gpiod_get_value_cansleep(ddata->hpd_gpio);
49 }
50
51 static void hdmic_register_hpd_cb(struct omap_dss_device *dssdev,
52                                   void (*cb)(void *cb_data,
53                                             enum drm_connector_status status),
54                                   void *cb_data)
55 {
56         struct panel_drv_data *ddata = to_panel_data(dssdev);
57
58         mutex_lock(&ddata->hpd_lock);
59         ddata->hpd_cb = cb;
60         ddata->hpd_cb_data = cb_data;
61         mutex_unlock(&ddata->hpd_lock);
62 }
63
64 static void hdmic_unregister_hpd_cb(struct omap_dss_device *dssdev)
65 {
66         struct panel_drv_data *ddata = to_panel_data(dssdev);
67
68         mutex_lock(&ddata->hpd_lock);
69         ddata->hpd_cb = NULL;
70         ddata->hpd_cb_data = NULL;
71         mutex_unlock(&ddata->hpd_lock);
72 }
73
74 static const struct omap_dss_device_ops hdmic_ops = {
75         .connect                = hdmic_connect,
76         .disconnect             = hdmic_disconnect,
77
78         .detect                 = hdmic_detect,
79         .register_hpd_cb        = hdmic_register_hpd_cb,
80         .unregister_hpd_cb      = hdmic_unregister_hpd_cb,
81 };
82
83 static irqreturn_t hdmic_hpd_isr(int irq, void *data)
84 {
85         struct panel_drv_data *ddata = data;
86
87         mutex_lock(&ddata->hpd_lock);
88         if (ddata->hpd_cb) {
89                 enum drm_connector_status status;
90
91                 if (hdmic_detect(&ddata->dssdev))
92                         status = connector_status_connected;
93                 else
94                         status = connector_status_disconnected;
95
96                 ddata->hpd_cb(ddata->hpd_cb_data, status);
97         }
98         mutex_unlock(&ddata->hpd_lock);
99
100         return IRQ_HANDLED;
101 }
102
103 static int hdmic_probe(struct platform_device *pdev)
104 {
105         struct panel_drv_data *ddata;
106         struct omap_dss_device *dssdev;
107         struct gpio_desc *gpio;
108         int r;
109
110         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
111         if (!ddata)
112                 return -ENOMEM;
113
114         platform_set_drvdata(pdev, ddata);
115         ddata->dev = &pdev->dev;
116
117         mutex_init(&ddata->hpd_lock);
118
119         /* HPD GPIO */
120         gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN);
121         if (IS_ERR(gpio)) {
122                 dev_err(&pdev->dev, "failed to parse HPD gpio\n");
123                 return PTR_ERR(gpio);
124         }
125
126         ddata->hpd_gpio = gpio;
127
128         if (ddata->hpd_gpio) {
129                 r = devm_request_threaded_irq(&pdev->dev,
130                                 gpiod_to_irq(ddata->hpd_gpio),
131                                 NULL, hdmic_hpd_isr,
132                                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
133                                 IRQF_ONESHOT,
134                                 "hdmic hpd", ddata);
135                 if (r)
136                         return r;
137         }
138
139         dssdev = &ddata->dssdev;
140         dssdev->ops = &hdmic_ops;
141         dssdev->dev = &pdev->dev;
142         dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
143         dssdev->display = true;
144         dssdev->owner = THIS_MODULE;
145         dssdev->of_ports = BIT(0);
146         dssdev->ops_flags = ddata->hpd_gpio
147                           ? OMAP_DSS_DEVICE_OP_DETECT | OMAP_DSS_DEVICE_OP_HPD
148                           : 0;
149
150         omapdss_display_init(dssdev);
151         omapdss_device_register(dssdev);
152
153         return 0;
154 }
155
156 static int __exit hdmic_remove(struct platform_device *pdev)
157 {
158         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
159
160         omapdss_device_unregister(&ddata->dssdev);
161
162         return 0;
163 }
164
165 static const struct of_device_id hdmic_of_match[] = {
166         { .compatible = "omapdss,hdmi-connector", },
167         {},
168 };
169
170 MODULE_DEVICE_TABLE(of, hdmic_of_match);
171
172 static struct platform_driver hdmi_connector_driver = {
173         .probe  = hdmic_probe,
174         .remove = __exit_p(hdmic_remove),
175         .driver = {
176                 .name   = "connector-hdmi",
177                 .of_match_table = hdmic_of_match,
178                 .suppress_bind_attrs = true,
179         },
180 };
181
182 module_platform_driver(hdmi_connector_driver);
183
184 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
185 MODULE_DESCRIPTION("HDMI Connector driver");
186 MODULE_LICENSE("GPL");