Loading...
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");
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
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
71static 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
141static 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
168static 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
224disable_iovcc:
225 regulator_disable(ctx->iovcc);
226disable_vci:
227 regulator_disable(ctx->vci);
228 return ret;
229}
230
231static 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
245static 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
269static const struct drm_panel_funcs xpp055c272_funcs = {
270 .unprepare = xpp055c272_unprepare,
271 .prepare = xpp055c272_prepare,
272 .get_modes = xpp055c272_get_modes,
273};
274
275static 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
328static 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
342static 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
356static const struct of_device_id xpp055c272_of_match[] = {
357 { .compatible = "xinpeng,xpp055c272" },
358 { /* sentinel */ }
359};
360MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
361
362static 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};
371module_mipi_dsi_driver(xpp055c272_driver);
372
373MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
374MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
375MODULE_LICENSE("GPL v2");