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 d[] = { seq };				\
66 		int ret;						\
67 		ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d));	\
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 	.vrefresh	= 60,
247 	.clock		= 64000,
248 	.width_mm	= 68,
249 	.height_mm	= 121,
250 };
251 
252 static int xpp055c272_get_modes(struct drm_panel *panel,
253 				struct drm_connector *connector)
254 {
255 	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
256 	struct drm_display_mode *mode;
257 
258 	mode = drm_mode_duplicate(connector->dev, &default_mode);
259 	if (!mode) {
260 		DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n",
261 			      default_mode.hdisplay, default_mode.vdisplay,
262 			      default_mode.vrefresh);
263 		return -ENOMEM;
264 	}
265 
266 	drm_mode_set_name(mode);
267 
268 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
269 	connector->display_info.width_mm = mode->width_mm;
270 	connector->display_info.height_mm = mode->height_mm;
271 	drm_mode_probed_add(connector, mode);
272 
273 	return 1;
274 }
275 
276 static const struct drm_panel_funcs xpp055c272_funcs = {
277 	.unprepare	= xpp055c272_unprepare,
278 	.prepare	= xpp055c272_prepare,
279 	.get_modes	= xpp055c272_get_modes,
280 };
281 
282 static int xpp055c272_probe(struct mipi_dsi_device *dsi)
283 {
284 	struct device *dev = &dsi->dev;
285 	struct xpp055c272 *ctx;
286 	int ret;
287 
288 	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
289 	if (!ctx)
290 		return -ENOMEM;
291 
292 	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
293 	if (IS_ERR(ctx->reset_gpio)) {
294 		DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
295 		return PTR_ERR(ctx->reset_gpio);
296 	}
297 
298 	ctx->vci = devm_regulator_get(dev, "vci");
299 	if (IS_ERR(ctx->vci)) {
300 		ret = PTR_ERR(ctx->vci);
301 		if (ret != -EPROBE_DEFER)
302 			DRM_DEV_ERROR(dev,
303 				      "Failed to request vci regulator: %d\n",
304 				      ret);
305 		return ret;
306 	}
307 
308 	ctx->iovcc = devm_regulator_get(dev, "iovcc");
309 	if (IS_ERR(ctx->iovcc)) {
310 		ret = PTR_ERR(ctx->iovcc);
311 		if (ret != -EPROBE_DEFER)
312 			DRM_DEV_ERROR(dev,
313 				      "Failed to request iovcc regulator: %d\n",
314 				      ret);
315 		return ret;
316 	}
317 
318 	mipi_dsi_set_drvdata(dsi, ctx);
319 
320 	ctx->dev = dev;
321 
322 	dsi->lanes = 4;
323 	dsi->format = MIPI_DSI_FMT_RGB888;
324 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
325 			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
326 
327 	drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
328 		       DRM_MODE_CONNECTOR_DSI);
329 
330 	ret = drm_panel_of_backlight(&ctx->panel);
331 	if (ret)
332 		return ret;
333 
334 	drm_panel_add(&ctx->panel);
335 
336 	ret = mipi_dsi_attach(dsi);
337 	if (ret < 0) {
338 		DRM_DEV_ERROR(dev, "mipi_dsi_attach failed: %d\n", ret);
339 		drm_panel_remove(&ctx->panel);
340 		return ret;
341 	}
342 
343 	return 0;
344 }
345 
346 static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
347 {
348 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
349 	int ret;
350 
351 	ret = drm_panel_unprepare(&ctx->panel);
352 	if (ret < 0)
353 		DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
354 			      ret);
355 
356 	ret = drm_panel_disable(&ctx->panel);
357 	if (ret < 0)
358 		DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
359 			      ret);
360 }
361 
362 static int xpp055c272_remove(struct mipi_dsi_device *dsi)
363 {
364 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
365 	int ret;
366 
367 	xpp055c272_shutdown(dsi);
368 
369 	ret = mipi_dsi_detach(dsi);
370 	if (ret < 0)
371 		DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
372 			      ret);
373 
374 	drm_panel_remove(&ctx->panel);
375 
376 	return 0;
377 }
378 
379 static const struct of_device_id xpp055c272_of_match[] = {
380 	{ .compatible = "xinpeng,xpp055c272" },
381 	{ /* sentinel */ }
382 };
383 MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
384 
385 static struct mipi_dsi_driver xpp055c272_driver = {
386 	.driver = {
387 		.name = "panel-xinpeng-xpp055c272",
388 		.of_match_table = xpp055c272_of_match,
389 	},
390 	.probe	= xpp055c272_probe,
391 	.remove = xpp055c272_remove,
392 	.shutdown = xpp055c272_shutdown,
393 };
394 module_mipi_dsi_driver(xpp055c272_driver);
395 
396 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
397 MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
398 MODULE_LICENSE("GPL v2");
399