GNU Linux-libre 4.14.313-gnu1
[releases.git] / drivers / gpu / drm / panel / panel-innolux-p079zca.c
1 /*
2  * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9
10 #include <linux/backlight.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/regulator/consumer.h>
15
16 #include <drm/drmP.h>
17 #include <drm/drm_crtc.h>
18 #include <drm/drm_mipi_dsi.h>
19 #include <drm/drm_panel.h>
20
21 #include <video/mipi_display.h>
22
23 struct innolux_panel {
24         struct drm_panel base;
25         struct mipi_dsi_device *link;
26
27         struct backlight_device *backlight;
28         struct regulator *supply;
29         struct gpio_desc *enable_gpio;
30
31         bool prepared;
32         bool enabled;
33 };
34
35 static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
36 {
37         return container_of(panel, struct innolux_panel, base);
38 }
39
40 static int innolux_panel_disable(struct drm_panel *panel)
41 {
42         struct innolux_panel *innolux = to_innolux_panel(panel);
43
44         if (!innolux->enabled)
45                 return 0;
46
47         innolux->backlight->props.power = FB_BLANK_POWERDOWN;
48         backlight_update_status(innolux->backlight);
49
50         innolux->enabled = false;
51
52         return 0;
53 }
54
55 static int innolux_panel_unprepare(struct drm_panel *panel)
56 {
57         struct innolux_panel *innolux = to_innolux_panel(panel);
58         int err;
59
60         if (!innolux->prepared)
61                 return 0;
62
63         err = mipi_dsi_dcs_set_display_off(innolux->link);
64         if (err < 0)
65                 DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
66                               err);
67
68         err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
69         if (err < 0) {
70                 DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
71                               err);
72                 return err;
73         }
74
75         gpiod_set_value_cansleep(innolux->enable_gpio, 0);
76
77         /* T8: 80ms - 1000ms */
78         msleep(80);
79
80         err = regulator_disable(innolux->supply);
81         if (err < 0)
82                 return err;
83
84         innolux->prepared = false;
85
86         return 0;
87 }
88
89 static int innolux_panel_prepare(struct drm_panel *panel)
90 {
91         struct innolux_panel *innolux = to_innolux_panel(panel);
92         int err, regulator_err;
93
94         if (innolux->prepared)
95                 return 0;
96
97         gpiod_set_value_cansleep(innolux->enable_gpio, 0);
98
99         err = regulator_enable(innolux->supply);
100         if (err < 0)
101                 return err;
102
103         /* T2: 15ms - 1000ms */
104         usleep_range(15000, 16000);
105
106         gpiod_set_value_cansleep(innolux->enable_gpio, 1);
107
108         /* T4: 15ms - 1000ms */
109         usleep_range(15000, 16000);
110
111         err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
112         if (err < 0) {
113                 DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
114                               err);
115                 goto poweroff;
116         }
117
118         /* T6: 120ms - 1000ms*/
119         msleep(120);
120
121         err = mipi_dsi_dcs_set_display_on(innolux->link);
122         if (err < 0) {
123                 DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
124                               err);
125                 goto poweroff;
126         }
127
128         /* T7: 5ms */
129         usleep_range(5000, 6000);
130
131         innolux->prepared = true;
132
133         return 0;
134
135 poweroff:
136         regulator_err = regulator_disable(innolux->supply);
137         if (regulator_err)
138                 DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
139                               regulator_err);
140
141         gpiod_set_value_cansleep(innolux->enable_gpio, 0);
142         return err;
143 }
144
145 static int innolux_panel_enable(struct drm_panel *panel)
146 {
147         struct innolux_panel *innolux = to_innolux_panel(panel);
148         int ret;
149
150         if (innolux->enabled)
151                 return 0;
152
153         innolux->backlight->props.power = FB_BLANK_UNBLANK;
154         ret = backlight_update_status(innolux->backlight);
155         if (ret) {
156                 DRM_DEV_ERROR(panel->drm->dev,
157                               "Failed to enable backlight %d\n", ret);
158                 return ret;
159         }
160
161         innolux->enabled = true;
162
163         return 0;
164 }
165
166 static const struct drm_display_mode default_mode = {
167         .clock = 56900,
168         .hdisplay = 768,
169         .hsync_start = 768 + 40,
170         .hsync_end = 768 + 40 + 40,
171         .htotal = 768 + 40 + 40 + 40,
172         .vdisplay = 1024,
173         .vsync_start = 1024 + 20,
174         .vsync_end = 1024 + 20 + 4,
175         .vtotal = 1024 + 20 + 4 + 20,
176         .vrefresh = 60,
177 };
178
179 static int innolux_panel_get_modes(struct drm_panel *panel)
180 {
181         struct drm_display_mode *mode;
182
183         mode = drm_mode_duplicate(panel->drm, &default_mode);
184         if (!mode) {
185                 DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
186                               default_mode.hdisplay, default_mode.vdisplay,
187                               default_mode.vrefresh);
188                 return -ENOMEM;
189         }
190
191         drm_mode_set_name(mode);
192
193         drm_mode_probed_add(panel->connector, mode);
194
195         panel->connector->display_info.width_mm = 120;
196         panel->connector->display_info.height_mm = 160;
197         panel->connector->display_info.bpc = 8;
198
199         return 1;
200 }
201
202 static const struct drm_panel_funcs innolux_panel_funcs = {
203         .disable = innolux_panel_disable,
204         .unprepare = innolux_panel_unprepare,
205         .prepare = innolux_panel_prepare,
206         .enable = innolux_panel_enable,
207         .get_modes = innolux_panel_get_modes,
208 };
209
210 static const struct of_device_id innolux_of_match[] = {
211         { .compatible = "innolux,p079zca", },
212         { }
213 };
214 MODULE_DEVICE_TABLE(of, innolux_of_match);
215
216 static int innolux_panel_add(struct innolux_panel *innolux)
217 {
218         struct device *dev = &innolux->link->dev;
219         struct device_node *np;
220         int err;
221
222         innolux->supply = devm_regulator_get(dev, "power");
223         if (IS_ERR(innolux->supply))
224                 return PTR_ERR(innolux->supply);
225
226         innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
227                                                        GPIOD_OUT_HIGH);
228         if (IS_ERR(innolux->enable_gpio)) {
229                 err = PTR_ERR(innolux->enable_gpio);
230                 dev_dbg(dev, "failed to get enable gpio: %d\n", err);
231                 innolux->enable_gpio = NULL;
232         }
233
234         np = of_parse_phandle(dev->of_node, "backlight", 0);
235         if (np) {
236                 innolux->backlight = of_find_backlight_by_node(np);
237                 of_node_put(np);
238
239                 if (!innolux->backlight)
240                         return -EPROBE_DEFER;
241         }
242
243         drm_panel_init(&innolux->base);
244         innolux->base.funcs = &innolux_panel_funcs;
245         innolux->base.dev = &innolux->link->dev;
246
247         err = drm_panel_add(&innolux->base);
248         if (err < 0)
249                 goto put_backlight;
250
251         return 0;
252
253 put_backlight:
254         put_device(&innolux->backlight->dev);
255
256         return err;
257 }
258
259 static void innolux_panel_del(struct innolux_panel *innolux)
260 {
261         if (innolux->base.dev)
262                 drm_panel_remove(&innolux->base);
263
264         put_device(&innolux->backlight->dev);
265 }
266
267 static int innolux_panel_probe(struct mipi_dsi_device *dsi)
268 {
269         struct innolux_panel *innolux;
270         int err;
271
272         dsi->lanes = 4;
273         dsi->format = MIPI_DSI_FMT_RGB888;
274         dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
275                           MIPI_DSI_MODE_LPM;
276
277         innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL);
278         if (!innolux)
279                 return -ENOMEM;
280
281         mipi_dsi_set_drvdata(dsi, innolux);
282
283         innolux->link = dsi;
284
285         err = innolux_panel_add(innolux);
286         if (err < 0)
287                 return err;
288
289         err = mipi_dsi_attach(dsi);
290         return err;
291 }
292
293 static int innolux_panel_remove(struct mipi_dsi_device *dsi)
294 {
295         struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
296         int err;
297
298         err = innolux_panel_unprepare(&innolux->base);
299         if (err < 0)
300                 DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
301                               err);
302
303         err = innolux_panel_disable(&innolux->base);
304         if (err < 0)
305                 DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
306
307         err = mipi_dsi_detach(dsi);
308         if (err < 0)
309                 DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
310                               err);
311
312         drm_panel_detach(&innolux->base);
313         innolux_panel_del(innolux);
314
315         return 0;
316 }
317
318 static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
319 {
320         struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
321
322         innolux_panel_unprepare(&innolux->base);
323         innolux_panel_disable(&innolux->base);
324 }
325
326 static struct mipi_dsi_driver innolux_panel_driver = {
327         .driver = {
328                 .name = "panel-innolux-p079zca",
329                 .of_match_table = innolux_of_match,
330         },
331         .probe = innolux_panel_probe,
332         .remove = innolux_panel_remove,
333         .shutdown = innolux_panel_shutdown,
334 };
335 module_mipi_dsi_driver(innolux_panel_driver);
336
337 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
338 MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
339 MODULE_LICENSE("GPL v2");