1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2023 Alexander Warnecke <awarnecke002@hotmail.com>
4 * Copyright (c) 2023 Manuel Traut <manut@mecka.net>
5 * Copyright (c) 2023 Dang Huynh <danct12@riseup.net>
6 */
7
8#include <linux/delay.h>
9#include <linux/gpio/consumer.h>
10#include <linux/module.h>
11#include <linux/of.h>
12#include <linux/of_device.h>
13#include <linux/regulator/consumer.h>
14
15#include <drm/drm_connector.h>
16#include <drm/drm_mipi_dsi.h>
17#include <drm/drm_modes.h>
18#include <drm/drm_panel.h>
19#include <drm/drm_probe_helper.h>
20
21struct boe_th101mb31ig002;
22
23struct panel_desc {
24 const struct drm_display_mode *modes;
25 unsigned long mode_flags;
26 enum mipi_dsi_pixel_format format;
27 int (*init)(struct boe_th101mb31ig002 *ctx);
28 unsigned int lanes;
29 bool lp11_before_reset;
30 unsigned int vcioo_to_lp11_delay_ms;
31 unsigned int lp11_to_reset_delay_ms;
32 unsigned int backlight_off_to_display_off_delay_ms;
33 unsigned int enter_sleep_to_reset_down_delay_ms;
34 unsigned int power_off_delay_ms;
35};
36
37struct boe_th101mb31ig002 {
38 struct drm_panel panel;
39
40 struct mipi_dsi_device *dsi;
41
42 const struct panel_desc *desc;
43
44 struct regulator *power;
45 struct gpio_desc *enable;
46 struct gpio_desc *reset;
47
48 enum drm_panel_orientation orientation;
49};
50
51static void boe_th101mb31ig002_reset(struct boe_th101mb31ig002 *ctx)
52{
53 gpiod_direction_output(desc: ctx->reset, value: 0);
54 usleep_range(min: 10, max: 100);
55 gpiod_direction_output(desc: ctx->reset, value: 1);
56 usleep_range(min: 10, max: 100);
57 gpiod_direction_output(desc: ctx->reset, value: 0);
58 usleep_range(min: 5000, max: 6000);
59}
60
61static int boe_th101mb31ig002_enable(struct boe_th101mb31ig002 *ctx)
62{
63 struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
64
65 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0xab, 0xba);
66 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0xba, 0xab);
67 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x10, 0x01, 0x47, 0xff);
68 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x0c, 0x14, 0x04, 0x50, 0x50, 0x14);
69 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x56, 0x53, 0x00);
70 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x33, 0x30, 0x04);
71 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0xb0, 0x00, 0x00, 0x10, 0x00, 0x10,
72 0x00);
73 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb8, 0x05, 0x12, 0x29, 0x49, 0x48, 0x00,
74 0x00);
75 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x7c, 0x65, 0x55, 0x49, 0x46, 0x36,
76 0x3b, 0x24, 0x3d, 0x3c, 0x3d, 0x5c, 0x4c,
77 0x55, 0x47, 0x46, 0x39, 0x26, 0x06, 0x7c,
78 0x65, 0x55, 0x49, 0x46, 0x36, 0x3b, 0x24,
79 0x3d, 0x3c, 0x3d, 0x5c, 0x4c, 0x55, 0x47,
80 0x46, 0x39, 0x26, 0x06);
81 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0xff, 0x87, 0x12, 0x34, 0x44, 0x44,
82 0x44, 0x44, 0x98, 0x04, 0x98, 0x04, 0x0f,
83 0x00, 0x00, 0xc1);
84 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x54, 0x94, 0x02, 0x85, 0x9f, 0x00,
85 0x7f, 0x00, 0x54, 0x00);
86 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11,
87 0x22, 0x20, 0x44, 0xff, 0x18, 0x00);
88 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc3, 0x86, 0x46, 0x05, 0x05, 0x1c, 0x1c,
89 0x1d, 0x1d, 0x02, 0x1f, 0x1f, 0x1e, 0x1e,
90 0x0f, 0x0f, 0x0d, 0x0d, 0x13, 0x13, 0x11,
91 0x11, 0x00);
92 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc4, 0x07, 0x07, 0x04, 0x04, 0x1c, 0x1c,
93 0x1d, 0x1d, 0x02, 0x1f, 0x1f, 0x1e, 0x1e,
94 0x0e, 0x0e, 0x0c, 0x0c, 0x12, 0x12, 0x10,
95 0x10, 0x00);
96 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc6, 0x2a, 0x2a);
97 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16);
98 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xca, 0xcb, 0x43);
99 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcd, 0x0e, 0x4b, 0x4b, 0x20, 0x19, 0x6b,
100 0x06, 0xb3);
101 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd2, 0xe3, 0x2b, 0x38, 0x00);
102 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x00, 0x01, 0x00, 0x0e, 0x04, 0x44,
103 0x08, 0x10, 0x00, 0x00, 0x00);
104 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff,
105 0xff, 0xff);
106 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x12, 0x03, 0x20, 0x00, 0xff);
107 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x00);
108
109 mipi_dsi_dcs_exit_sleep_mode_multi(ctx: &dsi_ctx);
110
111 mipi_dsi_msleep(&dsi_ctx, 120);
112
113 mipi_dsi_dcs_set_display_on_multi(ctx: &dsi_ctx);
114
115 return dsi_ctx.accum_err;
116}
117
118static int starry_er88577_init_cmd(struct boe_th101mb31ig002 *ctx)
119{
120 struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
121
122 msleep(msecs: 70);
123
124 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0xab, 0xba);
125 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0xba, 0xab);
126 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x10, 0x01, 0x47, 0xff);
127 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x0c, 0x14, 0x04, 0x50, 0x50, 0x14);
128 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x56, 0x53, 0x00);
129 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x33, 0x30, 0x04);
130 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0xb0, 0x00, 0x00, 0x10, 0x00, 0x10,
131 0x00);
132 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb8, 0x05, 0x12, 0x29, 0x49, 0x40);
133 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x7c, 0x61, 0x4f, 0x42, 0x3e, 0x2d,
134 0x31, 0x1a, 0x33, 0x33, 0x33, 0x52, 0x40,
135 0x47, 0x38, 0x34, 0x26, 0x0e, 0x06, 0x7c,
136 0x61, 0x4f, 0x42, 0x3e, 0x2d, 0x31, 0x1a,
137 0x33, 0x33, 0x33, 0x52, 0x40, 0x47, 0x38,
138 0x34, 0x26, 0x0e, 0x06);
139 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc0, 0xcc, 0x76, 0x12, 0x34, 0x44, 0x44,
140 0x44, 0x44, 0x98, 0x04, 0x98, 0x04, 0x0f,
141 0x00, 0x00, 0xc1);
142 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x54, 0x94, 0x02, 0x85, 0x9f, 0x00,
143 0x6f, 0x00, 0x54, 0x00);
144 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11,
145 0x22, 0x20, 0x44, 0xff, 0x18, 0x00);
146 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc3, 0x87, 0x47, 0x05, 0x05, 0x1c, 0x1c,
147 0x1d, 0x1d, 0x02, 0x1e, 0x1e, 0x1f, 0x1f,
148 0x0f, 0x0f, 0x0d, 0x0d, 0x13, 0x13, 0x11,
149 0x11, 0x24);
150 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc4, 0x06, 0x06, 0x04, 0x04, 0x1c, 0x1c,
151 0x1d, 0x1d, 0x02, 0x1e, 0x1e, 0x1f, 0x1f,
152 0x0e, 0x0e, 0x0c, 0x0c, 0x12, 0x12, 0x10,
153 0x10, 0x24);
154 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16);
155 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xca, 0xcb, 0x43);
156 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcd, 0x0e, 0x4b, 0x4b, 0x20, 0x19, 0x6b,
157 0x06, 0xb3);
158 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd1, 0x40, 0x0d, 0xff, 0x0f);
159 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd2, 0xe3, 0x2b, 0x38, 0x08);
160 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd3, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x33, 0x20, 0x3a, 0xd5, 0x86, 0xf3);
162 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x00, 0x01, 0x00, 0x0e, 0x04, 0x44,
163 0x08, 0x10, 0x00, 0x00, 0x00);
164 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x80, 0x09, 0xff, 0xff, 0xff, 0xff,
165 0xff, 0xff);
166 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x12, 0x03, 0x20, 0x00, 0xff);
167 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x00);
168
169 mipi_dsi_dcs_exit_sleep_mode_multi(ctx: &dsi_ctx);
170
171 mipi_dsi_msleep(&dsi_ctx, 120);
172
173 mipi_dsi_dcs_set_display_on_multi(ctx: &dsi_ctx);
174
175 mipi_dsi_msleep(&dsi_ctx, 20);
176
177 return dsi_ctx.accum_err;
178}
179
180static int boe_th101mb31ig002_disable(struct drm_panel *panel)
181{
182 struct boe_th101mb31ig002 *ctx = container_of(panel,
183 struct boe_th101mb31ig002,
184 panel);
185 struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
186
187 if (ctx->desc->backlight_off_to_display_off_delay_ms)
188 mipi_dsi_msleep(&dsi_ctx, ctx->desc->backlight_off_to_display_off_delay_ms);
189
190 mipi_dsi_dcs_set_display_off_multi(ctx: &dsi_ctx);
191
192 mipi_dsi_msleep(&dsi_ctx, 120);
193
194 mipi_dsi_dcs_enter_sleep_mode_multi(ctx: &dsi_ctx);
195
196 if (ctx->desc->enter_sleep_to_reset_down_delay_ms)
197 mipi_dsi_msleep(&dsi_ctx, ctx->desc->enter_sleep_to_reset_down_delay_ms);
198
199 return dsi_ctx.accum_err;
200}
201
202static int boe_th101mb31ig002_unprepare(struct drm_panel *panel)
203{
204 struct boe_th101mb31ig002 *ctx = container_of(panel,
205 struct boe_th101mb31ig002,
206 panel);
207
208 gpiod_set_value_cansleep(desc: ctx->reset, value: 1);
209 gpiod_set_value_cansleep(desc: ctx->enable, value: 0);
210 regulator_disable(regulator: ctx->power);
211
212 if (ctx->desc->power_off_delay_ms)
213 msleep(msecs: ctx->desc->power_off_delay_ms);
214
215 return 0;
216}
217
218static int boe_th101mb31ig002_prepare(struct drm_panel *panel)
219{
220 struct boe_th101mb31ig002 *ctx = container_of(panel,
221 struct boe_th101mb31ig002,
222 panel);
223 struct device *dev = &ctx->dsi->dev;
224 int ret;
225
226 ret = regulator_enable(regulator: ctx->power);
227 if (ret) {
228 dev_err(dev, "Failed to enable power supply: %d\n", ret);
229 return ret;
230 }
231
232 if (ctx->desc->vcioo_to_lp11_delay_ms)
233 msleep(msecs: ctx->desc->vcioo_to_lp11_delay_ms);
234
235 if (ctx->desc->lp11_before_reset) {
236 ret = mipi_dsi_dcs_nop(dsi: ctx->dsi);
237 if (ret)
238 return ret;
239 }
240
241 if (ctx->desc->lp11_to_reset_delay_ms)
242 msleep(msecs: ctx->desc->lp11_to_reset_delay_ms);
243
244 gpiod_set_value_cansleep(desc: ctx->enable, value: 1);
245 msleep(msecs: 50);
246 boe_th101mb31ig002_reset(ctx);
247
248 ret = ctx->desc->init(ctx);
249 if (ret)
250 return ret;
251
252 return 0;
253}
254
255static const struct drm_display_mode boe_th101mb31ig002_default_mode = {
256 .clock = 73500,
257 .hdisplay = 800,
258 .hsync_start = 800 + 64,
259 .hsync_end = 800 + 64 + 16,
260 .htotal = 800 + 64 + 16 + 64,
261 .vdisplay = 1280,
262 .vsync_start = 1280 + 2,
263 .vsync_end = 1280 + 2 + 4,
264 .vtotal = 1280 + 2 + 4 + 12,
265 .width_mm = 135,
266 .height_mm = 216,
267 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
268};
269
270static const struct panel_desc boe_th101mb31ig002_desc = {
271 .modes = &boe_th101mb31ig002_default_mode,
272 .lanes = 4,
273 .format = MIPI_DSI_FMT_RGB888,
274 .mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
275 MIPI_DSI_MODE_NO_EOT_PACKET |
276 MIPI_DSI_MODE_LPM,
277 .init = boe_th101mb31ig002_enable,
278};
279
280static const struct drm_display_mode starry_er88577_default_mode = {
281 .clock = (800 + 25 + 25 + 25) * (1280 + 20 + 4 + 12) * 60 / 1000,
282 .hdisplay = 800,
283 .hsync_start = 800 + 25,
284 .hsync_end = 800 + 25 + 25,
285 .htotal = 800 + 25 + 25 + 25,
286 .vdisplay = 1280,
287 .vsync_start = 1280 + 20,
288 .vsync_end = 1280 + 20 + 4,
289 .vtotal = 1280 + 20 + 4 + 12,
290 .width_mm = 135,
291 .height_mm = 216,
292 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
293};
294
295static const struct panel_desc starry_er88577_desc = {
296 .modes = &starry_er88577_default_mode,
297 .lanes = 4,
298 .format = MIPI_DSI_FMT_RGB888,
299 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
300 MIPI_DSI_MODE_LPM,
301 .init = starry_er88577_init_cmd,
302 .lp11_before_reset = true,
303 .vcioo_to_lp11_delay_ms = 5,
304 .lp11_to_reset_delay_ms = 50,
305 .backlight_off_to_display_off_delay_ms = 100,
306 .enter_sleep_to_reset_down_delay_ms = 100,
307 .power_off_delay_ms = 1000,
308};
309
310static int boe_th101mb31ig002_get_modes(struct drm_panel *panel,
311 struct drm_connector *connector)
312{
313 struct boe_th101mb31ig002 *ctx = container_of(panel,
314 struct boe_th101mb31ig002,
315 panel);
316 const struct drm_display_mode *desc_mode = ctx->desc->modes;
317
318 connector->display_info.bpc = 8;
319 /*
320 * TODO: Remove once all drm drivers call
321 * drm_connector_set_orientation_from_panel()
322 */
323 drm_connector_set_panel_orientation(connector, panel_orientation: ctx->orientation);
324
325 return drm_connector_helper_get_modes_fixed(connector, fixed_mode: desc_mode);
326}
327
328static enum drm_panel_orientation
329boe_th101mb31ig002_get_orientation(struct drm_panel *panel)
330{
331 struct boe_th101mb31ig002 *ctx = container_of(panel,
332 struct boe_th101mb31ig002,
333 panel);
334
335 return ctx->orientation;
336}
337
338static const struct drm_panel_funcs boe_th101mb31ig002_funcs = {
339 .prepare = boe_th101mb31ig002_prepare,
340 .unprepare = boe_th101mb31ig002_unprepare,
341 .disable = boe_th101mb31ig002_disable,
342 .get_modes = boe_th101mb31ig002_get_modes,
343 .get_orientation = boe_th101mb31ig002_get_orientation,
344};
345
346static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi)
347{
348 struct boe_th101mb31ig002 *ctx;
349 const struct panel_desc *desc;
350 int ret;
351
352 ctx = devm_drm_panel_alloc(&dsi->dev, struct boe_th101mb31ig002, panel,
353 &boe_th101mb31ig002_funcs,
354 DRM_MODE_CONNECTOR_DSI);
355 if (IS_ERR(ptr: ctx))
356 return PTR_ERR(ptr: ctx);
357
358 mipi_dsi_set_drvdata(dsi, data: ctx);
359 ctx->dsi = dsi;
360
361 desc = of_device_get_match_data(dev: &dsi->dev);
362 dsi->lanes = desc->lanes;
363 dsi->format = desc->format;
364 dsi->mode_flags = desc->mode_flags;
365 ctx->desc = desc;
366
367 ctx->power = devm_regulator_get(dev: &dsi->dev, id: "power");
368 if (IS_ERR(ptr: ctx->power))
369 return dev_err_probe(dev: &dsi->dev, err: PTR_ERR(ptr: ctx->power),
370 fmt: "Failed to get power regulator\n");
371
372 ctx->enable = devm_gpiod_get(dev: &dsi->dev, con_id: "enable", flags: GPIOD_OUT_LOW);
373 if (IS_ERR(ptr: ctx->enable))
374 return dev_err_probe(dev: &dsi->dev, err: PTR_ERR(ptr: ctx->enable),
375 fmt: "Failed to get enable GPIO\n");
376
377 ctx->reset = devm_gpiod_get_optional(dev: &dsi->dev, con_id: "reset", flags: GPIOD_OUT_HIGH);
378 if (IS_ERR(ptr: ctx->reset))
379 return dev_err_probe(dev: &dsi->dev, err: PTR_ERR(ptr: ctx->reset),
380 fmt: "Failed to get reset GPIO\n");
381
382 ret = of_drm_get_panel_orientation(np: dsi->dev.of_node,
383 orientation: &ctx->orientation);
384 if (ret)
385 return dev_err_probe(dev: &dsi->dev, err: ret,
386 fmt: "Failed to get orientation\n");
387
388 ret = drm_panel_of_backlight(panel: &ctx->panel);
389 if (ret)
390 return ret;
391
392 drm_panel_add(panel: &ctx->panel);
393
394 ret = mipi_dsi_attach(dsi);
395 if (ret < 0) {
396 dev_err_probe(dev: &dsi->dev, err: ret,
397 fmt: "Failed to attach panel to DSI host\n");
398 drm_panel_remove(panel: &ctx->panel);
399 return ret;
400 }
401
402 return 0;
403}
404
405static void boe_th101mb31ig002_dsi_remove(struct mipi_dsi_device *dsi)
406{
407 struct boe_th101mb31ig002 *ctx = mipi_dsi_get_drvdata(dsi);
408
409 mipi_dsi_detach(dsi);
410 drm_panel_remove(panel: &ctx->panel);
411}
412
413static const struct of_device_id boe_th101mb31ig002_of_match[] = {
414 {
415 .compatible = "boe,th101mb31ig002-28a",
416 .data = &boe_th101mb31ig002_desc
417 },
418 {
419 .compatible = "starry,er88577",
420 .data = &starry_er88577_desc
421 },
422 { /* sentinel */ }
423};
424MODULE_DEVICE_TABLE(of, boe_th101mb31ig002_of_match);
425
426static struct mipi_dsi_driver boe_th101mb31ig002_driver = {
427 .driver = {
428 .name = "boe-th101mb31ig002-28a",
429 .of_match_table = boe_th101mb31ig002_of_match,
430 },
431 .probe = boe_th101mb31ig002_dsi_probe,
432 .remove = boe_th101mb31ig002_dsi_remove,
433};
434module_mipi_dsi_driver(boe_th101mb31ig002_driver);
435
436MODULE_AUTHOR("Alexander Warnecke <awarnecke002@hotmail.com>");
437MODULE_DESCRIPTION("BOE TH101MB31IG002-28A MIPI-DSI LCD panel");
438MODULE_LICENSE("GPL");
439

source code of linux/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c