Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / video / fbdev / omap2 / omapfb / displays / encoder-tpd12s015.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * TPD12S015 HDMI ESD protection & level shifter chip driver
4  *
5  * Copyright (C) 2013 Texas Instruments
6  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
7  */
8
9 #include <linux/completion.h>
10 #include <linux/delay.h>
11 #include <linux/module.h>
12 #include <linux/mod_devicetable.h>
13 #include <linux/slab.h>
14 #include <linux/platform_device.h>
15 #include <linux/gpio/consumer.h>
16
17 #include <video/omapfb_dss.h>
18
19 struct panel_drv_data {
20         struct omap_dss_device dssdev;
21         struct omap_dss_device *in;
22
23         struct gpio_desc *ct_cp_hpd_gpio;
24         struct gpio_desc *ls_oe_gpio;
25         struct gpio_desc *hpd_gpio;
26
27         struct omap_video_timings timings;
28 };
29
30 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
31
32 static int tpd_connect(struct omap_dss_device *dssdev,
33                 struct omap_dss_device *dst)
34 {
35         struct panel_drv_data *ddata = to_panel_data(dssdev);
36         struct omap_dss_device *in = ddata->in;
37         int r;
38
39         r = in->ops.hdmi->connect(in, dssdev);
40         if (r)
41                 return r;
42
43         dst->src = dssdev;
44         dssdev->dst = dst;
45
46         if (ddata->ct_cp_hpd_gpio) {
47                 gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
48                 /* DC-DC converter needs at max 300us to get to 90% of 5V */
49                 udelay(300);
50         }
51
52         return 0;
53 }
54
55 static void tpd_disconnect(struct omap_dss_device *dssdev,
56                 struct omap_dss_device *dst)
57 {
58         struct panel_drv_data *ddata = to_panel_data(dssdev);
59         struct omap_dss_device *in = ddata->in;
60
61         WARN_ON(dst != dssdev->dst);
62
63         if (dst != dssdev->dst)
64                 return;
65
66         gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
67
68         dst->src = NULL;
69         dssdev->dst = NULL;
70
71         in->ops.hdmi->disconnect(in, &ddata->dssdev);
72 }
73
74 static int tpd_enable(struct omap_dss_device *dssdev)
75 {
76         struct panel_drv_data *ddata = to_panel_data(dssdev);
77         struct omap_dss_device *in = ddata->in;
78         int r;
79
80         if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
81                 return 0;
82
83         in->ops.hdmi->set_timings(in, &ddata->timings);
84
85         r = in->ops.hdmi->enable(in);
86         if (r)
87                 return r;
88
89         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
90
91         return r;
92 }
93
94 static void tpd_disable(struct omap_dss_device *dssdev)
95 {
96         struct panel_drv_data *ddata = to_panel_data(dssdev);
97         struct omap_dss_device *in = ddata->in;
98
99         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
100                 return;
101
102         in->ops.hdmi->disable(in);
103
104         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
105 }
106
107 static void tpd_set_timings(struct omap_dss_device *dssdev,
108                 struct omap_video_timings *timings)
109 {
110         struct panel_drv_data *ddata = to_panel_data(dssdev);
111         struct omap_dss_device *in = ddata->in;
112
113         ddata->timings = *timings;
114         dssdev->panel.timings = *timings;
115
116         in->ops.hdmi->set_timings(in, timings);
117 }
118
119 static void tpd_get_timings(struct omap_dss_device *dssdev,
120                 struct omap_video_timings *timings)
121 {
122         struct panel_drv_data *ddata = to_panel_data(dssdev);
123
124         *timings = ddata->timings;
125 }
126
127 static int tpd_check_timings(struct omap_dss_device *dssdev,
128                 struct omap_video_timings *timings)
129 {
130         struct panel_drv_data *ddata = to_panel_data(dssdev);
131         struct omap_dss_device *in = ddata->in;
132         int r;
133
134         r = in->ops.hdmi->check_timings(in, timings);
135
136         return r;
137 }
138
139 static int tpd_read_edid(struct omap_dss_device *dssdev,
140                 u8 *edid, int len)
141 {
142         struct panel_drv_data *ddata = to_panel_data(dssdev);
143         struct omap_dss_device *in = ddata->in;
144         int r;
145
146         if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
147                 return -ENODEV;
148
149         gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
150
151         r = in->ops.hdmi->read_edid(in, edid, len);
152
153         gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
154
155         return r;
156 }
157
158 static bool tpd_detect(struct omap_dss_device *dssdev)
159 {
160         struct panel_drv_data *ddata = to_panel_data(dssdev);
161
162         return gpiod_get_value_cansleep(ddata->hpd_gpio);
163 }
164
165 static int tpd_set_infoframe(struct omap_dss_device *dssdev,
166                 const struct hdmi_avi_infoframe *avi)
167 {
168         struct panel_drv_data *ddata = to_panel_data(dssdev);
169         struct omap_dss_device *in = ddata->in;
170
171         return in->ops.hdmi->set_infoframe(in, avi);
172 }
173
174 static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
175                 bool hdmi_mode)
176 {
177         struct panel_drv_data *ddata = to_panel_data(dssdev);
178         struct omap_dss_device *in = ddata->in;
179
180         return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
181 }
182
183 static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
184         .connect                = tpd_connect,
185         .disconnect             = tpd_disconnect,
186
187         .enable                 = tpd_enable,
188         .disable                = tpd_disable,
189
190         .check_timings          = tpd_check_timings,
191         .set_timings            = tpd_set_timings,
192         .get_timings            = tpd_get_timings,
193
194         .read_edid              = tpd_read_edid,
195         .detect                 = tpd_detect,
196         .set_infoframe          = tpd_set_infoframe,
197         .set_hdmi_mode          = tpd_set_hdmi_mode,
198 };
199
200 static int tpd_probe_of(struct platform_device *pdev)
201 {
202         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
203         struct device_node *node = pdev->dev.of_node;
204         struct omap_dss_device *in;
205
206         in = omapdss_of_find_source_for_first_ep(node);
207         if (IS_ERR(in)) {
208                 dev_err(&pdev->dev, "failed to find video source\n");
209                 return PTR_ERR(in);
210         }
211
212         ddata->in = in;
213
214         return 0;
215 }
216
217 static int tpd_probe(struct platform_device *pdev)
218 {
219         struct omap_dss_device *dssdev;
220         struct panel_drv_data *ddata;
221         int r;
222         struct gpio_desc *gpio;
223
224         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
225         if (!ddata)
226                 return -ENOMEM;
227
228         platform_set_drvdata(pdev, ddata);
229
230         if (pdev->dev.of_node) {
231                 r = tpd_probe_of(pdev);
232                 if (r)
233                         return r;
234         } else {
235                 return -ENODEV;
236         }
237
238         gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
239                 GPIOD_OUT_LOW);
240         if (IS_ERR(gpio)) {
241                 r = PTR_ERR(gpio);
242                 goto err_gpio;
243         }
244
245         ddata->ct_cp_hpd_gpio = gpio;
246
247         gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
248                 GPIOD_OUT_LOW);
249         if (IS_ERR(gpio)) {
250                 r = PTR_ERR(gpio);
251                 goto err_gpio;
252         }
253
254         ddata->ls_oe_gpio = gpio;
255
256         gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
257                 GPIOD_IN);
258         if (IS_ERR(gpio)) {
259                 r = PTR_ERR(gpio);
260                 goto err_gpio;
261         }
262
263         ddata->hpd_gpio = gpio;
264
265         dssdev = &ddata->dssdev;
266         dssdev->ops.hdmi = &tpd_hdmi_ops;
267         dssdev->dev = &pdev->dev;
268         dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
269         dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
270         dssdev->owner = THIS_MODULE;
271         dssdev->port_num = 1;
272
273         r = omapdss_register_output(dssdev);
274         if (r) {
275                 dev_err(&pdev->dev, "Failed to register output\n");
276                 goto err_reg;
277         }
278
279         return 0;
280 err_reg:
281 err_gpio:
282         omap_dss_put_device(ddata->in);
283         return r;
284 }
285
286 static int __exit tpd_remove(struct platform_device *pdev)
287 {
288         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
289         struct omap_dss_device *dssdev = &ddata->dssdev;
290         struct omap_dss_device *in = ddata->in;
291
292         omapdss_unregister_output(&ddata->dssdev);
293
294         WARN_ON(omapdss_device_is_enabled(dssdev));
295         if (omapdss_device_is_enabled(dssdev))
296                 tpd_disable(dssdev);
297
298         WARN_ON(omapdss_device_is_connected(dssdev));
299         if (omapdss_device_is_connected(dssdev))
300                 tpd_disconnect(dssdev, dssdev->dst);
301
302         omap_dss_put_device(in);
303
304         return 0;
305 }
306
307 static const struct of_device_id tpd_of_match[] = {
308         { .compatible = "omapdss,ti,tpd12s015", },
309         {},
310 };
311
312 MODULE_DEVICE_TABLE(of, tpd_of_match);
313
314 static struct platform_driver tpd_driver = {
315         .probe  = tpd_probe,
316         .remove = __exit_p(tpd_remove),
317         .driver = {
318                 .name   = "tpd12s015",
319                 .of_match_table = tpd_of_match,
320                 .suppress_bind_attrs = true,
321         },
322 };
323
324 module_platform_driver(tpd_driver);
325
326 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
327 MODULE_DESCRIPTION("TPD12S015 driver");
328 MODULE_LICENSE("GPL");