xref: /openbmc/linux/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1d1479d02SHeiko Stuebner // SPDX-License-Identifier: GPL-2.0
2d1479d02SHeiko Stuebner /*
3d1479d02SHeiko Stuebner  * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver
4d1479d02SHeiko Stuebner  * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH
5d1479d02SHeiko Stuebner  *
6d1479d02SHeiko Stuebner  * based on
7d1479d02SHeiko Stuebner  *
8d1479d02SHeiko Stuebner  * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
9d1479d02SHeiko Stuebner  * Copyright (C) Purism SPC 2019
10d1479d02SHeiko Stuebner  */
11d1479d02SHeiko Stuebner 
12d1479d02SHeiko Stuebner #include <drm/drm_mipi_dsi.h>
13d1479d02SHeiko Stuebner #include <drm/drm_modes.h>
14d1479d02SHeiko Stuebner #include <drm/drm_panel.h>
15d1479d02SHeiko Stuebner 
16d1479d02SHeiko Stuebner #include <video/display_timing.h>
17d1479d02SHeiko Stuebner #include <video/mipi_display.h>
18d1479d02SHeiko Stuebner 
19d1479d02SHeiko Stuebner #include <linux/delay.h>
20d1479d02SHeiko Stuebner #include <linux/gpio/consumer.h>
21d1479d02SHeiko Stuebner #include <linux/media-bus-format.h>
22d1479d02SHeiko Stuebner #include <linux/module.h>
23d1479d02SHeiko Stuebner #include <linux/of.h>
24d1479d02SHeiko Stuebner #include <linux/regulator/consumer.h>
25d1479d02SHeiko Stuebner 
26d1479d02SHeiko Stuebner /* Manufacturer specific Commands send via DSI */
27d1479d02SHeiko Stuebner #define XPP055C272_CMD_ALL_PIXEL_OFF	0x22
28d1479d02SHeiko Stuebner #define XPP055C272_CMD_ALL_PIXEL_ON	0x23
29d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETDISP		0xb2
30d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETRGBIF		0xb3
31d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETCYC		0xb4
32d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETBGP		0xb5
33d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETVCOM		0xb6
34d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETOTP		0xb7
35d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPOWER_EXT	0xb8
36d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETEXTC		0xb9
37d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETMIPI		0xbA
38d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETVDC		0xbc
39d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPCR		0xbf
40d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETSCR		0xc0
41d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPOWER		0xc1
42d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETECO		0xc6
43d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETPANEL		0xcc
44d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETGAMMA		0xe0
45d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETEQ		0xe3
46d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETGIP1		0xe9
47d1479d02SHeiko Stuebner #define XPP055C272_CMD_SETGIP2		0xea
48d1479d02SHeiko Stuebner 
49d1479d02SHeiko Stuebner struct xpp055c272 {
50d1479d02SHeiko Stuebner 	struct device *dev;
51d1479d02SHeiko Stuebner 	struct drm_panel panel;
52d1479d02SHeiko Stuebner 	struct gpio_desc *reset_gpio;
53d1479d02SHeiko Stuebner 	struct regulator *vci;
54d1479d02SHeiko Stuebner 	struct regulator *iovcc;
55d1479d02SHeiko Stuebner 	bool prepared;
56d1479d02SHeiko Stuebner };
57d1479d02SHeiko Stuebner 
panel_to_xpp055c272(struct drm_panel * panel)58d1479d02SHeiko Stuebner static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)
59d1479d02SHeiko Stuebner {
60d1479d02SHeiko Stuebner 	return container_of(panel, struct xpp055c272, panel);
61d1479d02SHeiko Stuebner }
62d1479d02SHeiko Stuebner 
xpp055c272_init_sequence(struct xpp055c272 * ctx)63d1479d02SHeiko Stuebner static int xpp055c272_init_sequence(struct xpp055c272 *ctx)
64d1479d02SHeiko Stuebner {
65d1479d02SHeiko Stuebner 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
66d1479d02SHeiko Stuebner 	struct device *dev = ctx->dev;
67d1479d02SHeiko Stuebner 
68d1479d02SHeiko Stuebner 	/*
69d1479d02SHeiko Stuebner 	 * Init sequence was supplied by the panel vendor without much
70d1479d02SHeiko Stuebner 	 * documentation.
71d1479d02SHeiko Stuebner 	 */
72*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83);
73*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETMIPI,
74d1479d02SHeiko Stuebner 			       0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,
75d1479d02SHeiko Stuebner 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
76d1479d02SHeiko Stuebner 			       0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,
77d1479d02SHeiko Stuebner 			       0x00, 0x00, 0x37);
78*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25);
79*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00);
80*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETRGBIF,
81d1479d02SHeiko Stuebner 			       0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,
82d1479d02SHeiko Stuebner 			       0x00, 0x00);
83*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETSCR,
84d1479d02SHeiko Stuebner 			       0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
85d1479d02SHeiko Stuebner 			       0x00);
86*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46);
87*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b);
88*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80);
89*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30);
90*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEQ,
91d1479d02SHeiko Stuebner 			       0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
92d1479d02SHeiko Stuebner 			       0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
93*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER,
94d1479d02SHeiko Stuebner 			       0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,
95d1479d02SHeiko Stuebner 			       0x67, 0x77, 0x33, 0x33);
96*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff,
97d1479d02SHeiko Stuebner 			       0xff, 0x01, 0xff);
98*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09);
99d1479d02SHeiko Stuebner 	msleep(20);
100d1479d02SHeiko Stuebner 
101*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95);
102*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP1,
103d1479d02SHeiko Stuebner 			       0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,
104d1479d02SHeiko Stuebner 			       0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,
105d1479d02SHeiko Stuebner 			       0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
106d1479d02SHeiko Stuebner 			       0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,
107d1479d02SHeiko Stuebner 			       0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,
108d1479d02SHeiko Stuebner 			       0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,
109d1479d02SHeiko Stuebner 			       0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
110d1479d02SHeiko Stuebner 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
111*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP2,
112d1479d02SHeiko Stuebner 			       0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
113d1479d02SHeiko Stuebner 			       0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,
114d1479d02SHeiko Stuebner 			       0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,
115d1479d02SHeiko Stuebner 			       0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,
116d1479d02SHeiko Stuebner 			       0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,
117d1479d02SHeiko Stuebner 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118d1479d02SHeiko Stuebner 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,
119d1479d02SHeiko Stuebner 			       0xa0, 0x00, 0x00, 0x00, 0x00);
120*1a4cf058SJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGAMMA,
121d1479d02SHeiko Stuebner 			       0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,
122d1479d02SHeiko Stuebner 			       0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,
123d1479d02SHeiko Stuebner 			       0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,
124d1479d02SHeiko Stuebner 			       0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,
125d1479d02SHeiko Stuebner 			       0x11, 0x18);
126d1479d02SHeiko Stuebner 
127d1479d02SHeiko Stuebner 	msleep(60);
128d1479d02SHeiko Stuebner 
129a25b6b27SSam Ravnborg 	dev_dbg(dev, "Panel init sequence done\n");
130d1479d02SHeiko Stuebner 	return 0;
131d1479d02SHeiko Stuebner }
132d1479d02SHeiko Stuebner 
xpp055c272_unprepare(struct drm_panel * panel)133d1479d02SHeiko Stuebner static int xpp055c272_unprepare(struct drm_panel *panel)
134d1479d02SHeiko Stuebner {
135d1479d02SHeiko Stuebner 	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
136d1479d02SHeiko Stuebner 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
137d1479d02SHeiko Stuebner 	int ret;
138d1479d02SHeiko Stuebner 
139d1479d02SHeiko Stuebner 	if (!ctx->prepared)
140d1479d02SHeiko Stuebner 		return 0;
141d1479d02SHeiko Stuebner 
142d1479d02SHeiko Stuebner 	ret = mipi_dsi_dcs_set_display_off(dsi);
143d1479d02SHeiko Stuebner 	if (ret < 0)
144a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "failed to set display off: %d\n", ret);
145d1479d02SHeiko Stuebner 
146d1479d02SHeiko Stuebner 	mipi_dsi_dcs_enter_sleep_mode(dsi);
147d1479d02SHeiko Stuebner 	if (ret < 0) {
148a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
149d1479d02SHeiko Stuebner 		return ret;
150d1479d02SHeiko Stuebner 	}
151d1479d02SHeiko Stuebner 
152d1479d02SHeiko Stuebner 	regulator_disable(ctx->iovcc);
153d1479d02SHeiko Stuebner 	regulator_disable(ctx->vci);
154d1479d02SHeiko Stuebner 
155d1479d02SHeiko Stuebner 	ctx->prepared = false;
156d1479d02SHeiko Stuebner 
157d1479d02SHeiko Stuebner 	return 0;
158d1479d02SHeiko Stuebner }
159d1479d02SHeiko Stuebner 
xpp055c272_prepare(struct drm_panel * panel)160d1479d02SHeiko Stuebner static int xpp055c272_prepare(struct drm_panel *panel)
161d1479d02SHeiko Stuebner {
162d1479d02SHeiko Stuebner 	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
163d1479d02SHeiko Stuebner 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
164d1479d02SHeiko Stuebner 	int ret;
165d1479d02SHeiko Stuebner 
166d1479d02SHeiko Stuebner 	if (ctx->prepared)
167d1479d02SHeiko Stuebner 		return 0;
168d1479d02SHeiko Stuebner 
169a25b6b27SSam Ravnborg 	dev_dbg(ctx->dev, "Resetting the panel\n");
170d1479d02SHeiko Stuebner 	ret = regulator_enable(ctx->vci);
171d1479d02SHeiko Stuebner 	if (ret < 0) {
172a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
173d1479d02SHeiko Stuebner 		return ret;
174d1479d02SHeiko Stuebner 	}
175d1479d02SHeiko Stuebner 	ret = regulator_enable(ctx->iovcc);
176d1479d02SHeiko Stuebner 	if (ret < 0) {
177a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
178d1479d02SHeiko Stuebner 		goto disable_vci;
179d1479d02SHeiko Stuebner 	}
180d1479d02SHeiko Stuebner 
181d1479d02SHeiko Stuebner 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
182d1479d02SHeiko Stuebner 	/* T6: 10us */
183d1479d02SHeiko Stuebner 	usleep_range(10, 20);
184d1479d02SHeiko Stuebner 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
185d1479d02SHeiko Stuebner 
186d1479d02SHeiko Stuebner 	/* T8: 20ms */
187d1479d02SHeiko Stuebner 	msleep(20);
188d1479d02SHeiko Stuebner 
189d1479d02SHeiko Stuebner 	ret = xpp055c272_init_sequence(ctx);
190d1479d02SHeiko Stuebner 	if (ret < 0) {
191a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
192d1479d02SHeiko Stuebner 		goto disable_iovcc;
193d1479d02SHeiko Stuebner 	}
194d1479d02SHeiko Stuebner 
195d1479d02SHeiko Stuebner 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
196d1479d02SHeiko Stuebner 	if (ret < 0) {
197a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
198d1479d02SHeiko Stuebner 		goto disable_iovcc;
199d1479d02SHeiko Stuebner 	}
200d1479d02SHeiko Stuebner 
201d1479d02SHeiko Stuebner 	/* T9: 120ms */
202d1479d02SHeiko Stuebner 	msleep(120);
203d1479d02SHeiko Stuebner 
204d1479d02SHeiko Stuebner 	ret = mipi_dsi_dcs_set_display_on(dsi);
205d1479d02SHeiko Stuebner 	if (ret < 0) {
206a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
207d1479d02SHeiko Stuebner 		goto disable_iovcc;
208d1479d02SHeiko Stuebner 	}
209d1479d02SHeiko Stuebner 
210d1479d02SHeiko Stuebner 	msleep(50);
211d1479d02SHeiko Stuebner 
212d1479d02SHeiko Stuebner 	ctx->prepared = true;
213d1479d02SHeiko Stuebner 
214d1479d02SHeiko Stuebner 	return 0;
215d1479d02SHeiko Stuebner 
216d1479d02SHeiko Stuebner disable_iovcc:
217d1479d02SHeiko Stuebner 	regulator_disable(ctx->iovcc);
218d1479d02SHeiko Stuebner disable_vci:
219d1479d02SHeiko Stuebner 	regulator_disable(ctx->vci);
220d1479d02SHeiko Stuebner 	return ret;
221d1479d02SHeiko Stuebner }
222d1479d02SHeiko Stuebner 
223d1479d02SHeiko Stuebner static const struct drm_display_mode default_mode = {
224d1479d02SHeiko Stuebner 	.hdisplay	= 720,
225d1479d02SHeiko Stuebner 	.hsync_start	= 720 + 40,
226d1479d02SHeiko Stuebner 	.hsync_end	= 720 + 40 + 10,
227d1479d02SHeiko Stuebner 	.htotal		= 720 + 40 + 10 + 40,
228d1479d02SHeiko Stuebner 	.vdisplay	= 1280,
229d1479d02SHeiko Stuebner 	.vsync_start	= 1280 + 22,
230d1479d02SHeiko Stuebner 	.vsync_end	= 1280 + 22 + 4,
231d1479d02SHeiko Stuebner 	.vtotal		= 1280 + 22 + 4 + 11,
232d1479d02SHeiko Stuebner 	.clock		= 64000,
233d1479d02SHeiko Stuebner 	.width_mm	= 68,
234d1479d02SHeiko Stuebner 	.height_mm	= 121,
235d1479d02SHeiko Stuebner };
236d1479d02SHeiko Stuebner 
xpp055c272_get_modes(struct drm_panel * panel,struct drm_connector * connector)237d1479d02SHeiko Stuebner static int xpp055c272_get_modes(struct drm_panel *panel,
238d1479d02SHeiko Stuebner 				struct drm_connector *connector)
239d1479d02SHeiko Stuebner {
240d1479d02SHeiko Stuebner 	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
241d1479d02SHeiko Stuebner 	struct drm_display_mode *mode;
242d1479d02SHeiko Stuebner 
243d1479d02SHeiko Stuebner 	mode = drm_mode_duplicate(connector->dev, &default_mode);
244d1479d02SHeiko Stuebner 	if (!mode) {
245a25b6b27SSam Ravnborg 		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
246d1479d02SHeiko Stuebner 			default_mode.hdisplay, default_mode.vdisplay,
2470425662fSVille Syrjälä 			drm_mode_vrefresh(&default_mode));
248d1479d02SHeiko Stuebner 		return -ENOMEM;
249d1479d02SHeiko Stuebner 	}
250d1479d02SHeiko Stuebner 
251d1479d02SHeiko Stuebner 	drm_mode_set_name(mode);
252d1479d02SHeiko Stuebner 
253d1479d02SHeiko Stuebner 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
254d1479d02SHeiko Stuebner 	connector->display_info.width_mm = mode->width_mm;
255d1479d02SHeiko Stuebner 	connector->display_info.height_mm = mode->height_mm;
256d1479d02SHeiko Stuebner 	drm_mode_probed_add(connector, mode);
257d1479d02SHeiko Stuebner 
258d1479d02SHeiko Stuebner 	return 1;
259d1479d02SHeiko Stuebner }
260d1479d02SHeiko Stuebner 
261d1479d02SHeiko Stuebner static const struct drm_panel_funcs xpp055c272_funcs = {
262d1479d02SHeiko Stuebner 	.unprepare	= xpp055c272_unprepare,
263d1479d02SHeiko Stuebner 	.prepare	= xpp055c272_prepare,
264d1479d02SHeiko Stuebner 	.get_modes	= xpp055c272_get_modes,
265d1479d02SHeiko Stuebner };
266d1479d02SHeiko Stuebner 
xpp055c272_probe(struct mipi_dsi_device * dsi)267d1479d02SHeiko Stuebner static int xpp055c272_probe(struct mipi_dsi_device *dsi)
268d1479d02SHeiko Stuebner {
269d1479d02SHeiko Stuebner 	struct device *dev = &dsi->dev;
270d1479d02SHeiko Stuebner 	struct xpp055c272 *ctx;
271d1479d02SHeiko Stuebner 	int ret;
272d1479d02SHeiko Stuebner 
273d1479d02SHeiko Stuebner 	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
274d1479d02SHeiko Stuebner 	if (!ctx)
275d1479d02SHeiko Stuebner 		return -ENOMEM;
276d1479d02SHeiko Stuebner 
277d1479d02SHeiko Stuebner 	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
278e82ef424SCai Huoqing 	if (IS_ERR(ctx->reset_gpio))
279e82ef424SCai Huoqing 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
280e82ef424SCai Huoqing 				     "cannot get reset gpio\n");
281d1479d02SHeiko Stuebner 
282d1479d02SHeiko Stuebner 	ctx->vci = devm_regulator_get(dev, "vci");
283e82ef424SCai Huoqing 	if (IS_ERR(ctx->vci))
284e82ef424SCai Huoqing 		return dev_err_probe(dev, PTR_ERR(ctx->vci),
285e82ef424SCai Huoqing 				     "Failed to request vci regulator\n");
286d1479d02SHeiko Stuebner 
287d1479d02SHeiko Stuebner 	ctx->iovcc = devm_regulator_get(dev, "iovcc");
288e82ef424SCai Huoqing 	if (IS_ERR(ctx->iovcc))
289e82ef424SCai Huoqing 		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
290e82ef424SCai Huoqing 				     "Failed to request iovcc regulator\n");
291d1479d02SHeiko Stuebner 
292d1479d02SHeiko Stuebner 	mipi_dsi_set_drvdata(dsi, ctx);
293d1479d02SHeiko Stuebner 
294d1479d02SHeiko Stuebner 	ctx->dev = dev;
295d1479d02SHeiko Stuebner 
296d1479d02SHeiko Stuebner 	dsi->lanes = 4;
297d1479d02SHeiko Stuebner 	dsi->format = MIPI_DSI_FMT_RGB888;
298d1479d02SHeiko Stuebner 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
2990f3b68b6SNicolas Boichat 			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
300d1479d02SHeiko Stuebner 
301d1479d02SHeiko Stuebner 	drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
302d1479d02SHeiko Stuebner 		       DRM_MODE_CONNECTOR_DSI);
303d1479d02SHeiko Stuebner 
304d1479d02SHeiko Stuebner 	ret = drm_panel_of_backlight(&ctx->panel);
305d1479d02SHeiko Stuebner 	if (ret)
306d1479d02SHeiko Stuebner 		return ret;
307d1479d02SHeiko Stuebner 
308d1479d02SHeiko Stuebner 	drm_panel_add(&ctx->panel);
309d1479d02SHeiko Stuebner 
310d1479d02SHeiko Stuebner 	ret = mipi_dsi_attach(dsi);
311d1479d02SHeiko Stuebner 	if (ret < 0) {
312a25b6b27SSam Ravnborg 		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
313d1479d02SHeiko Stuebner 		drm_panel_remove(&ctx->panel);
314d1479d02SHeiko Stuebner 		return ret;
315d1479d02SHeiko Stuebner 	}
316d1479d02SHeiko Stuebner 
317d1479d02SHeiko Stuebner 	return 0;
318d1479d02SHeiko Stuebner }
319d1479d02SHeiko Stuebner 
xpp055c272_shutdown(struct mipi_dsi_device * dsi)320d1479d02SHeiko Stuebner static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
321d1479d02SHeiko Stuebner {
322d1479d02SHeiko Stuebner 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
323d1479d02SHeiko Stuebner 	int ret;
324d1479d02SHeiko Stuebner 
325d1479d02SHeiko Stuebner 	ret = drm_panel_unprepare(&ctx->panel);
326d1479d02SHeiko Stuebner 	if (ret < 0)
327a25b6b27SSam Ravnborg 		dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
328d1479d02SHeiko Stuebner 
329d1479d02SHeiko Stuebner 	ret = drm_panel_disable(&ctx->panel);
330d1479d02SHeiko Stuebner 	if (ret < 0)
331a25b6b27SSam Ravnborg 		dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
332d1479d02SHeiko Stuebner }
333d1479d02SHeiko Stuebner 
xpp055c272_remove(struct mipi_dsi_device * dsi)33479abca2bSUwe Kleine-König static void xpp055c272_remove(struct mipi_dsi_device *dsi)
335d1479d02SHeiko Stuebner {
336d1479d02SHeiko Stuebner 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
337d1479d02SHeiko Stuebner 	int ret;
338d1479d02SHeiko Stuebner 
339d1479d02SHeiko Stuebner 	xpp055c272_shutdown(dsi);
340d1479d02SHeiko Stuebner 
341d1479d02SHeiko Stuebner 	ret = mipi_dsi_detach(dsi);
342d1479d02SHeiko Stuebner 	if (ret < 0)
343a25b6b27SSam Ravnborg 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
344d1479d02SHeiko Stuebner 
345d1479d02SHeiko Stuebner 	drm_panel_remove(&ctx->panel);
346d1479d02SHeiko Stuebner }
347d1479d02SHeiko Stuebner 
348d1479d02SHeiko Stuebner static const struct of_device_id xpp055c272_of_match[] = {
349d1479d02SHeiko Stuebner 	{ .compatible = "xinpeng,xpp055c272" },
350d1479d02SHeiko Stuebner 	{ /* sentinel */ }
351d1479d02SHeiko Stuebner };
352d1479d02SHeiko Stuebner MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
353d1479d02SHeiko Stuebner 
354d1479d02SHeiko Stuebner static struct mipi_dsi_driver xpp055c272_driver = {
355d1479d02SHeiko Stuebner 	.driver = {
356d1479d02SHeiko Stuebner 		.name = "panel-xinpeng-xpp055c272",
357d1479d02SHeiko Stuebner 		.of_match_table = xpp055c272_of_match,
358d1479d02SHeiko Stuebner 	},
359d1479d02SHeiko Stuebner 	.probe	= xpp055c272_probe,
360d1479d02SHeiko Stuebner 	.remove = xpp055c272_remove,
361d1479d02SHeiko Stuebner 	.shutdown = xpp055c272_shutdown,
362d1479d02SHeiko Stuebner };
363d1479d02SHeiko Stuebner module_mipi_dsi_driver(xpp055c272_driver);
364d1479d02SHeiko Stuebner 
365d1479d02SHeiko Stuebner MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
366d1479d02SHeiko Stuebner MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
367d1479d02SHeiko Stuebner MODULE_LICENSE("GPL v2");
368