xref: /linux/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c (revision 6beeaf48db6c548fcfc2ad32739d33af2fef3a5b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver
4  * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH
5  *
6  * based on
7  *
8  * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
9  * Copyright (C) Purism SPC 2019
10  */
11 
12 #include <drm/drm_mipi_dsi.h>
13 #include <drm/drm_modes.h>
14 #include <drm/drm_panel.h>
15 
16 #include <video/display_timing.h>
17 #include <video/mipi_display.h>
18 
19 #include <linux/delay.h>
20 #include <linux/gpio/consumer.h>
21 #include <linux/media-bus-format.h>
22 #include <linux/module.h>
23 #include <linux/of.h>
24 #include <linux/regulator/consumer.h>
25 
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
48 
49 struct xpp055c272 {
50 	struct device *dev;
51 	struct drm_panel panel;
52 	struct gpio_desc *reset_gpio;
53 	struct regulator *vci;
54 	struct regulator *iovcc;
55 	bool prepared;
56 };
57 
58 static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)
59 {
60 	return container_of(panel, struct xpp055c272, panel);
61 }
62 
63 #define dsi_generic_write_seq(dsi, cmd, seq...) do {			\
64 		static const u8 b[] = { cmd, seq };			\
65 		int ret;						\
66 		ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b));	\
67 		if (ret < 0)						\
68 			return ret;					\
69 	} while (0)
70 
71 static int xpp055c272_init_sequence(struct xpp055c272 *ctx)
72 {
73 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
74 	struct device *dev = ctx->dev;
75 
76 	/*
77 	 * Init sequence was supplied by the panel vendor without much
78 	 * documentation.
79 	 */
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,
85 			      0x00, 0x00, 0x37);
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,
90 			      0x00, 0x00);
91 	dsi_generic_write_seq(dsi, XPP055C272_CMD_SETSCR,
92 			      0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
93 			      0x00);
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,
105 			      0xff, 0x01, 0xff);
106 	dsi_generic_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09);
107 	msleep(20);
108 
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,
133 			      0x11, 0x18);
134 
135 	msleep(60);
136 
137 	dev_dbg(dev, "Panel init sequence done\n");
138 	return 0;
139 }
140 
141 static int xpp055c272_unprepare(struct drm_panel *panel)
142 {
143 	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
144 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
145 	int ret;
146 
147 	if (!ctx->prepared)
148 		return 0;
149 
150 	ret = mipi_dsi_dcs_set_display_off(dsi);
151 	if (ret < 0)
152 		dev_err(ctx->dev, "failed to set display off: %d\n", ret);
153 
154 	mipi_dsi_dcs_enter_sleep_mode(dsi);
155 	if (ret < 0) {
156 		dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
157 		return ret;
158 	}
159 
160 	regulator_disable(ctx->iovcc);
161 	regulator_disable(ctx->vci);
162 
163 	ctx->prepared = false;
164 
165 	return 0;
166 }
167 
168 static int xpp055c272_prepare(struct drm_panel *panel)
169 {
170 	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
171 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
172 	int ret;
173 
174 	if (ctx->prepared)
175 		return 0;
176 
177 	dev_dbg(ctx->dev, "Resetting the panel\n");
178 	ret = regulator_enable(ctx->vci);
179 	if (ret < 0) {
180 		dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
181 		return ret;
182 	}
183 	ret = regulator_enable(ctx->iovcc);
184 	if (ret < 0) {
185 		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
186 		goto disable_vci;
187 	}
188 
189 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
190 	/* T6: 10us */
191 	usleep_range(10, 20);
192 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
193 
194 	/* T8: 20ms */
195 	msleep(20);
196 
197 	ret = xpp055c272_init_sequence(ctx);
198 	if (ret < 0) {
199 		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
200 		goto disable_iovcc;
201 	}
202 
203 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
204 	if (ret < 0) {
205 		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
206 		goto disable_iovcc;
207 	}
208 
209 	/* T9: 120ms */
210 	msleep(120);
211 
212 	ret = mipi_dsi_dcs_set_display_on(dsi);
213 	if (ret < 0) {
214 		dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
215 		goto disable_iovcc;
216 	}
217 
218 	msleep(50);
219 
220 	ctx->prepared = true;
221 
222 	return 0;
223 
224 disable_iovcc:
225 	regulator_disable(ctx->iovcc);
226 disable_vci:
227 	regulator_disable(ctx->vci);
228 	return ret;
229 }
230 
231 static const struct drm_display_mode default_mode = {
232 	.hdisplay	= 720,
233 	.hsync_start	= 720 + 40,
234 	.hsync_end	= 720 + 40 + 10,
235 	.htotal		= 720 + 40 + 10 + 40,
236 	.vdisplay	= 1280,
237 	.vsync_start	= 1280 + 22,
238 	.vsync_end	= 1280 + 22 + 4,
239 	.vtotal		= 1280 + 22 + 4 + 11,
240 	.clock		= 64000,
241 	.width_mm	= 68,
242 	.height_mm	= 121,
243 };
244 
245 static int xpp055c272_get_modes(struct drm_panel *panel,
246 				struct drm_connector *connector)
247 {
248 	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
249 	struct drm_display_mode *mode;
250 
251 	mode = drm_mode_duplicate(connector->dev, &default_mode);
252 	if (!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));
256 		return -ENOMEM;
257 	}
258 
259 	drm_mode_set_name(mode);
260 
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);
265 
266 	return 1;
267 }
268 
269 static const struct drm_panel_funcs xpp055c272_funcs = {
270 	.unprepare	= xpp055c272_unprepare,
271 	.prepare	= xpp055c272_prepare,
272 	.get_modes	= xpp055c272_get_modes,
273 };
274 
275 static int xpp055c272_probe(struct mipi_dsi_device *dsi)
276 {
277 	struct device *dev = &dsi->dev;
278 	struct xpp055c272 *ctx;
279 	int ret;
280 
281 	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
282 	if (!ctx)
283 		return -ENOMEM;
284 
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);
289 	}
290 
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);
296 		return ret;
297 	}
298 
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);
304 		return ret;
305 	}
306 
307 	mipi_dsi_set_drvdata(dsi, ctx);
308 
309 	ctx->dev = dev;
310 
311 	dsi->lanes = 4;
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_NO_EOT_PACKET;
315 
316 	drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
317 		       DRM_MODE_CONNECTOR_DSI);
318 
319 	ret = drm_panel_of_backlight(&ctx->panel);
320 	if (ret)
321 		return ret;
322 
323 	drm_panel_add(&ctx->panel);
324 
325 	ret = mipi_dsi_attach(dsi);
326 	if (ret < 0) {
327 		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
328 		drm_panel_remove(&ctx->panel);
329 		return ret;
330 	}
331 
332 	return 0;
333 }
334 
335 static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
336 {
337 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
338 	int ret;
339 
340 	ret = drm_panel_unprepare(&ctx->panel);
341 	if (ret < 0)
342 		dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
343 
344 	ret = drm_panel_disable(&ctx->panel);
345 	if (ret < 0)
346 		dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
347 }
348 
349 static int xpp055c272_remove(struct mipi_dsi_device *dsi)
350 {
351 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
352 	int ret;
353 
354 	xpp055c272_shutdown(dsi);
355 
356 	ret = mipi_dsi_detach(dsi);
357 	if (ret < 0)
358 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
359 
360 	drm_panel_remove(&ctx->panel);
361 
362 	return 0;
363 }
364 
365 static const struct of_device_id xpp055c272_of_match[] = {
366 	{ .compatible = "xinpeng,xpp055c272" },
367 	{ /* sentinel */ }
368 };
369 MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
370 
371 static struct mipi_dsi_driver xpp055c272_driver = {
372 	.driver = {
373 		.name = "panel-xinpeng-xpp055c272",
374 		.of_match_table = xpp055c272_of_match,
375 	},
376 	.probe	= xpp055c272_probe,
377 	.remove = xpp055c272_remove,
378 	.shutdown = xpp055c272_shutdown,
379 };
380 module_mipi_dsi_driver(xpp055c272_driver);
381 
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");
385