GNU Linux-libre 4.19.295-gnu1
[releases.git] / drivers / gpu / drm / omapdrm / displays / panel-sharp-ls037v7dw01.c
1 /*
2  * LCD panel driver for Sharp LS037V7DW01
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/delay.h>
13 #include <linux/gpio/consumer.h>
14 #include <linux/module.h>
15 #include <linux/of.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18 #include <linux/regulator/consumer.h>
19
20 #include "../dss/omapdss.h"
21
22 struct panel_drv_data {
23         struct omap_dss_device dssdev;
24         struct omap_dss_device *in;
25         struct regulator *vcc;
26
27         struct videomode vm;
28
29         struct gpio_desc *resb_gpio;    /* low = reset active min 20 us */
30         struct gpio_desc *ini_gpio;     /* high = power on */
31         struct gpio_desc *mo_gpio;      /* low = 480x640, high = 240x320 */
32         struct gpio_desc *lr_gpio;      /* high = conventional horizontal scanning */
33         struct gpio_desc *ud_gpio;      /* high = conventional vertical scanning */
34 };
35
36 static const struct videomode sharp_ls_vm = {
37         .hactive = 480,
38         .vactive = 640,
39
40         .pixelclock     = 19200000,
41
42         .hsync_len      = 2,
43         .hfront_porch   = 1,
44         .hback_porch    = 28,
45
46         .vsync_len      = 1,
47         .vfront_porch   = 1,
48         .vback_porch    = 1,
49
50         .flags          = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
51                           DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_NEGEDGE |
52                           DISPLAY_FLAGS_PIXDATA_POSEDGE,
53         /*
54          * Note: According to the panel documentation:
55          * DATA needs to be driven on the FALLING edge
56          */
57 };
58
59 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
60
61 static int sharp_ls_connect(struct omap_dss_device *dssdev)
62 {
63         struct panel_drv_data *ddata = to_panel_data(dssdev);
64         struct omap_dss_device *in;
65         int r;
66
67         if (omapdss_device_is_connected(dssdev))
68                 return 0;
69
70         in = omapdss_of_find_source_for_first_ep(dssdev->dev->of_node);
71         if (IS_ERR(in)) {
72                 dev_err(dssdev->dev, "failed to find video source\n");
73                 return PTR_ERR(in);
74         }
75
76         r = in->ops.dpi->connect(in, dssdev);
77         if (r) {
78                 omap_dss_put_device(in);
79                 return r;
80         }
81
82         ddata->in = in;
83         return 0;
84 }
85
86 static void sharp_ls_disconnect(struct omap_dss_device *dssdev)
87 {
88         struct panel_drv_data *ddata = to_panel_data(dssdev);
89         struct omap_dss_device *in = ddata->in;
90
91         if (!omapdss_device_is_connected(dssdev))
92                 return;
93
94         in->ops.dpi->disconnect(in, dssdev);
95
96         omap_dss_put_device(in);
97         ddata->in = NULL;
98 }
99
100 static int sharp_ls_enable(struct omap_dss_device *dssdev)
101 {
102         struct panel_drv_data *ddata = to_panel_data(dssdev);
103         struct omap_dss_device *in = ddata->in;
104         int r;
105
106         if (!omapdss_device_is_connected(dssdev))
107                 return -ENODEV;
108
109         if (omapdss_device_is_enabled(dssdev))
110                 return 0;
111
112         in->ops.dpi->set_timings(in, &ddata->vm);
113
114         if (ddata->vcc) {
115                 r = regulator_enable(ddata->vcc);
116                 if (r != 0)
117                         return r;
118         }
119
120         r = in->ops.dpi->enable(in);
121         if (r) {
122                 regulator_disable(ddata->vcc);
123                 return r;
124         }
125
126         /* wait couple of vsyncs until enabling the LCD */
127         msleep(50);
128
129         if (ddata->resb_gpio)
130                 gpiod_set_value_cansleep(ddata->resb_gpio, 1);
131
132         if (ddata->ini_gpio)
133                 gpiod_set_value_cansleep(ddata->ini_gpio, 1);
134
135         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
136
137         return 0;
138 }
139
140 static void sharp_ls_disable(struct omap_dss_device *dssdev)
141 {
142         struct panel_drv_data *ddata = to_panel_data(dssdev);
143         struct omap_dss_device *in = ddata->in;
144
145         if (!omapdss_device_is_enabled(dssdev))
146                 return;
147
148         if (ddata->ini_gpio)
149                 gpiod_set_value_cansleep(ddata->ini_gpio, 0);
150
151         if (ddata->resb_gpio)
152                 gpiod_set_value_cansleep(ddata->resb_gpio, 0);
153
154         /* wait at least 5 vsyncs after disabling the LCD */
155
156         msleep(100);
157
158         in->ops.dpi->disable(in);
159
160         if (ddata->vcc)
161                 regulator_disable(ddata->vcc);
162
163         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
164 }
165
166 static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
167                                  struct videomode *vm)
168 {
169         struct panel_drv_data *ddata = to_panel_data(dssdev);
170         struct omap_dss_device *in = ddata->in;
171
172         ddata->vm = *vm;
173         dssdev->panel.vm = *vm;
174
175         in->ops.dpi->set_timings(in, vm);
176 }
177
178 static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
179                                  struct videomode *vm)
180 {
181         struct panel_drv_data *ddata = to_panel_data(dssdev);
182
183         *vm = ddata->vm;
184 }
185
186 static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
187                                   struct videomode *vm)
188 {
189         struct panel_drv_data *ddata = to_panel_data(dssdev);
190         struct omap_dss_device *in = ddata->in;
191
192         return in->ops.dpi->check_timings(in, vm);
193 }
194
195 static struct omap_dss_driver sharp_ls_ops = {
196         .connect        = sharp_ls_connect,
197         .disconnect     = sharp_ls_disconnect,
198
199         .enable         = sharp_ls_enable,
200         .disable        = sharp_ls_disable,
201
202         .set_timings    = sharp_ls_set_timings,
203         .get_timings    = sharp_ls_get_timings,
204         .check_timings  = sharp_ls_check_timings,
205 };
206
207 static  int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
208         const char *desc, struct gpio_desc **gpiod)
209 {
210         struct gpio_desc *gd;
211
212         *gpiod = NULL;
213
214         gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
215         if (IS_ERR(gd))
216                 return PTR_ERR(gd);
217
218         *gpiod = gd;
219         return 0;
220 }
221
222 static int sharp_ls_probe_of(struct platform_device *pdev)
223 {
224         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
225         int r;
226
227         ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
228         if (IS_ERR(ddata->vcc)) {
229                 dev_err(&pdev->dev, "failed to get regulator\n");
230                 return PTR_ERR(ddata->vcc);
231         }
232
233         /* lcd INI */
234         r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
235         if (r)
236                 return r;
237
238         /* lcd RESB */
239         r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
240         if (r)
241                 return r;
242
243         /* lcd MO */
244         r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
245         if (r)
246                 return r;
247
248         /* lcd LR */
249         r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
250         if (r)
251                 return r;
252
253         /* lcd UD */
254         r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
255         if (r)
256                 return r;
257
258         return 0;
259 }
260
261 static int sharp_ls_probe(struct platform_device *pdev)
262 {
263         struct panel_drv_data *ddata;
264         struct omap_dss_device *dssdev;
265         int r;
266
267         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
268         if (ddata == NULL)
269                 return -ENOMEM;
270
271         platform_set_drvdata(pdev, ddata);
272
273         r = sharp_ls_probe_of(pdev);
274         if (r)
275                 return r;
276
277         ddata->vm = sharp_ls_vm;
278
279         dssdev = &ddata->dssdev;
280         dssdev->dev = &pdev->dev;
281         dssdev->driver = &sharp_ls_ops;
282         dssdev->type = OMAP_DISPLAY_TYPE_DPI;
283         dssdev->owner = THIS_MODULE;
284         dssdev->panel.vm = ddata->vm;
285
286         r = omapdss_register_display(dssdev);
287         if (r) {
288                 dev_err(&pdev->dev, "Failed to register panel\n");
289                 return r;
290         }
291
292         return 0;
293 }
294
295 static int __exit sharp_ls_remove(struct platform_device *pdev)
296 {
297         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
298         struct omap_dss_device *dssdev = &ddata->dssdev;
299
300         omapdss_unregister_display(dssdev);
301
302         sharp_ls_disable(dssdev);
303         sharp_ls_disconnect(dssdev);
304
305         return 0;
306 }
307
308 static const struct of_device_id sharp_ls_of_match[] = {
309         { .compatible = "omapdss,sharp,ls037v7dw01", },
310         {},
311 };
312
313 MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
314
315 static struct platform_driver sharp_ls_driver = {
316         .probe = sharp_ls_probe,
317         .remove = __exit_p(sharp_ls_remove),
318         .driver = {
319                 .name = "panel-sharp-ls037v7dw01",
320                 .of_match_table = sharp_ls_of_match,
321                 .suppress_bind_attrs = true,
322         },
323 };
324
325 module_platform_driver(sharp_ls_driver);
326
327 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
328 MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
329 MODULE_LICENSE("GPL");