panel: rocktech: no need to check return value of debugfs_create functions
[linux-2.6-block.git] / drivers / gpu / drm / panel / panel-rocktech-jh057n00900.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
4  *
5  * Copyright (C) Purism SPC 2019
6  */
7
8 #include <drm/drm_mipi_dsi.h>
9 #include <drm/drm_modes.h>
10 #include <drm/drm_panel.h>
11 #include <drm/drm_print.h>
12 #include <linux/backlight.h>
13 #include <linux/debugfs.h>
14 #include <linux/delay.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/media-bus-format.h>
17 #include <linux/module.h>
18 #include <video/display_timing.h>
19 #include <video/mipi_display.h>
20
21 #define DRV_NAME "panel-rocktech-jh057n00900"
22
23 /* Manufacturer specific Commands send via DSI */
24 #define ST7703_CMD_ALL_PIXEL_OFF 0x22
25 #define ST7703_CMD_ALL_PIXEL_ON  0x23
26 #define ST7703_CMD_SETDISP       0xB2
27 #define ST7703_CMD_SETRGBIF      0xB3
28 #define ST7703_CMD_SETCYC        0xB4
29 #define ST7703_CMD_SETBGP        0xB5
30 #define ST7703_CMD_SETVCOM       0xB6
31 #define ST7703_CMD_SETOTP        0xB7
32 #define ST7703_CMD_SETPOWER_EXT  0xB8
33 #define ST7703_CMD_SETEXTC       0xB9
34 #define ST7703_CMD_SETMIPI       0xBA
35 #define ST7703_CMD_SETVDC        0xBC
36 #define ST7703_CMD_SETSCR        0xC0
37 #define ST7703_CMD_SETPOWER      0xC1
38 #define ST7703_CMD_SETPANEL      0xCC
39 #define ST7703_CMD_SETGAMMA      0xE0
40 #define ST7703_CMD_SETEQ         0xE3
41 #define ST7703_CMD_SETGIP1       0xE9
42 #define ST7703_CMD_SETGIP2       0xEA
43
44 struct jh057n {
45         struct device *dev;
46         struct drm_panel panel;
47         struct gpio_desc *reset_gpio;
48         struct backlight_device *backlight;
49         bool prepared;
50
51         struct dentry *debugfs;
52 };
53
54 static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel)
55 {
56         return container_of(panel, struct jh057n, panel);
57 }
58
59 #define dsi_generic_write_seq(dsi, seq...) do {                         \
60                 static const u8 d[] = { seq };                          \
61                 int ret;                                                \
62                 ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d));    \
63                 if (ret < 0)                                            \
64                         return ret;                                     \
65         } while (0)
66
67 static int jh057n_init_sequence(struct jh057n *ctx)
68 {
69         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
70         struct device *dev = ctx->dev;
71         int ret;
72
73         /*
74          * Init sequence was supplied by the panel vendor. Most of the commands
75          * resemble the ST7703 but the number of parameters often don't match
76          * so it's likely a clone.
77          */
78         dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC,
79                               0xF1, 0x12, 0x83);
80         dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF,
81                               0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
82                               0x00, 0x00);
83         dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR,
84                               0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
85                               0x00);
86         dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
87         dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
88         dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
89         dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30);
90         dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ,
91                               0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
92                               0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
93         dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08);
94         msleep(20);
95
96         dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F);
97         dsi_generic_write_seq(dsi, 0xBF, 0x02, 0x11, 0x00);
98         dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1,
99                               0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12,
100                               0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
101                               0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
102                               0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
103                               0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
104                               0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
105                               0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
107         dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2,
108                               0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109                               0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
110                               0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
111                               0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
112                               0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
113                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A,
115                               0xA5, 0x00, 0x00, 0x00, 0x00);
116         dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA,
117                               0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37,
118                               0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11,
119                               0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41,
120                               0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10,
121                               0x11, 0x18);
122         msleep(20);
123
124         ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
125         if (ret < 0) {
126                 DRM_DEV_ERROR(dev, "Failed to exit sleep mode\n");
127                 return ret;
128         }
129         /* Panel is operational 120 msec after reset */
130         msleep(60);
131         ret = mipi_dsi_dcs_set_display_on(dsi);
132         if (ret)
133                 return ret;
134
135         DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
136         return 0;
137 }
138
139 static int jh057n_enable(struct drm_panel *panel)
140 {
141         struct jh057n *ctx = panel_to_jh057n(panel);
142
143         return backlight_enable(ctx->backlight);
144 }
145
146 static int jh057n_disable(struct drm_panel *panel)
147 {
148         struct jh057n *ctx = panel_to_jh057n(panel);
149
150         return backlight_disable(ctx->backlight);
151 }
152
153 static int jh057n_unprepare(struct drm_panel *panel)
154 {
155         struct jh057n *ctx = panel_to_jh057n(panel);
156         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
157
158         if (!ctx->prepared)
159                 return 0;
160
161         mipi_dsi_dcs_set_display_off(dsi);
162         ctx->prepared = false;
163
164         return 0;
165 }
166
167 static int jh057n_prepare(struct drm_panel *panel)
168 {
169         struct jh057n *ctx = panel_to_jh057n(panel);
170         int ret;
171
172         if (ctx->prepared)
173                 return 0;
174
175         DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
176         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
177         usleep_range(20, 40);
178         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
179         msleep(20);
180
181         ret = jh057n_init_sequence(ctx);
182         if (ret < 0) {
183                 DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
184                               ret);
185                 return ret;
186         }
187
188         ctx->prepared = true;
189
190         return 0;
191 }
192
193 static const struct drm_display_mode default_mode = {
194         .hdisplay    = 720,
195         .hsync_start = 720 + 90,
196         .hsync_end   = 720 + 90 + 20,
197         .htotal      = 720 + 90 + 20 + 20,
198         .vdisplay    = 1440,
199         .vsync_start = 1440 + 20,
200         .vsync_end   = 1440 + 20 + 4,
201         .vtotal      = 1440 + 20 + 4 + 12,
202         .vrefresh    = 60,
203         .clock       = 75276,
204         .flags       = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
205         .width_mm    = 65,
206         .height_mm   = 130,
207 };
208
209 static int jh057n_get_modes(struct drm_panel *panel)
210 {
211         struct jh057n *ctx = panel_to_jh057n(panel);
212         struct drm_display_mode *mode;
213
214         mode = drm_mode_duplicate(panel->drm, &default_mode);
215         if (!mode) {
216                 DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n",
217                               default_mode.hdisplay, default_mode.vdisplay,
218                               default_mode.vrefresh);
219                 return -ENOMEM;
220         }
221
222         drm_mode_set_name(mode);
223
224         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
225         panel->connector->display_info.width_mm = mode->width_mm;
226         panel->connector->display_info.height_mm = mode->height_mm;
227         drm_mode_probed_add(panel->connector, mode);
228
229         return 1;
230 }
231
232 static const struct drm_panel_funcs jh057n_drm_funcs = {
233         .disable   = jh057n_disable,
234         .unprepare = jh057n_unprepare,
235         .prepare   = jh057n_prepare,
236         .enable    = jh057n_enable,
237         .get_modes = jh057n_get_modes,
238 };
239
240 static int allpixelson_set(void *data, u64 val)
241 {
242         struct jh057n *ctx = data;
243         struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
244
245         DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n");
246         dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
247         msleep(val * 1000);
248         /* Reset the panel to get video back */
249         drm_panel_disable(&ctx->panel);
250         drm_panel_unprepare(&ctx->panel);
251         drm_panel_prepare(&ctx->panel);
252         drm_panel_enable(&ctx->panel);
253
254         return 0;
255 }
256
257 DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL,
258                         allpixelson_set, "%llu\n");
259
260 static void jh057n_debugfs_init(struct jh057n *ctx)
261 {
262         ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
263
264         debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx,
265                             &allpixelson_fops);
266 }
267
268 static void jh057n_debugfs_remove(struct jh057n *ctx)
269 {
270         debugfs_remove_recursive(ctx->debugfs);
271         ctx->debugfs = NULL;
272 }
273
274 static int jh057n_probe(struct mipi_dsi_device *dsi)
275 {
276         struct device *dev = &dsi->dev;
277         struct jh057n *ctx;
278         int ret;
279
280         ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
281         if (!ctx)
282                 return -ENOMEM;
283
284         ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
285         if (IS_ERR(ctx->reset_gpio)) {
286                 DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
287                 return PTR_ERR(ctx->reset_gpio);
288         }
289
290         mipi_dsi_set_drvdata(dsi, ctx);
291
292         ctx->dev = dev;
293
294         dsi->lanes = 4;
295         dsi->format = MIPI_DSI_FMT_RGB888;
296         dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
297                 MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
298
299         ctx->backlight = devm_of_find_backlight(dev);
300         if (IS_ERR(ctx->backlight))
301                 return PTR_ERR(ctx->backlight);
302
303         drm_panel_init(&ctx->panel);
304         ctx->panel.dev = dev;
305         ctx->panel.funcs = &jh057n_drm_funcs;
306
307         drm_panel_add(&ctx->panel);
308
309         ret = mipi_dsi_attach(dsi);
310         if (ret < 0) {
311                 DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?\n");
312                 drm_panel_remove(&ctx->panel);
313                 return ret;
314         }
315
316         DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
317                      default_mode.hdisplay, default_mode.vdisplay,
318                      default_mode.vrefresh,
319                      mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
320
321         jh057n_debugfs_init(ctx);
322         return 0;
323 }
324
325 static void jh057n_shutdown(struct mipi_dsi_device *dsi)
326 {
327         struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
328         int ret;
329
330         ret = jh057n_unprepare(&ctx->panel);
331         if (ret < 0)
332                 DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
333                               ret);
334
335         ret = jh057n_disable(&ctx->panel);
336         if (ret < 0)
337                 DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
338                               ret);
339 }
340
341 static int jh057n_remove(struct mipi_dsi_device *dsi)
342 {
343         struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
344         int ret;
345
346         jh057n_shutdown(dsi);
347
348         ret = mipi_dsi_detach(dsi);
349         if (ret < 0)
350                 DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
351                               ret);
352
353         drm_panel_remove(&ctx->panel);
354
355         jh057n_debugfs_remove(ctx);
356
357         return 0;
358 }
359
360 static const struct of_device_id jh057n_of_match[] = {
361         { .compatible = "rocktech,jh057n00900" },
362         { /* sentinel */ }
363 };
364 MODULE_DEVICE_TABLE(of, jh057n_of_match);
365
366 static struct mipi_dsi_driver jh057n_driver = {
367         .probe  = jh057n_probe,
368         .remove = jh057n_remove,
369         .shutdown = jh057n_shutdown,
370         .driver = {
371                 .name = DRV_NAME,
372                 .of_match_table = jh057n_of_match,
373         },
374 };
375 module_mipi_dsi_driver(jh057n_driver);
376
377 MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
378 MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel");
379 MODULE_LICENSE("GPL v2");