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 
panel_to_xpp055c272(struct drm_panel * panel)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 
xpp055c272_init_sequence(struct xpp055c272 * ctx)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 
xpp055c272_unprepare(struct drm_panel * panel)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 
xpp055c272_prepare(struct drm_panel * panel)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 
xpp055c272_get_modes(struct drm_panel * panel,struct drm_connector * connector)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 
xpp055c272_probe(struct mipi_dsi_device * dsi)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 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
288 				     "cannot get reset gpio\n");
289 
290 	ctx->vci = devm_regulator_get(dev, "vci");
291 	if (IS_ERR(ctx->vci))
292 		return dev_err_probe(dev, PTR_ERR(ctx->vci),
293 				     "Failed to request vci regulator\n");
294 
295 	ctx->iovcc = devm_regulator_get(dev, "iovcc");
296 	if (IS_ERR(ctx->iovcc))
297 		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
298 				     "Failed to request iovcc regulator\n");
299 
300 	mipi_dsi_set_drvdata(dsi, ctx);
301 
302 	ctx->dev = dev;
303 
304 	dsi->lanes = 4;
305 	dsi->format = MIPI_DSI_FMT_RGB888;
306 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
307 			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
308 
309 	drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
310 		       DRM_MODE_CONNECTOR_DSI);
311 
312 	ret = drm_panel_of_backlight(&ctx->panel);
313 	if (ret)
314 		return ret;
315 
316 	drm_panel_add(&ctx->panel);
317 
318 	ret = mipi_dsi_attach(dsi);
319 	if (ret < 0) {
320 		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
321 		drm_panel_remove(&ctx->panel);
322 		return ret;
323 	}
324 
325 	return 0;
326 }
327 
xpp055c272_shutdown(struct mipi_dsi_device * dsi)328 static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
329 {
330 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
331 	int ret;
332 
333 	ret = drm_panel_unprepare(&ctx->panel);
334 	if (ret < 0)
335 		dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
336 
337 	ret = drm_panel_disable(&ctx->panel);
338 	if (ret < 0)
339 		dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
340 }
341 
xpp055c272_remove(struct mipi_dsi_device * dsi)342 static void xpp055c272_remove(struct mipi_dsi_device *dsi)
343 {
344 	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
345 	int ret;
346 
347 	xpp055c272_shutdown(dsi);
348 
349 	ret = mipi_dsi_detach(dsi);
350 	if (ret < 0)
351 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
352 
353 	drm_panel_remove(&ctx->panel);
354 }
355 
356 static const struct of_device_id xpp055c272_of_match[] = {
357 	{ .compatible = "xinpeng,xpp055c272" },
358 	{ /* sentinel */ }
359 };
360 MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
361 
362 static struct mipi_dsi_driver xpp055c272_driver = {
363 	.driver = {
364 		.name = "panel-xinpeng-xpp055c272",
365 		.of_match_table = xpp055c272_of_match,
366 	},
367 	.probe	= xpp055c272_probe,
368 	.remove = xpp055c272_remove,
369 	.shutdown = xpp055c272_shutdown,
370 };
371 module_mipi_dsi_driver(xpp055c272_driver);
372 
373 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
374 MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
375 MODULE_LICENSE("GPL v2");
376