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