1 // SPDX-License-Identifier: GPL-2.0
3 * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver
4 * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH
8 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
9 * Copyright (C) Purism SPC 2019
12 #include <drm/drm_mipi_dsi.h>
13 #include <drm/drm_modes.h>
14 #include <drm/drm_panel.h>
16 #include <video/display_timing.h>
17 #include <video/mipi_display.h>
19 #include <linux/delay.h>
20 #include <linux/gpio/consumer.h>
21 #include <linux/media-bus-format.h>
22 #include <linux/module.h>
24 #include <linux/regulator/consumer.h>
26 /* Manufacturer specific Commands send via DSI */
27 #define XPP055C272_CMD_ALL_PIXEL_OFF 0x22
28 #define XPP055C272_CMD_ALL_PIXEL_ON 0x23
29 #define XPP055C272_CMD_SETDISP 0xb2
30 #define XPP055C272_CMD_SETRGBIF 0xb3
31 #define XPP055C272_CMD_SETCYC 0xb4
32 #define XPP055C272_CMD_SETBGP 0xb5
33 #define XPP055C272_CMD_SETVCOM 0xb6
34 #define XPP055C272_CMD_SETOTP 0xb7
35 #define XPP055C272_CMD_SETPOWER_EXT 0xb8
36 #define XPP055C272_CMD_SETEXTC 0xb9
37 #define XPP055C272_CMD_SETMIPI 0xbA
38 #define XPP055C272_CMD_SETVDC 0xbc
39 #define XPP055C272_CMD_SETPCR 0xbf
40 #define XPP055C272_CMD_SETSCR 0xc0
41 #define XPP055C272_CMD_SETPOWER 0xc1
42 #define XPP055C272_CMD_SETECO 0xc6
43 #define XPP055C272_CMD_SETPANEL 0xcc
44 #define XPP055C272_CMD_SETGAMMA 0xe0
45 #define XPP055C272_CMD_SETEQ 0xe3
46 #define XPP055C272_CMD_SETGIP1 0xe9
47 #define XPP055C272_CMD_SETGIP2 0xea
51 struct drm_panel panel
;
52 struct gpio_desc
*reset_gpio
;
53 struct regulator
*vci
;
54 struct regulator
*iovcc
;
58 static inline struct xpp055c272
*panel_to_xpp055c272(struct drm_panel
*panel
)
60 return container_of(panel
, struct xpp055c272
, panel
);
63 #define dsi_generic_write_seq(dsi, cmd, seq...) do { \
64 static const u8 b[] = { cmd, seq }; \
66 ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
71 static int xpp055c272_init_sequence(struct xpp055c272
*ctx
)
73 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(ctx
->dev
);
74 struct device
*dev
= ctx
->dev
;
77 * Init sequence was supplied by the panel vendor without much
80 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETEXTC
, 0xf1, 0x12, 0x83);
81 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETMIPI
,
82 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
84 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,
86 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETPOWER_EXT
, 0x25);
87 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETPCR
, 0x02, 0x11, 0x00);
88 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETRGBIF
,
89 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,
91 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETSCR
,
92 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
94 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETVDC
, 0x46);
95 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETPANEL
, 0x0b);
96 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETCYC
, 0x80);
97 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETDISP
, 0xc8, 0x12, 0x30);
98 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETEQ
,
99 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
100 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
101 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETPOWER
,
102 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,
103 0x67, 0x77, 0x33, 0x33);
104 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETECO
, 0x00, 0x00, 0xff,
106 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETBGP
, 0x09, 0x09);
109 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETVCOM
, 0x87, 0x95);
110 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETGIP1
,
111 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,
112 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,
113 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
114 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,
115 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,
116 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,
117 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
119 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETGIP2
,
120 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,
122 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,
123 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,
124 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,
127 0xa0, 0x00, 0x00, 0x00, 0x00);
128 dsi_generic_write_seq(dsi
, XPP055C272_CMD_SETGAMMA
,
129 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,
130 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,
131 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,
132 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,
137 dev_dbg(dev
, "Panel init sequence done\n");
141 static int xpp055c272_unprepare(struct drm_panel
*panel
)
143 struct xpp055c272
*ctx
= panel_to_xpp055c272(panel
);
144 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(ctx
->dev
);
150 ret
= mipi_dsi_dcs_set_display_off(dsi
);
152 dev_err(ctx
->dev
, "failed to set display off: %d\n", ret
);
154 mipi_dsi_dcs_enter_sleep_mode(dsi
);
156 dev_err(ctx
->dev
, "failed to enter sleep mode: %d\n", ret
);
160 regulator_disable(ctx
->iovcc
);
161 regulator_disable(ctx
->vci
);
163 ctx
->prepared
= false;
168 static int xpp055c272_prepare(struct drm_panel
*panel
)
170 struct xpp055c272
*ctx
= panel_to_xpp055c272(panel
);
171 struct mipi_dsi_device
*dsi
= to_mipi_dsi_device(ctx
->dev
);
177 dev_dbg(ctx
->dev
, "Resetting the panel\n");
178 ret
= regulator_enable(ctx
->vci
);
180 dev_err(ctx
->dev
, "Failed to enable vci supply: %d\n", ret
);
183 ret
= regulator_enable(ctx
->iovcc
);
185 dev_err(ctx
->dev
, "Failed to enable iovcc supply: %d\n", ret
);
189 gpiod_set_value_cansleep(ctx
->reset_gpio
, 1);
191 usleep_range(10, 20);
192 gpiod_set_value_cansleep(ctx
->reset_gpio
, 0);
197 ret
= xpp055c272_init_sequence(ctx
);
199 dev_err(ctx
->dev
, "Panel init sequence failed: %d\n", ret
);
203 ret
= mipi_dsi_dcs_exit_sleep_mode(dsi
);
205 dev_err(ctx
->dev
, "Failed to exit sleep mode: %d\n", ret
);
212 ret
= mipi_dsi_dcs_set_display_on(dsi
);
214 dev_err(ctx
->dev
, "Failed to set display on: %d\n", ret
);
220 ctx
->prepared
= true;
225 regulator_disable(ctx
->iovcc
);
227 regulator_disable(ctx
->vci
);
231 static const struct drm_display_mode default_mode
= {
233 .hsync_start
= 720 + 40,
234 .hsync_end
= 720 + 40 + 10,
235 .htotal
= 720 + 40 + 10 + 40,
237 .vsync_start
= 1280 + 22,
238 .vsync_end
= 1280 + 22 + 4,
239 .vtotal
= 1280 + 22 + 4 + 11,
245 static int xpp055c272_get_modes(struct drm_panel
*panel
,
246 struct drm_connector
*connector
)
248 struct xpp055c272
*ctx
= panel_to_xpp055c272(panel
);
249 struct drm_display_mode
*mode
;
251 mode
= drm_mode_duplicate(connector
->dev
, &default_mode
);
253 dev_err(ctx
->dev
, "Failed to add mode %ux%u@%u\n",
254 default_mode
.hdisplay
, default_mode
.vdisplay
,
255 drm_mode_vrefresh(&default_mode
));
259 drm_mode_set_name(mode
);
261 mode
->type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
262 connector
->display_info
.width_mm
= mode
->width_mm
;
263 connector
->display_info
.height_mm
= mode
->height_mm
;
264 drm_mode_probed_add(connector
, mode
);
269 static const struct drm_panel_funcs xpp055c272_funcs
= {
270 .unprepare
= xpp055c272_unprepare
,
271 .prepare
= xpp055c272_prepare
,
272 .get_modes
= xpp055c272_get_modes
,
275 static int xpp055c272_probe(struct mipi_dsi_device
*dsi
)
277 struct device
*dev
= &dsi
->dev
;
278 struct xpp055c272
*ctx
;
281 ctx
= devm_kzalloc(dev
, sizeof(*ctx
), GFP_KERNEL
);
285 ctx
->reset_gpio
= devm_gpiod_get_optional(dev
, "reset", GPIOD_OUT_LOW
);
286 if (IS_ERR(ctx
->reset_gpio
)) {
287 dev_err(dev
, "cannot get reset gpio\n");
288 return PTR_ERR(ctx
->reset_gpio
);
291 ctx
->vci
= devm_regulator_get(dev
, "vci");
292 if (IS_ERR(ctx
->vci
)) {
293 ret
= PTR_ERR(ctx
->vci
);
294 if (ret
!= -EPROBE_DEFER
)
295 dev_err(dev
, "Failed to request vci regulator: %d\n", ret
);
299 ctx
->iovcc
= devm_regulator_get(dev
, "iovcc");
300 if (IS_ERR(ctx
->iovcc
)) {
301 ret
= PTR_ERR(ctx
->iovcc
);
302 if (ret
!= -EPROBE_DEFER
)
303 dev_err(dev
, "Failed to request iovcc regulator: %d\n", ret
);
307 mipi_dsi_set_drvdata(dsi
, ctx
);
312 dsi
->format
= MIPI_DSI_FMT_RGB888
;
313 dsi
->mode_flags
= MIPI_DSI_MODE_VIDEO
| MIPI_DSI_MODE_VIDEO_BURST
|
314 MIPI_DSI_MODE_LPM
| MIPI_DSI_MODE_EOT_PACKET
;
316 drm_panel_init(&ctx
->panel
, &dsi
->dev
, &xpp055c272_funcs
,
317 DRM_MODE_CONNECTOR_DSI
);
319 ret
= drm_panel_of_backlight(&ctx
->panel
);
323 drm_panel_add(&ctx
->panel
);
325 ret
= mipi_dsi_attach(dsi
);
327 dev_err(dev
, "mipi_dsi_attach failed: %d\n", ret
);
328 drm_panel_remove(&ctx
->panel
);
335 static void xpp055c272_shutdown(struct mipi_dsi_device
*dsi
)
337 struct xpp055c272
*ctx
= mipi_dsi_get_drvdata(dsi
);
340 ret
= drm_panel_unprepare(&ctx
->panel
);
342 dev_err(&dsi
->dev
, "Failed to unprepare panel: %d\n", ret
);
344 ret
= drm_panel_disable(&ctx
->panel
);
346 dev_err(&dsi
->dev
, "Failed to disable panel: %d\n", ret
);
349 static int xpp055c272_remove(struct mipi_dsi_device
*dsi
)
351 struct xpp055c272
*ctx
= mipi_dsi_get_drvdata(dsi
);
354 xpp055c272_shutdown(dsi
);
356 ret
= mipi_dsi_detach(dsi
);
358 dev_err(&dsi
->dev
, "Failed to detach from DSI host: %d\n", ret
);
360 drm_panel_remove(&ctx
->panel
);
365 static const struct of_device_id xpp055c272_of_match
[] = {
366 { .compatible
= "xinpeng,xpp055c272" },
369 MODULE_DEVICE_TABLE(of
, xpp055c272_of_match
);
371 static struct mipi_dsi_driver xpp055c272_driver
= {
373 .name
= "panel-xinpeng-xpp055c272",
374 .of_match_table
= xpp055c272_of_match
,
376 .probe
= xpp055c272_probe
,
377 .remove
= xpp055c272_remove
,
378 .shutdown
= xpp055c272_shutdown
,
380 module_mipi_dsi_driver(xpp055c272_driver
);
382 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
383 MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
384 MODULE_LICENSE("GPL v2");