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