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