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 int ret; 225 226 ret = regulator_enable(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(ctx->desc->vcioo_to_lp11_delay_ms); 234 235 if (ctx->desc->lp11_before_reset) { 236 ret = mipi_dsi_dcs_nop(ctx->dsi); 237 if (ret) 238 return ret; 239 } 240 241 if (ctx->desc->lp11_to_reset_delay_ms) 242 msleep(ctx->desc->lp11_to_reset_delay_ms); 243 244 gpiod_set_value_cansleep(ctx->enable, 1); 245 msleep(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 255 static 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 270 static 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 280 static 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 295 static 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 310 static 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, ctx->orientation); 324 325 return drm_connector_helper_get_modes_fixed(connector, desc_mode); 326 } 327 328 static enum drm_panel_orientation 329 boe_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 338 static 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 346 static 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_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); 353 if (!ctx) 354 return -ENOMEM; 355 356 mipi_dsi_set_drvdata(dsi, ctx); 357 ctx->dsi = dsi; 358 359 desc = of_device_get_match_data(&dsi->dev); 360 dsi->lanes = desc->lanes; 361 dsi->format = desc->format; 362 dsi->mode_flags = desc->mode_flags; 363 ctx->desc = desc; 364 365 ctx->power = devm_regulator_get(&dsi->dev, "power"); 366 if (IS_ERR(ctx->power)) 367 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power), 368 "Failed to get power regulator\n"); 369 370 ctx->enable = devm_gpiod_get(&dsi->dev, "enable", GPIOD_OUT_LOW); 371 if (IS_ERR(ctx->enable)) 372 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->enable), 373 "Failed to get enable GPIO\n"); 374 375 ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_HIGH); 376 if (IS_ERR(ctx->reset)) 377 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), 378 "Failed to get reset GPIO\n"); 379 380 ret = of_drm_get_panel_orientation(dsi->dev.of_node, 381 &ctx->orientation); 382 if (ret) 383 return dev_err_probe(&dsi->dev, ret, 384 "Failed to get orientation\n"); 385 386 drm_panel_init(&ctx->panel, &dsi->dev, &boe_th101mb31ig002_funcs, 387 DRM_MODE_CONNECTOR_DSI); 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