1 // SPDX-License-Identifier: GPL-2.0
3 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
5 * Copyright (C) Purism SPC 2019
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>
21 #define DRV_NAME "panel-rocktech-jh057n00900"
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
46 struct drm_panel panel;
47 struct gpio_desc *reset_gpio;
48 struct backlight_device *backlight;
51 struct dentry *debugfs;
54 static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel)
56 return container_of(panel, struct jh057n, panel);
59 #define dsi_generic_write_seq(dsi, seq...) do { \
60 static const u8 d[] = { seq }; \
62 ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \
67 static int jh057n_init_sequence(struct jh057n *ctx)
69 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
70 struct device *dev = ctx->dev;
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.
78 dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC,
80 dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF,
81 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
83 dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR,
84 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
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);
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,
124 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
126 DRM_DEV_ERROR(dev, "Failed to exit sleep mode\n");
129 /* Panel is operational 120 msec after reset */
131 ret = mipi_dsi_dcs_set_display_on(dsi);
135 DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
139 static int jh057n_enable(struct drm_panel *panel)
141 struct jh057n *ctx = panel_to_jh057n(panel);
143 return backlight_enable(ctx->backlight);
146 static int jh057n_disable(struct drm_panel *panel)
148 struct jh057n *ctx = panel_to_jh057n(panel);
150 return backlight_disable(ctx->backlight);
153 static int jh057n_unprepare(struct drm_panel *panel)
155 struct jh057n *ctx = panel_to_jh057n(panel);
156 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
161 mipi_dsi_dcs_set_display_off(dsi);
162 ctx->prepared = false;
167 static int jh057n_prepare(struct drm_panel *panel)
169 struct jh057n *ctx = panel_to_jh057n(panel);
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);
181 ret = jh057n_init_sequence(ctx);
183 DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
188 ctx->prepared = true;
193 static const struct drm_display_mode default_mode = {
195 .hsync_start = 720 + 90,
196 .hsync_end = 720 + 90 + 20,
197 .htotal = 720 + 90 + 20 + 20,
199 .vsync_start = 1440 + 20,
200 .vsync_end = 1440 + 20 + 4,
201 .vtotal = 1440 + 20 + 4 + 12,
204 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
209 static int jh057n_get_modes(struct drm_panel *panel)
211 struct jh057n *ctx = panel_to_jh057n(panel);
212 struct drm_display_mode *mode;
214 mode = drm_mode_duplicate(panel->drm, &default_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);
222 drm_mode_set_name(mode);
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);
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,
240 static int allpixelson_set(void *data, u64 val)
242 struct jh057n *ctx = data;
243 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
245 DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n");
246 dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
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);
257 DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL,
258 allpixelson_set, "%llu\n");
260 static int jh057n_debugfs_init(struct jh057n *ctx)
264 ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
268 f = debugfs_create_file("allpixelson", 0600,
269 ctx->debugfs, ctx, &allpixelson_fops);
276 static void jh057n_debugfs_remove(struct jh057n *ctx)
278 debugfs_remove_recursive(ctx->debugfs);
282 static int jh057n_probe(struct mipi_dsi_device *dsi)
284 struct device *dev = &dsi->dev;
288 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
292 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
293 if (IS_ERR(ctx->reset_gpio)) {
294 DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
295 return PTR_ERR(ctx->reset_gpio);
298 mipi_dsi_set_drvdata(dsi, ctx);
303 dsi->format = MIPI_DSI_FMT_RGB888;
304 dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
305 MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
307 ctx->backlight = devm_of_find_backlight(dev);
308 if (IS_ERR(ctx->backlight))
309 return PTR_ERR(ctx->backlight);
311 drm_panel_init(&ctx->panel);
312 ctx->panel.dev = dev;
313 ctx->panel.funcs = &jh057n_drm_funcs;
315 drm_panel_add(&ctx->panel);
317 ret = mipi_dsi_attach(dsi);
319 DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?\n");
320 drm_panel_remove(&ctx->panel);
324 DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
325 default_mode.hdisplay, default_mode.vdisplay,
326 default_mode.vrefresh,
327 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
329 jh057n_debugfs_init(ctx);
333 static void jh057n_shutdown(struct mipi_dsi_device *dsi)
335 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
338 ret = jh057n_unprepare(&ctx->panel);
340 DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
343 ret = jh057n_disable(&ctx->panel);
345 DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
349 static int jh057n_remove(struct mipi_dsi_device *dsi)
351 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
354 jh057n_shutdown(dsi);
356 ret = mipi_dsi_detach(dsi);
358 DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
361 drm_panel_remove(&ctx->panel);
363 jh057n_debugfs_remove(ctx);
368 static const struct of_device_id jh057n_of_match[] = {
369 { .compatible = "rocktech,jh057n00900" },
372 MODULE_DEVICE_TABLE(of, jh057n_of_match);
374 static struct mipi_dsi_driver jh057n_driver = {
375 .probe = jh057n_probe,
376 .remove = jh057n_remove,
377 .shutdown = jh057n_shutdown,
380 .of_match_table = jh057n_of_match,
383 module_mipi_dsi_driver(jh057n_driver);
385 MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
386 MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel");
387 MODULE_LICENSE("GPL v2");