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