Linux-libre 4.19.116-gnu
[librecmc/linux-libre.git] / drivers / gpu / drm / omapdrm / displays / panel-dpi.c
1 /*
2  * Generic MIPI DPI Panel 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/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/of.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/backlight.h>
19
20 #include <video/of_display_timing.h>
21
22 #include "../dss/omapdss.h"
23
24 struct panel_drv_data {
25         struct omap_dss_device dssdev;
26         struct omap_dss_device *in;
27
28         struct videomode vm;
29
30         struct backlight_device *backlight;
31
32         struct gpio_desc *enable_gpio;
33         struct regulator *vcc_supply;
34 };
35
36 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
37
38 static int panel_dpi_connect(struct omap_dss_device *dssdev)
39 {
40         struct panel_drv_data *ddata = to_panel_data(dssdev);
41         struct omap_dss_device *in;
42         int r;
43
44         if (omapdss_device_is_connected(dssdev))
45                 return 0;
46
47         in = omapdss_of_find_source_for_first_ep(dssdev->dev->of_node);
48         if (IS_ERR(in)) {
49                 dev_err(dssdev->dev, "failed to find video source\n");
50                 return PTR_ERR(in);
51         }
52
53         r = in->ops.dpi->connect(in, dssdev);
54         if (r) {
55                 omap_dss_put_device(in);
56                 return r;
57         }
58
59         ddata->in = in;
60         return 0;
61 }
62
63 static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
64 {
65         struct panel_drv_data *ddata = to_panel_data(dssdev);
66         struct omap_dss_device *in = ddata->in;
67
68         if (!omapdss_device_is_connected(dssdev))
69                 return;
70
71         in->ops.dpi->disconnect(in, dssdev);
72
73         omap_dss_put_device(in);
74         ddata->in = NULL;
75 }
76
77 static int panel_dpi_enable(struct omap_dss_device *dssdev)
78 {
79         struct panel_drv_data *ddata = to_panel_data(dssdev);
80         struct omap_dss_device *in = ddata->in;
81         int r;
82
83         if (!omapdss_device_is_connected(dssdev))
84                 return -ENODEV;
85
86         if (omapdss_device_is_enabled(dssdev))
87                 return 0;
88
89         in->ops.dpi->set_timings(in, &ddata->vm);
90
91         r = in->ops.dpi->enable(in);
92         if (r)
93                 return r;
94
95         r = regulator_enable(ddata->vcc_supply);
96         if (r) {
97                 in->ops.dpi->disable(in);
98                 return r;
99         }
100
101         gpiod_set_value_cansleep(ddata->enable_gpio, 1);
102         backlight_enable(ddata->backlight);
103
104         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
105
106         return 0;
107 }
108
109 static void panel_dpi_disable(struct omap_dss_device *dssdev)
110 {
111         struct panel_drv_data *ddata = to_panel_data(dssdev);
112         struct omap_dss_device *in = ddata->in;
113
114         if (!omapdss_device_is_enabled(dssdev))
115                 return;
116
117         backlight_disable(ddata->backlight);
118
119         gpiod_set_value_cansleep(ddata->enable_gpio, 0);
120         regulator_disable(ddata->vcc_supply);
121
122         in->ops.dpi->disable(in);
123
124         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
125 }
126
127 static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
128                                   struct videomode *vm)
129 {
130         struct panel_drv_data *ddata = to_panel_data(dssdev);
131         struct omap_dss_device *in = ddata->in;
132
133         ddata->vm = *vm;
134         dssdev->panel.vm = *vm;
135
136         in->ops.dpi->set_timings(in, vm);
137 }
138
139 static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
140                                   struct videomode *vm)
141 {
142         struct panel_drv_data *ddata = to_panel_data(dssdev);
143
144         *vm = ddata->vm;
145 }
146
147 static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
148                                    struct videomode *vm)
149 {
150         struct panel_drv_data *ddata = to_panel_data(dssdev);
151         struct omap_dss_device *in = ddata->in;
152
153         return in->ops.dpi->check_timings(in, vm);
154 }
155
156 static struct omap_dss_driver panel_dpi_ops = {
157         .connect        = panel_dpi_connect,
158         .disconnect     = panel_dpi_disconnect,
159
160         .enable         = panel_dpi_enable,
161         .disable        = panel_dpi_disable,
162
163         .set_timings    = panel_dpi_set_timings,
164         .get_timings    = panel_dpi_get_timings,
165         .check_timings  = panel_dpi_check_timings,
166 };
167
168 static int panel_dpi_probe_of(struct platform_device *pdev)
169 {
170         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
171         struct device_node *node = pdev->dev.of_node;
172         int r;
173         struct display_timing timing;
174         struct gpio_desc *gpio;
175
176         gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
177         if (IS_ERR(gpio))
178                 return PTR_ERR(gpio);
179
180         ddata->enable_gpio = gpio;
181
182         /*
183          * Many different panels are supported by this driver and there are
184          * probably very different needs for their reset pins in regards to
185          * timing and order relative to the enable gpio. So for now it's just
186          * ensured that the reset line isn't active.
187          */
188         gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
189         if (IS_ERR(gpio))
190                 return PTR_ERR(gpio);
191
192         ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
193         if (IS_ERR(ddata->vcc_supply))
194                 return PTR_ERR(ddata->vcc_supply);
195
196         ddata->backlight = devm_of_find_backlight(&pdev->dev);
197
198         if (IS_ERR(ddata->backlight))
199                 return PTR_ERR(ddata->backlight);
200
201         r = of_get_display_timing(node, "panel-timing", &timing);
202         if (r) {
203                 dev_err(&pdev->dev, "failed to get video timing\n");
204                 return r;
205         }
206
207         videomode_from_timing(&timing, &ddata->vm);
208
209         return 0;
210 }
211
212 static int panel_dpi_probe(struct platform_device *pdev)
213 {
214         struct panel_drv_data *ddata;
215         struct omap_dss_device *dssdev;
216         int r;
217
218         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
219         if (ddata == NULL)
220                 return -ENOMEM;
221
222         platform_set_drvdata(pdev, ddata);
223
224         r = panel_dpi_probe_of(pdev);
225         if (r)
226                 return r;
227
228         dssdev = &ddata->dssdev;
229         dssdev->dev = &pdev->dev;
230         dssdev->driver = &panel_dpi_ops;
231         dssdev->type = OMAP_DISPLAY_TYPE_DPI;
232         dssdev->owner = THIS_MODULE;
233         dssdev->panel.vm = ddata->vm;
234
235         r = omapdss_register_display(dssdev);
236         if (r) {
237                 dev_err(&pdev->dev, "Failed to register panel\n");
238                 return r;
239         }
240
241         return 0;
242 }
243
244 static int __exit panel_dpi_remove(struct platform_device *pdev)
245 {
246         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
247         struct omap_dss_device *dssdev = &ddata->dssdev;
248
249         omapdss_unregister_display(dssdev);
250
251         panel_dpi_disable(dssdev);
252         panel_dpi_disconnect(dssdev);
253
254         return 0;
255 }
256
257 static const struct of_device_id panel_dpi_of_match[] = {
258         { .compatible = "omapdss,panel-dpi", },
259         {},
260 };
261
262 MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
263
264 static struct platform_driver panel_dpi_driver = {
265         .probe = panel_dpi_probe,
266         .remove = __exit_p(panel_dpi_remove),
267         .driver = {
268                 .name = "panel-dpi",
269                 .of_match_table = panel_dpi_of_match,
270                 .suppress_bind_attrs = true,
271         },
272 };
273
274 module_platform_driver(panel_dpi_driver);
275
276 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
277 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
278 MODULE_LICENSE("GPL");