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