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 21 struct boe_th101mb31ig002; 22 23 struct 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 37 struct 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 51 static void boe_th101mb31ig002_reset(struct boe_th101mb31ig002 *ctx) 52 { 53 gpiod_direction_output(ctx->reset, 0); 54 usleep_range(10, 100); 55 gpiod_direction_output(ctx->reset, 1); 56 usleep_range(10, 100); 57 gpiod_direction_output(ctx->reset, 0); 58 usleep_range(5000, 6000); 59 } 60 61 static 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(&dsi_ctx); 110 111 mipi_dsi_msleep(&dsi_ctx, 120); 112 113 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 114 115 return dsi_ctx.accum_err; 116 } 117 118 static int starry_er88577_init_cmd(struct boe_th101mb31ig002 *ctx) 119 { 120 struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 121 122 msleep(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(&dsi_ctx); 170 171 mipi_dsi_msleep(&dsi_ctx, 120); 172 173 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 174 175 mipi_dsi_msleep(&dsi_ctx, 20); 176 177 return dsi_ctx.accum_err; 178 } 179 180 static 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(&dsi_ctx); 191 192 mipi_dsi_msleep(&dsi_ctx, 120); 193 194 mipi_dsi_dcs_enter_sleep_mode_multi(&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 202 static 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(ctx->reset, 1); 209 gpiod_set_value_cansleep(ctx->enable, 0); 210 regulator_disable(ctx->power); 211 212 if (ctx->desc->power_off_delay_ms) 213 msleep(ctx->desc->power_off_delay_ms); 214 215 return 0; 216 } 217 218 static 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 struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 225 int ret; 226 227 ret = regulator_enable(ctx->power); 228 if (ret) { 229 dev_err(dev, "Failed to enable power supply: %d\n", ret); 230 return ret; 231 } 232 233 if (ctx->desc->vcioo_to_lp11_delay_ms) 234 msleep(ctx->desc->vcioo_to_lp11_delay_ms); 235 236 if (ctx->desc->lp11_before_reset) { 237 mipi_dsi_dcs_nop_multi(&dsi_ctx); 238 if (dsi_ctx.accum_err) 239 return dsi_ctx.accum_err; 240 } 241 242 if (ctx->desc->lp11_to_reset_delay_ms) 243 msleep(ctx->desc->lp11_to_reset_delay_ms); 244 245 gpiod_set_value_cansleep(ctx->enable, 1); 246 msleep(50); 247 boe_th101mb31ig002_reset(ctx); 248 249 ret = ctx->desc->init(ctx); 250 if (ret) 251 return ret; 252 253 return 0; 254 } 255 256 static const struct drm_display_mode boe_th101mb31ig002_default_mode = { 257 .clock = 73500, 258 .hdisplay = 800, 259 .hsync_start = 800 + 64, 260 .hsync_end = 800 + 64 + 16, 261 .htotal = 800 + 64 + 16 + 64, 262 .vdisplay = 1280, 263 .vsync_start = 1280 + 2, 264 .vsync_end = 1280 + 2 + 4, 265 .vtotal = 1280 + 2 + 4 + 12, 266 .width_mm = 135, 267 .height_mm = 216, 268 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 269 }; 270 271 static const struct panel_desc boe_th101mb31ig002_desc = { 272 .modes = &boe_th101mb31ig002_default_mode, 273 .lanes = 4, 274 .format = MIPI_DSI_FMT_RGB888, 275 .mode_flags = MIPI_DSI_MODE_VIDEO_BURST | 276 MIPI_DSI_MODE_NO_EOT_PACKET | 277 MIPI_DSI_MODE_LPM, 278 .init = boe_th101mb31ig002_enable, 279 }; 280 281 static const struct drm_display_mode starry_er88577_default_mode = { 282 .clock = (800 + 25 + 25 + 25) * (1280 + 20 + 4 + 12) * 60 / 1000, 283 .hdisplay = 800, 284 .hsync_start = 800 + 25, 285 .hsync_end = 800 + 25 + 25, 286 .htotal = 800 + 25 + 25 + 25, 287 .vdisplay = 1280, 288 .vsync_start = 1280 + 20, 289 .vsync_end = 1280 + 20 + 4, 290 .vtotal = 1280 + 20 + 4 + 12, 291 .width_mm = 135, 292 .height_mm = 216, 293 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 294 }; 295 296 static const struct panel_desc starry_er88577_desc = { 297 .modes = &starry_er88577_default_mode, 298 .lanes = 4, 299 .format = MIPI_DSI_FMT_RGB888, 300 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 301 MIPI_DSI_MODE_LPM, 302 .init = starry_er88577_init_cmd, 303 .lp11_before_reset = true, 304 .vcioo_to_lp11_delay_ms = 5, 305 .lp11_to_reset_delay_ms = 50, 306 .backlight_off_to_display_off_delay_ms = 100, 307 .enter_sleep_to_reset_down_delay_ms = 100, 308 .power_off_delay_ms = 1000, 309 }; 310 311 static int boe_th101mb31ig002_get_modes(struct drm_panel *panel, 312 struct drm_connector *connector) 313 { 314 struct boe_th101mb31ig002 *ctx = container_of(panel, 315 struct boe_th101mb31ig002, 316 panel); 317 const struct drm_display_mode *desc_mode = ctx->desc->modes; 318 319 connector->display_info.bpc = 8; 320 /* 321 * TODO: Remove once all drm drivers call 322 * drm_connector_set_orientation_from_panel() 323 */ 324 drm_connector_set_panel_orientation(connector, ctx->orientation); 325 326 return drm_connector_helper_get_modes_fixed(connector, desc_mode); 327 } 328 329 static enum drm_panel_orientation 330 boe_th101mb31ig002_get_orientation(struct drm_panel *panel) 331 { 332 struct boe_th101mb31ig002 *ctx = container_of(panel, 333 struct boe_th101mb31ig002, 334 panel); 335 336 return ctx->orientation; 337 } 338 339 static const struct drm_panel_funcs boe_th101mb31ig002_funcs = { 340 .prepare = boe_th101mb31ig002_prepare, 341 .unprepare = boe_th101mb31ig002_unprepare, 342 .disable = boe_th101mb31ig002_disable, 343 .get_modes = boe_th101mb31ig002_get_modes, 344 .get_orientation = boe_th101mb31ig002_get_orientation, 345 }; 346 347 static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi) 348 { 349 struct boe_th101mb31ig002 *ctx; 350 const struct panel_desc *desc; 351 int ret; 352 353 ctx = devm_drm_panel_alloc(&dsi->dev, struct boe_th101mb31ig002, panel, 354 &boe_th101mb31ig002_funcs, 355 DRM_MODE_CONNECTOR_DSI); 356 if (IS_ERR(ctx)) 357 return PTR_ERR(ctx); 358 359 mipi_dsi_set_drvdata(dsi, ctx); 360 ctx->dsi = dsi; 361 362 desc = of_device_get_match_data(&dsi->dev); 363 dsi->lanes = desc->lanes; 364 dsi->format = desc->format; 365 dsi->mode_flags = desc->mode_flags; 366 ctx->desc = desc; 367 368 ctx->power = devm_regulator_get(&dsi->dev, "power"); 369 if (IS_ERR(ctx->power)) 370 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power), 371 "Failed to get power regulator\n"); 372 373 ctx->enable = devm_gpiod_get(&dsi->dev, "enable", GPIOD_OUT_LOW); 374 if (IS_ERR(ctx->enable)) 375 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->enable), 376 "Failed to get enable GPIO\n"); 377 378 ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_HIGH); 379 if (IS_ERR(ctx->reset)) 380 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), 381 "Failed to get reset GPIO\n"); 382 383 ret = of_drm_get_panel_orientation(dsi->dev.of_node, 384 &ctx->orientation); 385 if (ret) 386 return dev_err_probe(&dsi->dev, ret, 387 "Failed to get orientation\n"); 388 389 ret = drm_panel_of_backlight(&ctx->panel); 390 if (ret) 391 return ret; 392 393 drm_panel_add(&ctx->panel); 394 395 ret = mipi_dsi_attach(dsi); 396 if (ret < 0) { 397 dev_err_probe(&dsi->dev, ret, 398 "Failed to attach panel to DSI host\n"); 399 drm_panel_remove(&ctx->panel); 400 return ret; 401 } 402 403 return 0; 404 } 405 406 static void boe_th101mb31ig002_dsi_remove(struct mipi_dsi_device *dsi) 407 { 408 struct boe_th101mb31ig002 *ctx = mipi_dsi_get_drvdata(dsi); 409 410 mipi_dsi_detach(dsi); 411 drm_panel_remove(&ctx->panel); 412 } 413 414 static const struct of_device_id boe_th101mb31ig002_of_match[] = { 415 { 416 .compatible = "boe,th101mb31ig002-28a", 417 .data = &boe_th101mb31ig002_desc 418 }, 419 { 420 .compatible = "starry,er88577", 421 .data = &starry_er88577_desc 422 }, 423 { /* sentinel */ } 424 }; 425 MODULE_DEVICE_TABLE(of, boe_th101mb31ig002_of_match); 426 427 static struct mipi_dsi_driver boe_th101mb31ig002_driver = { 428 .driver = { 429 .name = "boe-th101mb31ig002-28a", 430 .of_match_table = boe_th101mb31ig002_of_match, 431 }, 432 .probe = boe_th101mb31ig002_dsi_probe, 433 .remove = boe_th101mb31ig002_dsi_remove, 434 }; 435 module_mipi_dsi_driver(boe_th101mb31ig002_driver); 436 437 MODULE_AUTHOR("Alexander Warnecke <awarnecke002@hotmail.com>"); 438 MODULE_DESCRIPTION("BOE TH101MB31IG002-28A MIPI-DSI LCD panel"); 439 MODULE_LICENSE("GPL"); 440