GNU Linux-libre 4.14.262-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
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 = ddata->in;
65         int r;
66
67         if (omapdss_device_is_connected(dssdev))
68                 return 0;
69
70         r = in->ops.dpi->connect(in, dssdev);
71         if (r)
72                 return r;
73
74         return 0;
75 }
76
77 static void sharp_ls_disconnect(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
82         if (!omapdss_device_is_connected(dssdev))
83                 return;
84
85         in->ops.dpi->disconnect(in, dssdev);
86 }
87
88 static int sharp_ls_enable(struct omap_dss_device *dssdev)
89 {
90         struct panel_drv_data *ddata = to_panel_data(dssdev);
91         struct omap_dss_device *in = ddata->in;
92         int r;
93
94         if (!omapdss_device_is_connected(dssdev))
95                 return -ENODEV;
96
97         if (omapdss_device_is_enabled(dssdev))
98                 return 0;
99
100         in->ops.dpi->set_timings(in, &ddata->vm);
101
102         if (ddata->vcc) {
103                 r = regulator_enable(ddata->vcc);
104                 if (r != 0)
105                         return r;
106         }
107
108         r = in->ops.dpi->enable(in);
109         if (r) {
110                 regulator_disable(ddata->vcc);
111                 return r;
112         }
113
114         /* wait couple of vsyncs until enabling the LCD */
115         msleep(50);
116
117         if (ddata->resb_gpio)
118                 gpiod_set_value_cansleep(ddata->resb_gpio, 1);
119
120         if (ddata->ini_gpio)
121                 gpiod_set_value_cansleep(ddata->ini_gpio, 1);
122
123         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
124
125         return 0;
126 }
127
128 static void sharp_ls_disable(struct omap_dss_device *dssdev)
129 {
130         struct panel_drv_data *ddata = to_panel_data(dssdev);
131         struct omap_dss_device *in = ddata->in;
132
133         if (!omapdss_device_is_enabled(dssdev))
134                 return;
135
136         if (ddata->ini_gpio)
137                 gpiod_set_value_cansleep(ddata->ini_gpio, 0);
138
139         if (ddata->resb_gpio)
140                 gpiod_set_value_cansleep(ddata->resb_gpio, 0);
141
142         /* wait at least 5 vsyncs after disabling the LCD */
143
144         msleep(100);
145
146         in->ops.dpi->disable(in);
147
148         if (ddata->vcc)
149                 regulator_disable(ddata->vcc);
150
151         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
152 }
153
154 static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
155                                  struct videomode *vm)
156 {
157         struct panel_drv_data *ddata = to_panel_data(dssdev);
158         struct omap_dss_device *in = ddata->in;
159
160         ddata->vm = *vm;
161         dssdev->panel.vm = *vm;
162
163         in->ops.dpi->set_timings(in, vm);
164 }
165
166 static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
167                                  struct videomode *vm)
168 {
169         struct panel_drv_data *ddata = to_panel_data(dssdev);
170
171         *vm = ddata->vm;
172 }
173
174 static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
175                                   struct videomode *vm)
176 {
177         struct panel_drv_data *ddata = to_panel_data(dssdev);
178         struct omap_dss_device *in = ddata->in;
179
180         return in->ops.dpi->check_timings(in, vm);
181 }
182
183 static struct omap_dss_driver sharp_ls_ops = {
184         .connect        = sharp_ls_connect,
185         .disconnect     = sharp_ls_disconnect,
186
187         .enable         = sharp_ls_enable,
188         .disable        = sharp_ls_disable,
189
190         .set_timings    = sharp_ls_set_timings,
191         .get_timings    = sharp_ls_get_timings,
192         .check_timings  = sharp_ls_check_timings,
193 };
194
195 static  int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
196         const char *desc, struct gpio_desc **gpiod)
197 {
198         struct gpio_desc *gd;
199
200         *gpiod = NULL;
201
202         gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
203         if (IS_ERR(gd))
204                 return PTR_ERR(gd);
205
206         *gpiod = gd;
207         return 0;
208 }
209
210 static int sharp_ls_probe_of(struct platform_device *pdev)
211 {
212         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
213         struct device_node *node = pdev->dev.of_node;
214         struct omap_dss_device *in;
215         int r;
216
217         ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
218         if (IS_ERR(ddata->vcc)) {
219                 dev_err(&pdev->dev, "failed to get regulator\n");
220                 return PTR_ERR(ddata->vcc);
221         }
222
223         /* lcd INI */
224         r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
225         if (r)
226                 return r;
227
228         /* lcd RESB */
229         r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
230         if (r)
231                 return r;
232
233         /* lcd MO */
234         r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
235         if (r)
236                 return r;
237
238         /* lcd LR */
239         r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
240         if (r)
241                 return r;
242
243         /* lcd UD */
244         r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
245         if (r)
246                 return r;
247
248         in = omapdss_of_find_source_for_first_ep(node);
249         if (IS_ERR(in)) {
250                 dev_err(&pdev->dev, "failed to find video source\n");
251                 return PTR_ERR(in);
252         }
253
254         ddata->in = in;
255
256         return 0;
257 }
258
259 static int sharp_ls_probe(struct platform_device *pdev)
260 {
261         struct panel_drv_data *ddata;
262         struct omap_dss_device *dssdev;
263         int r;
264
265         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
266         if (ddata == NULL)
267                 return -ENOMEM;
268
269         platform_set_drvdata(pdev, ddata);
270
271         if (!pdev->dev.of_node)
272                 return -ENODEV;
273
274         r = sharp_ls_probe_of(pdev);
275         if (r)
276                 return r;
277
278         ddata->vm = sharp_ls_vm;
279
280         dssdev = &ddata->dssdev;
281         dssdev->dev = &pdev->dev;
282         dssdev->driver = &sharp_ls_ops;
283         dssdev->type = OMAP_DISPLAY_TYPE_DPI;
284         dssdev->owner = THIS_MODULE;
285         dssdev->panel.vm = ddata->vm;
286
287         r = omapdss_register_display(dssdev);
288         if (r) {
289                 dev_err(&pdev->dev, "Failed to register panel\n");
290                 goto err_reg;
291         }
292
293         return 0;
294
295 err_reg:
296         omap_dss_put_device(ddata->in);
297         return r;
298 }
299
300 static int __exit sharp_ls_remove(struct platform_device *pdev)
301 {
302         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
303         struct omap_dss_device *dssdev = &ddata->dssdev;
304         struct omap_dss_device *in = ddata->in;
305
306         omapdss_unregister_display(dssdev);
307
308         sharp_ls_disable(dssdev);
309         sharp_ls_disconnect(dssdev);
310
311         omap_dss_put_device(in);
312
313         return 0;
314 }
315
316 static const struct of_device_id sharp_ls_of_match[] = {
317         { .compatible = "omapdss,sharp,ls037v7dw01", },
318         {},
319 };
320
321 MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
322
323 static struct platform_driver sharp_ls_driver = {
324         .probe = sharp_ls_probe,
325         .remove = __exit_p(sharp_ls_remove),
326         .driver = {
327                 .name = "panel-sharp-ls037v7dw01",
328                 .of_match_table = sharp_ls_of_match,
329                 .suppress_bind_attrs = true,
330         },
331 };
332
333 module_platform_driver(sharp_ls_driver);
334
335 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
336 MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
337 MODULE_LICENSE("GPL");