This source file includes following definitions.
- panel_to_jh057n
- jh057n_init_sequence
- jh057n_enable
- jh057n_disable
- jh057n_unprepare
- jh057n_prepare
- jh057n_get_modes
- allpixelson_set
- jh057n_debugfs_init
- jh057n_debugfs_remove
- jh057n_probe
- jh057n_shutdown
- jh057n_remove
1
2
3
4
5
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
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
79
80
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
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
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);
347 ctx->panel.dev = dev;
348 ctx->panel.funcs = &jh057n_drm_funcs;
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
370 static 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
386 static 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
405 static const struct of_device_id jh057n_of_match[] = {
406 { .compatible = "rocktech,jh057n00900" },
407 { }
408 };
409 MODULE_DEVICE_TABLE(of, jh057n_of_match);
410
411 static 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 };
420 module_mipi_dsi_driver(jh057n_driver);
421
422 MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
423 MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel");
424 MODULE_LICENSE("GPL v2");