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