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
49struct 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
58static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)
59{
60	return container_of(panel, struct xpp055c272, panel);
61}
62
63static 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
133static 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
160static 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
216disable_iovcc:
217	regulator_disable(ctx->iovcc);
218disable_vci:
219	regulator_disable(ctx->vci);
220	return ret;
221}
222
223static 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
237static 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
261static const struct drm_panel_funcs xpp055c272_funcs = {
262	.unprepare	= xpp055c272_unprepare,
263	.prepare	= xpp055c272_prepare,
264	.get_modes	= xpp055c272_get_modes,
265};
266
267static 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
320static 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
334static 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
348static const struct of_device_id xpp055c272_of_match[] = {
349	{ .compatible = "xinpeng,xpp055c272" },
350	{ /* sentinel */ }
351};
352MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
353
354static 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};
363module_mipi_dsi_driver(xpp055c272_driver);
364
365MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
366MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
367MODULE_LICENSE("GPL v2");
368