1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd 4 */ 5 6 #include <linux/delay.h> 7 #include <linux/gpio/consumer.h> 8 #include <linux/module.h> 9 #include <linux/of.h> 10 #include <linux/regulator/consumer.h> 11 12 #include <video/mipi_display.h> 13 14 #include <drm/drm_crtc.h> 15 #include <drm/drm_device.h> 16 #include <drm/drm_mipi_dsi.h> 17 #include <drm/drm_modes.h> 18 #include <drm/drm_panel.h> 19 20 struct innolux_panel; 21 22 struct panel_desc { 23 const struct drm_display_mode *mode; 24 unsigned int bpc; 25 struct { 26 unsigned int width; 27 unsigned int height; 28 } size; 29 30 unsigned long flags; 31 enum mipi_dsi_pixel_format format; 32 int (*init)(struct innolux_panel *innolux); 33 unsigned int lanes; 34 const char * const *supply_names; 35 unsigned int num_supplies; 36 unsigned int sleep_mode_delay; 37 unsigned int power_down_delay; 38 }; 39 40 struct innolux_panel { 41 struct drm_panel base; 42 struct mipi_dsi_device *link; 43 const struct panel_desc *desc; 44 45 struct regulator_bulk_data *supplies; 46 struct gpio_desc *enable_gpio; 47 }; 48 49 static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel) 50 { 51 return container_of(panel, struct innolux_panel, base); 52 } 53 54 static int innolux_panel_unprepare(struct drm_panel *panel) 55 { 56 struct innolux_panel *innolux = to_innolux_panel(panel); 57 int err; 58 59 err = mipi_dsi_dcs_set_display_off(innolux->link); 60 if (err < 0) 61 dev_err(panel->dev, "failed to set display off: %d\n", err); 62 63 err = mipi_dsi_dcs_enter_sleep_mode(innolux->link); 64 if (err < 0) { 65 dev_err(panel->dev, "failed to enter sleep mode: %d\n", err); 66 return err; 67 } 68 69 if (innolux->desc->sleep_mode_delay) 70 msleep(innolux->desc->sleep_mode_delay); 71 72 gpiod_set_value_cansleep(innolux->enable_gpio, 0); 73 74 if (innolux->desc->power_down_delay) 75 msleep(innolux->desc->power_down_delay); 76 77 err = regulator_bulk_disable(innolux->desc->num_supplies, 78 innolux->supplies); 79 if (err < 0) 80 return err; 81 82 return 0; 83 } 84 85 static int innolux_panel_prepare(struct drm_panel *panel) 86 { 87 struct innolux_panel *innolux = to_innolux_panel(panel); 88 int err; 89 90 gpiod_set_value_cansleep(innolux->enable_gpio, 0); 91 92 err = regulator_bulk_enable(innolux->desc->num_supplies, 93 innolux->supplies); 94 if (err < 0) 95 return err; 96 97 /* p079zca: t2 (20ms), p097pfg: t4 (15ms) */ 98 usleep_range(20000, 21000); 99 100 gpiod_set_value_cansleep(innolux->enable_gpio, 1); 101 102 /* p079zca: t4, p097pfg: t5 */ 103 usleep_range(20000, 21000); 104 105 if (innolux->desc->init) { 106 err = innolux->desc->init(innolux); 107 if (err < 0) 108 goto poweroff; 109 } 110 111 err = mipi_dsi_dcs_exit_sleep_mode(innolux->link); 112 if (err < 0) { 113 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); 114 goto poweroff; 115 } 116 117 /* T6: 120ms - 1000ms*/ 118 msleep(120); 119 120 err = mipi_dsi_dcs_set_display_on(innolux->link); 121 if (err < 0) { 122 dev_err(panel->dev, "failed to set display on: %d\n", err); 123 goto poweroff; 124 } 125 126 /* T7: 5ms */ 127 usleep_range(5000, 6000); 128 129 return 0; 130 131 poweroff: 132 gpiod_set_value_cansleep(innolux->enable_gpio, 0); 133 regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies); 134 135 return err; 136 } 137 138 static const char * const innolux_p079zca_supply_names[] = { 139 "power", 140 }; 141 142 static const struct drm_display_mode innolux_p079zca_mode = { 143 .clock = 56900, 144 .hdisplay = 768, 145 .hsync_start = 768 + 40, 146 .hsync_end = 768 + 40 + 40, 147 .htotal = 768 + 40 + 40 + 40, 148 .vdisplay = 1024, 149 .vsync_start = 1024 + 20, 150 .vsync_end = 1024 + 20 + 4, 151 .vtotal = 1024 + 20 + 4 + 20, 152 }; 153 154 static const struct panel_desc innolux_p079zca_panel_desc = { 155 .mode = &innolux_p079zca_mode, 156 .bpc = 8, 157 .size = { 158 .width = 120, 159 .height = 160, 160 }, 161 .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 162 MIPI_DSI_MODE_LPM, 163 .format = MIPI_DSI_FMT_RGB888, 164 .lanes = 4, 165 .supply_names = innolux_p079zca_supply_names, 166 .num_supplies = ARRAY_SIZE(innolux_p079zca_supply_names), 167 .power_down_delay = 80, /* T8: 80ms - 1000ms */ 168 }; 169 170 static const char * const innolux_p097pfg_supply_names[] = { 171 "avdd", 172 "avee", 173 }; 174 175 static const struct drm_display_mode innolux_p097pfg_mode = { 176 .clock = 229000, 177 .hdisplay = 1536, 178 .hsync_start = 1536 + 100, 179 .hsync_end = 1536 + 100 + 24, 180 .htotal = 1536 + 100 + 24 + 100, 181 .vdisplay = 2048, 182 .vsync_start = 2048 + 100, 183 .vsync_end = 2048 + 100 + 2, 184 .vtotal = 2048 + 100 + 2 + 18, 185 }; 186 187 static void innolux_panel_write_multi(struct mipi_dsi_multi_context *ctx, 188 const void *payload, size_t size) 189 { 190 mipi_dsi_generic_write_multi(ctx, payload, size); 191 192 /* 193 * Included by random guessing, because without this 194 * (or at least, some delay), the panel sometimes 195 * didn't appear to pick up the command sequence. 196 */ 197 mipi_dsi_dcs_nop_multi(ctx); 198 } 199 200 #define innolux_panel_init_cmd_multi(ctx, seq...) \ 201 do { \ 202 static const u8 d[] = { seq }; \ 203 innolux_panel_write_multi(ctx, d, ARRAY_SIZE(d)); \ 204 } while (0) 205 206 #define innolux_panel_switch_page(ctx, page) \ 207 innolux_panel_init_cmd_multi(ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, (page)) 208 209 /* 210 * Display manufacturer failed to provide init sequencing according to 211 * https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/892065/ 212 * so the init sequence stems from a register dump of a working panel. 213 */ 214 static int innolux_p097pfg_init(struct innolux_panel *innolux) 215 { 216 struct mipi_dsi_multi_context ctx = { .dsi = innolux->link }; 217 218 innolux_panel_switch_page(&ctx, 0x00); 219 innolux_panel_init_cmd_multi(&ctx, 0xb1, 0xe8, 0x11); 220 innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x25, 0x02); 221 innolux_panel_init_cmd_multi(&ctx, 0xb5, 0x08, 0x00); 222 innolux_panel_init_cmd_multi(&ctx, 0xbc, 0x0f, 0x00); 223 innolux_panel_init_cmd_multi(&ctx, 0xb8, 0x03, 0x06, 0x00, 0x00); 224 innolux_panel_init_cmd_multi(&ctx, 0xbd, 0x01, 0x90, 0x14, 0x14); 225 innolux_panel_init_cmd_multi(&ctx, 0x6f, 0x01); 226 innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x03); 227 innolux_panel_init_cmd_multi(&ctx, 0x6f, 0x02); 228 innolux_panel_init_cmd_multi(&ctx, 0xc1, 0x0d); 229 innolux_panel_init_cmd_multi(&ctx, 0xd9, 0x01, 0x09, 0x70); 230 innolux_panel_init_cmd_multi(&ctx, 0xc5, 0x12, 0x21, 0x00); 231 innolux_panel_init_cmd_multi(&ctx, 0xbb, 0x93, 0x93); 232 233 innolux_panel_switch_page(&ctx, 0x01); 234 innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x3c, 0x3c); 235 innolux_panel_init_cmd_multi(&ctx, 0xb4, 0x0f, 0x0f); 236 innolux_panel_init_cmd_multi(&ctx, 0xb9, 0x45, 0x45); 237 innolux_panel_init_cmd_multi(&ctx, 0xba, 0x14, 0x14); 238 innolux_panel_init_cmd_multi(&ctx, 0xca, 0x02); 239 innolux_panel_init_cmd_multi(&ctx, 0xce, 0x04); 240 innolux_panel_init_cmd_multi(&ctx, 0xc3, 0x9b, 0x9b); 241 innolux_panel_init_cmd_multi(&ctx, 0xd8, 0xc0, 0x03); 242 innolux_panel_init_cmd_multi(&ctx, 0xbc, 0x82, 0x01); 243 innolux_panel_init_cmd_multi(&ctx, 0xbd, 0x9e, 0x01); 244 245 innolux_panel_switch_page(&ctx, 0x02); 246 innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x82); 247 innolux_panel_init_cmd_multi(&ctx, 0xd1, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x82, 0x00, 0xa5, 248 0x00, 0xc1, 0x00, 0xea, 0x01, 0x0d, 0x01, 0x40); 249 innolux_panel_init_cmd_multi(&ctx, 0xd2, 0x01, 0x6a, 0x01, 0xa8, 0x01, 0xdc, 0x02, 0x29, 250 0x02, 0x67, 0x02, 0x68, 0x02, 0xa8, 0x02, 0xf0); 251 innolux_panel_init_cmd_multi(&ctx, 0xd3, 0x03, 0x19, 0x03, 0x49, 0x03, 0x67, 0x03, 0x8c, 252 0x03, 0xa6, 0x03, 0xc7, 0x03, 0xde, 0x03, 0xec); 253 innolux_panel_init_cmd_multi(&ctx, 0xd4, 0x03, 0xff, 0x03, 0xff); 254 innolux_panel_init_cmd_multi(&ctx, 0xe0, 0x00, 0x00, 0x00, 0x86, 0x00, 0xc5, 0x00, 0xe5, 255 0x00, 0xff, 0x01, 0x26, 0x01, 0x45, 0x01, 0x75); 256 innolux_panel_init_cmd_multi(&ctx, 0xe1, 0x01, 0x9c, 0x01, 0xd5, 0x02, 0x05, 0x02, 0x4d, 257 0x02, 0x86, 0x02, 0x87, 0x02, 0xc3, 0x03, 0x03); 258 innolux_panel_init_cmd_multi(&ctx, 0xe2, 0x03, 0x2a, 0x03, 0x56, 0x03, 0x72, 0x03, 0x94, 259 0x03, 0xac, 0x03, 0xcb, 0x03, 0xe0, 0x03, 0xed); 260 innolux_panel_init_cmd_multi(&ctx, 0xe3, 0x03, 0xff, 0x03, 0xff); 261 262 innolux_panel_switch_page(&ctx, 0x03); 263 innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x00, 0x00, 0x00, 0x00); 264 innolux_panel_init_cmd_multi(&ctx, 0xb1, 0x00, 0x00, 0x00, 0x00); 265 innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x00, 0x00, 0x06, 0x04, 0x01, 0x40, 0x85); 266 innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x10, 0x07, 0xfc, 0x04, 0x01, 0x40, 0x80); 267 innolux_panel_init_cmd_multi(&ctx, 0xb6, 0xf0, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 268 0x40, 0x80); 269 innolux_panel_init_cmd_multi(&ctx, 0xba, 0xc5, 0x07, 0x00, 0x04, 0x11, 0x25, 0x8c); 270 innolux_panel_init_cmd_multi(&ctx, 0xbb, 0xc5, 0x07, 0x00, 0x03, 0x11, 0x25, 0x8c); 271 innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x80, 0x80); 272 innolux_panel_init_cmd_multi(&ctx, 0xc1, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x80, 0x80); 273 innolux_panel_init_cmd_multi(&ctx, 0xc4, 0x00, 0x00); 274 innolux_panel_init_cmd_multi(&ctx, 0xef, 0x41); 275 276 innolux_panel_switch_page(&ctx, 0x04); 277 innolux_panel_init_cmd_multi(&ctx, 0xec, 0x4c); 278 279 innolux_panel_switch_page(&ctx, 0x05); 280 innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x13, 0x03, 0x03, 0x01); 281 innolux_panel_init_cmd_multi(&ctx, 0xb1, 0x30, 0x00); 282 innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x02, 0x02, 0x00); 283 innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x82, 0x23, 0x82, 0x9d); 284 innolux_panel_init_cmd_multi(&ctx, 0xb4, 0xc5, 0x75, 0x24, 0x57); 285 innolux_panel_init_cmd_multi(&ctx, 0xb5, 0x00, 0xd4, 0x72, 0x11, 0x11, 0xab, 0x0a); 286 innolux_panel_init_cmd_multi(&ctx, 0xb6, 0x00, 0x00, 0xd5, 0x72, 0x24, 0x56); 287 innolux_panel_init_cmd_multi(&ctx, 0xb7, 0x5c, 0xdc, 0x5c, 0x5c); 288 innolux_panel_init_cmd_multi(&ctx, 0xb9, 0x0c, 0x00, 0x00, 0x01, 0x00); 289 innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x75, 0x11, 0x11, 0x54, 0x05); 290 innolux_panel_init_cmd_multi(&ctx, 0xc6, 0x00, 0x00, 0x00, 0x00); 291 innolux_panel_init_cmd_multi(&ctx, 0xd0, 0x00, 0x48, 0x08, 0x00, 0x00); 292 innolux_panel_init_cmd_multi(&ctx, 0xd1, 0x00, 0x48, 0x09, 0x00, 0x00); 293 294 innolux_panel_switch_page(&ctx, 0x06); 295 innolux_panel_init_cmd_multi(&ctx, 0xb0, 0x02, 0x32, 0x32, 0x08, 0x2f); 296 innolux_panel_init_cmd_multi(&ctx, 0xb1, 0x2e, 0x15, 0x14, 0x13, 0x12); 297 innolux_panel_init_cmd_multi(&ctx, 0xb2, 0x11, 0x10, 0x00, 0x3d, 0x3d); 298 innolux_panel_init_cmd_multi(&ctx, 0xb3, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d); 299 innolux_panel_init_cmd_multi(&ctx, 0xb4, 0x3d, 0x32); 300 innolux_panel_init_cmd_multi(&ctx, 0xb5, 0x03, 0x32, 0x32, 0x09, 0x2f); 301 innolux_panel_init_cmd_multi(&ctx, 0xb6, 0x2e, 0x1b, 0x1a, 0x19, 0x18); 302 innolux_panel_init_cmd_multi(&ctx, 0xb7, 0x17, 0x16, 0x01, 0x3d, 0x3d); 303 innolux_panel_init_cmd_multi(&ctx, 0xb8, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d); 304 innolux_panel_init_cmd_multi(&ctx, 0xb9, 0x3d, 0x32); 305 innolux_panel_init_cmd_multi(&ctx, 0xc0, 0x01, 0x32, 0x32, 0x09, 0x2f); 306 innolux_panel_init_cmd_multi(&ctx, 0xc1, 0x2e, 0x1a, 0x1b, 0x16, 0x17); 307 innolux_panel_init_cmd_multi(&ctx, 0xc2, 0x18, 0x19, 0x03, 0x3d, 0x3d); 308 innolux_panel_init_cmd_multi(&ctx, 0xc3, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d); 309 innolux_panel_init_cmd_multi(&ctx, 0xc4, 0x3d, 0x32); 310 innolux_panel_init_cmd_multi(&ctx, 0xc5, 0x00, 0x32, 0x32, 0x08, 0x2f); 311 innolux_panel_init_cmd_multi(&ctx, 0xc6, 0x2e, 0x14, 0x15, 0x10, 0x11); 312 innolux_panel_init_cmd_multi(&ctx, 0xc7, 0x12, 0x13, 0x02, 0x3d, 0x3d); 313 innolux_panel_init_cmd_multi(&ctx, 0xc8, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d); 314 innolux_panel_init_cmd_multi(&ctx, 0xc9, 0x3d, 0x32); 315 316 return ctx.accum_err; 317 } 318 319 static const struct panel_desc innolux_p097pfg_panel_desc = { 320 .mode = &innolux_p097pfg_mode, 321 .bpc = 8, 322 .size = { 323 .width = 147, 324 .height = 196, 325 }, 326 .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 327 MIPI_DSI_MODE_LPM, 328 .format = MIPI_DSI_FMT_RGB888, 329 .init = innolux_p097pfg_init, 330 .lanes = 4, 331 .supply_names = innolux_p097pfg_supply_names, 332 .num_supplies = ARRAY_SIZE(innolux_p097pfg_supply_names), 333 .sleep_mode_delay = 100, /* T15 */ 334 }; 335 336 static int innolux_panel_get_modes(struct drm_panel *panel, 337 struct drm_connector *connector) 338 { 339 struct innolux_panel *innolux = to_innolux_panel(panel); 340 const struct drm_display_mode *m = innolux->desc->mode; 341 struct drm_display_mode *mode; 342 343 mode = drm_mode_duplicate(connector->dev, m); 344 if (!mode) { 345 dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 346 m->hdisplay, m->vdisplay, drm_mode_vrefresh(m)); 347 return -ENOMEM; 348 } 349 350 drm_mode_set_name(mode); 351 352 drm_mode_probed_add(connector, mode); 353 354 connector->display_info.width_mm = innolux->desc->size.width; 355 connector->display_info.height_mm = innolux->desc->size.height; 356 connector->display_info.bpc = innolux->desc->bpc; 357 358 return 1; 359 } 360 361 static const struct drm_panel_funcs innolux_panel_funcs = { 362 .unprepare = innolux_panel_unprepare, 363 .prepare = innolux_panel_prepare, 364 .get_modes = innolux_panel_get_modes, 365 }; 366 367 static const struct of_device_id innolux_of_match[] = { 368 { .compatible = "innolux,p079zca", 369 .data = &innolux_p079zca_panel_desc 370 }, 371 { .compatible = "innolux,p097pfg", 372 .data = &innolux_p097pfg_panel_desc 373 }, 374 { } 375 }; 376 MODULE_DEVICE_TABLE(of, innolux_of_match); 377 378 static int innolux_panel_add(struct mipi_dsi_device *dsi, 379 const struct panel_desc *desc) 380 { 381 struct innolux_panel *innolux; 382 struct device *dev = &dsi->dev; 383 int err, i; 384 385 innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL); 386 if (!innolux) 387 return -ENOMEM; 388 389 innolux->desc = desc; 390 391 innolux->supplies = devm_kcalloc(dev, desc->num_supplies, 392 sizeof(*innolux->supplies), 393 GFP_KERNEL); 394 if (!innolux->supplies) 395 return -ENOMEM; 396 397 for (i = 0; i < desc->num_supplies; i++) 398 innolux->supplies[i].supply = desc->supply_names[i]; 399 400 err = devm_regulator_bulk_get(dev, desc->num_supplies, 401 innolux->supplies); 402 if (err < 0) 403 return err; 404 405 innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable", 406 GPIOD_OUT_HIGH); 407 if (IS_ERR(innolux->enable_gpio)) { 408 err = PTR_ERR(innolux->enable_gpio); 409 dev_dbg(dev, "failed to get enable gpio: %d\n", err); 410 innolux->enable_gpio = NULL; 411 } 412 413 drm_panel_init(&innolux->base, dev, &innolux_panel_funcs, 414 DRM_MODE_CONNECTOR_DSI); 415 416 err = drm_panel_of_backlight(&innolux->base); 417 if (err) 418 return err; 419 420 drm_panel_add(&innolux->base); 421 422 mipi_dsi_set_drvdata(dsi, innolux); 423 innolux->link = dsi; 424 425 return 0; 426 } 427 428 static void innolux_panel_del(struct innolux_panel *innolux) 429 { 430 drm_panel_remove(&innolux->base); 431 } 432 433 static int innolux_panel_probe(struct mipi_dsi_device *dsi) 434 { 435 const struct panel_desc *desc; 436 struct innolux_panel *innolux; 437 int err; 438 439 desc = of_device_get_match_data(&dsi->dev); 440 dsi->mode_flags = desc->flags; 441 dsi->format = desc->format; 442 dsi->lanes = desc->lanes; 443 444 err = innolux_panel_add(dsi, desc); 445 if (err < 0) 446 return err; 447 448 err = mipi_dsi_attach(dsi); 449 if (err < 0) { 450 innolux = mipi_dsi_get_drvdata(dsi); 451 innolux_panel_del(innolux); 452 return err; 453 } 454 455 return 0; 456 } 457 458 static void innolux_panel_remove(struct mipi_dsi_device *dsi) 459 { 460 struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi); 461 int err; 462 463 464 err = mipi_dsi_detach(dsi); 465 if (err < 0) 466 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); 467 468 innolux_panel_del(innolux); 469 } 470 471 static struct mipi_dsi_driver innolux_panel_driver = { 472 .driver = { 473 .name = "panel-innolux-p079zca", 474 .of_match_table = innolux_of_match, 475 }, 476 .probe = innolux_panel_probe, 477 .remove = innolux_panel_remove, 478 }; 479 module_mipi_dsi_driver(innolux_panel_driver); 480 481 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); 482 MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>"); 483 MODULE_DESCRIPTION("Innolux P079ZCA panel driver"); 484 MODULE_LICENSE("GPL v2"); 485