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 void jh057n_debugfs_init(struct jh057n *ctx)
262 ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
264 debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx,
268 static void jh057n_debugfs_remove(struct jh057n *ctx)
270 debugfs_remove_recursive(ctx->debugfs);
274 static int jh057n_probe(struct mipi_dsi_device *dsi)
276 struct device *dev = &dsi->dev;
280 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
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);
290 mipi_dsi_set_drvdata(dsi, ctx);
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;
299 ctx->backlight = devm_of_find_backlight(dev);
300 if (IS_ERR(ctx->backlight))
301 return PTR_ERR(ctx->backlight);
303 drm_panel_init(&ctx->panel);
304 ctx->panel.dev = dev;
305 ctx->panel.funcs = &jh057n_drm_funcs;
307 drm_panel_add(&ctx->panel);
309 ret = mipi_dsi_attach(dsi);
311 DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?\n");
312 drm_panel_remove(&ctx->panel);
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);
321 jh057n_debugfs_init(ctx);
325 static void jh057n_shutdown(struct mipi_dsi_device *dsi)
327 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
330 ret = jh057n_unprepare(&ctx->panel);
332 DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
335 ret = jh057n_disable(&ctx->panel);
337 DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
341 static int jh057n_remove(struct mipi_dsi_device *dsi)
343 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
346 jh057n_shutdown(dsi);
348 ret = mipi_dsi_detach(dsi);
350 DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
353 drm_panel_remove(&ctx->panel);
355 jh057n_debugfs_remove(ctx);
360 static const struct of_device_id jh057n_of_match[] = {
361 { .compatible = "rocktech,jh057n00900" },
364 MODULE_DEVICE_TABLE(of, jh057n_of_match);
366 static struct mipi_dsi_driver jh057n_driver = {
367 .probe = jh057n_probe,
368 .remove = jh057n_remove,
369 .shutdown = jh057n_shutdown,
372 .of_match_table = jh057n_of_match,
375 module_mipi_dsi_driver(jh057n_driver);
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");