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