Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mattst88...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / omapdrm / displays / panel-dpi.c
1 /*
2  * Generic MIPI DPI Panel Driver
3  *
4  * Copyright (C) 2013 Texas Instruments
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 = ddata->in;
42         int r;
43
44         if (omapdss_device_is_connected(dssdev))
45                 return 0;
46
47         r = in->ops.dpi->connect(in, dssdev);
48         if (r)
49                 return r;
50
51         return 0;
52 }
53
54 static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
55 {
56         struct panel_drv_data *ddata = to_panel_data(dssdev);
57         struct omap_dss_device *in = ddata->in;
58
59         if (!omapdss_device_is_connected(dssdev))
60                 return;
61
62         in->ops.dpi->disconnect(in, dssdev);
63 }
64
65 static int panel_dpi_enable(struct omap_dss_device *dssdev)
66 {
67         struct panel_drv_data *ddata = to_panel_data(dssdev);
68         struct omap_dss_device *in = ddata->in;
69         int r;
70
71         if (!omapdss_device_is_connected(dssdev))
72                 return -ENODEV;
73
74         if (omapdss_device_is_enabled(dssdev))
75                 return 0;
76
77         in->ops.dpi->set_timings(in, &ddata->vm);
78
79         r = in->ops.dpi->enable(in);
80         if (r)
81                 return r;
82
83         r = regulator_enable(ddata->vcc_supply);
84         if (r) {
85                 in->ops.dpi->disable(in);
86                 return r;
87         }
88
89         gpiod_set_value_cansleep(ddata->enable_gpio, 1);
90
91         if (ddata->backlight) {
92                 ddata->backlight->props.power = FB_BLANK_UNBLANK;
93                 backlight_update_status(ddata->backlight);
94         }
95
96         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
97
98         return 0;
99 }
100
101 static void panel_dpi_disable(struct omap_dss_device *dssdev)
102 {
103         struct panel_drv_data *ddata = to_panel_data(dssdev);
104         struct omap_dss_device *in = ddata->in;
105
106         if (!omapdss_device_is_enabled(dssdev))
107                 return;
108
109         if (ddata->backlight) {
110                 ddata->backlight->props.power = FB_BLANK_POWERDOWN;
111                 backlight_update_status(ddata->backlight);
112         }
113
114         gpiod_set_value_cansleep(ddata->enable_gpio, 0);
115         regulator_disable(ddata->vcc_supply);
116
117         in->ops.dpi->disable(in);
118
119         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
120 }
121
122 static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
123                                   struct videomode *vm)
124 {
125         struct panel_drv_data *ddata = to_panel_data(dssdev);
126         struct omap_dss_device *in = ddata->in;
127
128         ddata->vm = *vm;
129         dssdev->panel.vm = *vm;
130
131         in->ops.dpi->set_timings(in, vm);
132 }
133
134 static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
135                                   struct videomode *vm)
136 {
137         struct panel_drv_data *ddata = to_panel_data(dssdev);
138
139         *vm = ddata->vm;
140 }
141
142 static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
143                                    struct videomode *vm)
144 {
145         struct panel_drv_data *ddata = to_panel_data(dssdev);
146         struct omap_dss_device *in = ddata->in;
147
148         return in->ops.dpi->check_timings(in, vm);
149 }
150
151 static struct omap_dss_driver panel_dpi_ops = {
152         .connect        = panel_dpi_connect,
153         .disconnect     = panel_dpi_disconnect,
154
155         .enable         = panel_dpi_enable,
156         .disable        = panel_dpi_disable,
157
158         .set_timings    = panel_dpi_set_timings,
159         .get_timings    = panel_dpi_get_timings,
160         .check_timings  = panel_dpi_check_timings,
161 };
162
163 static int panel_dpi_probe_of(struct platform_device *pdev)
164 {
165         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
166         struct device_node *node = pdev->dev.of_node;
167         struct device_node *bl_node;
168         struct omap_dss_device *in;
169         int r;
170         struct display_timing timing;
171         struct gpio_desc *gpio;
172
173         gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
174         if (IS_ERR(gpio))
175                 return PTR_ERR(gpio);
176
177         ddata->enable_gpio = gpio;
178
179         /*
180          * Many different panels are supported by this driver and there are
181          * probably very different needs for their reset pins in regards to
182          * timing and order relative to the enable gpio. So for now it's just
183          * ensured that the reset line isn't active.
184          */
185         gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
186         if (IS_ERR(gpio))
187                 return PTR_ERR(gpio);
188
189         ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
190         if (IS_ERR(ddata->vcc_supply))
191                 return PTR_ERR(ddata->vcc_supply);
192
193         bl_node = of_parse_phandle(node, "backlight", 0);
194         if (bl_node) {
195                 ddata->backlight = of_find_backlight_by_node(bl_node);
196                 of_node_put(bl_node);
197
198                 if (!ddata->backlight)
199                         return -EPROBE_DEFER;
200         }
201
202         r = of_get_display_timing(node, "panel-timing", &timing);
203         if (r) {
204                 dev_err(&pdev->dev, "failed to get video timing\n");
205                 goto error_free_backlight;
206         }
207
208         videomode_from_timing(&timing, &ddata->vm);
209
210         in = omapdss_of_find_source_for_first_ep(node);
211         if (IS_ERR(in)) {
212                 dev_err(&pdev->dev, "failed to find video source\n");
213                 r = PTR_ERR(in);
214                 goto error_free_backlight;
215         }
216
217         ddata->in = in;
218
219         return 0;
220
221 error_free_backlight:
222         if (ddata->backlight)
223                 put_device(&ddata->backlight->dev);
224
225         return r;
226 }
227
228 static int panel_dpi_probe(struct platform_device *pdev)
229 {
230         struct panel_drv_data *ddata;
231         struct omap_dss_device *dssdev;
232         int r;
233
234         if (!pdev->dev.of_node)
235                 return -ENODEV;
236
237         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
238         if (ddata == NULL)
239                 return -ENOMEM;
240
241         platform_set_drvdata(pdev, ddata);
242
243         r = panel_dpi_probe_of(pdev);
244         if (r)
245                 return r;
246
247         dssdev = &ddata->dssdev;
248         dssdev->dev = &pdev->dev;
249         dssdev->driver = &panel_dpi_ops;
250         dssdev->type = OMAP_DISPLAY_TYPE_DPI;
251         dssdev->owner = THIS_MODULE;
252         dssdev->panel.vm = ddata->vm;
253
254         r = omapdss_register_display(dssdev);
255         if (r) {
256                 dev_err(&pdev->dev, "Failed to register panel\n");
257                 goto err_reg;
258         }
259
260         return 0;
261
262 err_reg:
263         omap_dss_put_device(ddata->in);
264         return r;
265 }
266
267 static int __exit panel_dpi_remove(struct platform_device *pdev)
268 {
269         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
270         struct omap_dss_device *dssdev = &ddata->dssdev;
271         struct omap_dss_device *in = ddata->in;
272
273         omapdss_unregister_display(dssdev);
274
275         panel_dpi_disable(dssdev);
276         panel_dpi_disconnect(dssdev);
277
278         omap_dss_put_device(in);
279
280         if (ddata->backlight)
281                 put_device(&ddata->backlight->dev);
282
283         return 0;
284 }
285
286 static const struct of_device_id panel_dpi_of_match[] = {
287         { .compatible = "omapdss,panel-dpi", },
288         {},
289 };
290
291 MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
292
293 static struct platform_driver panel_dpi_driver = {
294         .probe = panel_dpi_probe,
295         .remove = __exit_p(panel_dpi_remove),
296         .driver = {
297                 .name = "panel-dpi",
298                 .of_match_table = panel_dpi_of_match,
299                 .suppress_bind_attrs = true,
300         },
301 };
302
303 module_platform_driver(panel_dpi_driver);
304
305 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
306 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
307 MODULE_LICENSE("GPL");