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 <video/display_timing.h>
19#include <video/mipi_display.h>
20
21#define DRV_NAME "panel-rocktech-jh057n00900"
22
23
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
43
44struct jh057n {
45 struct device *dev;
46 struct drm_panel panel;
47 struct gpio_desc *reset_gpio;
48 struct backlight_device *backlight;
49 bool prepared;
50
51 struct dentry *debugfs;
52};
53
54static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel)
55{
56 return container_of(panel, struct jh057n, panel);
57}
58
59#define dsi_generic_write_seq(dsi, seq...) do { \
60 static const u8 d[] = { seq }; \
61 int ret; \
62 ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \
63 if (ret < 0) \
64 return ret; \
65 } while (0)
66
67static int jh057n_init_sequence(struct jh057n *ctx)
68{
69 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
70 struct device *dev = ctx->dev;
71 int ret;
72
73
74
75
76
77
78 dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC,
79 0xF1, 0x12, 0x83);
80 dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF,
81 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
82 0x00, 0x00);
83 dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR,
84 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
85 0x00);
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);
94 msleep(20);
95
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,
121 0x11, 0x18);
122 msleep(20);
123
124 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
125 if (ret < 0) {
126 DRM_DEV_ERROR(dev, "Failed to exit sleep mode\n");
127 return ret;
128 }
129
130 msleep(60);
131 ret = mipi_dsi_dcs_set_display_on(dsi);
132 if (ret)
133 return ret;
134
135 DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
136 return 0;
137}
138
139static int jh057n_enable(struct drm_panel *panel)
140{
141 struct jh057n *ctx = panel_to_jh057n(panel);
142
143 return backlight_enable(ctx->backlight);
144}
145
146static int jh057n_disable(struct drm_panel *panel)
147{
148 struct jh057n *ctx = panel_to_jh057n(panel);
149
150 return backlight_disable(ctx->backlight);
151}
152
153static int jh057n_unprepare(struct drm_panel *panel)
154{
155 struct jh057n *ctx = panel_to_jh057n(panel);
156 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
157
158 if (!ctx->prepared)
159 return 0;
160
161 mipi_dsi_dcs_set_display_off(dsi);
162 ctx->prepared = false;
163
164 return 0;
165}
166
167static int jh057n_prepare(struct drm_panel *panel)
168{
169 struct jh057n *ctx = panel_to_jh057n(panel);
170 int ret;
171
172 if (ctx->prepared)
173 return 0;
174
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);
179 msleep(20);
180
181 ret = jh057n_init_sequence(ctx);
182 if (ret < 0) {
183 DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
184 ret);
185 return ret;
186 }
187
188 ctx->prepared = true;
189
190 return 0;
191}
192
193static const struct drm_display_mode default_mode = {
194 .hdisplay = 720,
195 .hsync_start = 720 + 90,
196 .hsync_end = 720 + 90 + 20,
197 .htotal = 720 + 90 + 20 + 20,
198 .vdisplay = 1440,
199 .vsync_start = 1440 + 20,
200 .vsync_end = 1440 + 20 + 4,
201 .vtotal = 1440 + 20 + 4 + 12,
202 .vrefresh = 60,
203 .clock = 75276,
204 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
205 .width_mm = 65,
206 .height_mm = 130,
207};
208
209static int jh057n_get_modes(struct drm_panel *panel)
210{
211 struct jh057n *ctx = panel_to_jh057n(panel);
212 struct drm_display_mode *mode;
213
214 mode = drm_mode_duplicate(panel->drm, &default_mode);
215 if (!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);
219 return -ENOMEM;
220 }
221
222 drm_mode_set_name(mode);
223
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);
228
229 return 1;
230}
231
232static 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,
238};
239
240static int allpixelson_set(void *data, u64 val)
241{
242 struct jh057n *ctx = data;
243 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
244
245 DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n");
246 dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
247 msleep(val * 1000);
248
249 drm_panel_disable(&ctx->panel);
250 drm_panel_unprepare(&ctx->panel);
251 drm_panel_prepare(&ctx->panel);
252 drm_panel_enable(&ctx->panel);
253
254 return 0;
255}
256
257DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL,
258 allpixelson_set, "%llu\n");
259
260static void jh057n_debugfs_init(struct jh057n *ctx)
261{
262 ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
263
264 debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx,
265 &allpixelson_fops);
266}
267
268static void jh057n_debugfs_remove(struct jh057n *ctx)
269{
270 debugfs_remove_recursive(ctx->debugfs);
271 ctx->debugfs = NULL;
272}
273
274static int jh057n_probe(struct mipi_dsi_device *dsi)
275{
276 struct device *dev = &dsi->dev;
277 struct jh057n *ctx;
278 int ret;
279
280 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
281 if (!ctx)
282 return -ENOMEM;
283
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);
288 }
289
290 mipi_dsi_set_drvdata(dsi, ctx);
291
292 ctx->dev = dev;
293
294 dsi->lanes = 4;
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;
298
299 ctx->backlight = devm_of_find_backlight(dev);
300 if (IS_ERR(ctx->backlight))
301 return PTR_ERR(ctx->backlight);
302
303 drm_panel_init(&ctx->panel);
304 ctx->panel.dev = dev;
305 ctx->panel.funcs = &jh057n_drm_funcs;
306
307 drm_panel_add(&ctx->panel);
308
309 ret = mipi_dsi_attach(dsi);
310 if (ret < 0) {
311 DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?\n");
312 drm_panel_remove(&ctx->panel);
313 return ret;
314 }
315
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);
320
321 jh057n_debugfs_init(ctx);
322 return 0;
323}
324
325static void jh057n_shutdown(struct mipi_dsi_device *dsi)
326{
327 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
328 int ret;
329
330 ret = jh057n_unprepare(&ctx->panel);
331 if (ret < 0)
332 DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
333 ret);
334
335 ret = jh057n_disable(&ctx->panel);
336 if (ret < 0)
337 DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
338 ret);
339}
340
341static int jh057n_remove(struct mipi_dsi_device *dsi)
342{
343 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
344 int ret;
345
346 jh057n_shutdown(dsi);
347
348 ret = mipi_dsi_detach(dsi);
349 if (ret < 0)
350 DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
351 ret);
352
353 drm_panel_remove(&ctx->panel);
354
355 jh057n_debugfs_remove(ctx);
356
357 return 0;
358}
359
360static const struct of_device_id jh057n_of_match[] = {
361 { .compatible = "rocktech,jh057n00900" },
362 { }
363};
364MODULE_DEVICE_TABLE(of, jh057n_of_match);
365
366static struct mipi_dsi_driver jh057n_driver = {
367 .probe = jh057n_probe,
368 .remove = jh057n_remove,
369 .shutdown = jh057n_shutdown,
370 .driver = {
371 .name = DRV_NAME,
372 .of_match_table = jh057n_of_match,
373 },
374};
375module_mipi_dsi_driver(jh057n_driver);
376
377MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
378MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel");
379MODULE_LICENSE("GPL v2");
380