panel-leadtek-ltk050h3146w.c (16032B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH 4 */ 5 6#include <linux/delay.h> 7#include <linux/gpio/consumer.h> 8#include <linux/media-bus-format.h> 9#include <linux/module.h> 10#include <linux/of.h> 11#include <linux/of_device.h> 12#include <linux/regulator/consumer.h> 13 14#include <video/display_timing.h> 15#include <video/mipi_display.h> 16 17#include <drm/drm_mipi_dsi.h> 18#include <drm/drm_modes.h> 19#include <drm/drm_panel.h> 20 21struct ltk050h3146w_cmd { 22 char cmd; 23 char data; 24}; 25 26struct ltk050h3146w; 27struct ltk050h3146w_desc { 28 const struct drm_display_mode *mode; 29 int (*init)(struct ltk050h3146w *ctx); 30}; 31 32struct ltk050h3146w { 33 struct device *dev; 34 struct drm_panel panel; 35 struct gpio_desc *reset_gpio; 36 struct regulator *vci; 37 struct regulator *iovcc; 38 const struct ltk050h3146w_desc *panel_desc; 39 bool prepared; 40}; 41 42static const struct ltk050h3146w_cmd page1_cmds[] = { 43 { 0x22, 0x0A }, /* BGR SS GS */ 44 { 0x31, 0x00 }, /* column inversion */ 45 { 0x53, 0xA2 }, /* VCOM1 */ 46 { 0x55, 0xA2 }, /* VCOM2 */ 47 { 0x50, 0x81 }, /* VREG1OUT=5V */ 48 { 0x51, 0x85 }, /* VREG2OUT=-5V */ 49 { 0x62, 0x0D }, /* EQT Time setting */ 50/* 51 * The vendor init selected page 1 here _again_ 52 * Is this supposed to be page 2? 53 */ 54 { 0xA0, 0x00 }, 55 { 0xA1, 0x1A }, 56 { 0xA2, 0x28 }, 57 { 0xA3, 0x13 }, 58 { 0xA4, 0x16 }, 59 { 0xA5, 0x29 }, 60 { 0xA6, 0x1D }, 61 { 0xA7, 0x1E }, 62 { 0xA8, 0x84 }, 63 { 0xA9, 0x1C }, 64 { 0xAA, 0x28 }, 65 { 0xAB, 0x75 }, 66 { 0xAC, 0x1A }, 67 { 0xAD, 0x19 }, 68 { 0xAE, 0x4D }, 69 { 0xAF, 0x22 }, 70 { 0xB0, 0x28 }, 71 { 0xB1, 0x54 }, 72 { 0xB2, 0x66 }, 73 { 0xB3, 0x39 }, 74 { 0xC0, 0x00 }, 75 { 0xC1, 0x1A }, 76 { 0xC2, 0x28 }, 77 { 0xC3, 0x13 }, 78 { 0xC4, 0x16 }, 79 { 0xC5, 0x29 }, 80 { 0xC6, 0x1D }, 81 { 0xC7, 0x1E }, 82 { 0xC8, 0x84 }, 83 { 0xC9, 0x1C }, 84 { 0xCA, 0x28 }, 85 { 0xCB, 0x75 }, 86 { 0xCC, 0x1A }, 87 { 0xCD, 0x19 }, 88 { 0xCE, 0x4D }, 89 { 0xCF, 0x22 }, 90 { 0xD0, 0x28 }, 91 { 0xD1, 0x54 }, 92 { 0xD2, 0x66 }, 93 { 0xD3, 0x39 }, 94}; 95 96static const struct ltk050h3146w_cmd page3_cmds[] = { 97 { 0x01, 0x00 }, 98 { 0x02, 0x00 }, 99 { 0x03, 0x73 }, 100 { 0x04, 0x00 }, 101 { 0x05, 0x00 }, 102 { 0x06, 0x0a }, 103 { 0x07, 0x00 }, 104 { 0x08, 0x00 }, 105 { 0x09, 0x01 }, 106 { 0x0a, 0x00 }, 107 { 0x0b, 0x00 }, 108 { 0x0c, 0x01 }, 109 { 0x0d, 0x00 }, 110 { 0x0e, 0x00 }, 111 { 0x0f, 0x1d }, 112 { 0x10, 0x1d }, 113 { 0x11, 0x00 }, 114 { 0x12, 0x00 }, 115 { 0x13, 0x00 }, 116 { 0x14, 0x00 }, 117 { 0x15, 0x00 }, 118 { 0x16, 0x00 }, 119 { 0x17, 0x00 }, 120 { 0x18, 0x00 }, 121 { 0x19, 0x00 }, 122 { 0x1a, 0x00 }, 123 { 0x1b, 0x00 }, 124 { 0x1c, 0x00 }, 125 { 0x1d, 0x00 }, 126 { 0x1e, 0x40 }, 127 { 0x1f, 0x80 }, 128 { 0x20, 0x06 }, 129 { 0x21, 0x02 }, 130 { 0x22, 0x00 }, 131 { 0x23, 0x00 }, 132 { 0x24, 0x00 }, 133 { 0x25, 0x00 }, 134 { 0x26, 0x00 }, 135 { 0x27, 0x00 }, 136 { 0x28, 0x33 }, 137 { 0x29, 0x03 }, 138 { 0x2a, 0x00 }, 139 { 0x2b, 0x00 }, 140 { 0x2c, 0x00 }, 141 { 0x2d, 0x00 }, 142 { 0x2e, 0x00 }, 143 { 0x2f, 0x00 }, 144 { 0x30, 0x00 }, 145 { 0x31, 0x00 }, 146 { 0x32, 0x00 }, 147 { 0x33, 0x00 }, 148 { 0x34, 0x04 }, 149 { 0x35, 0x00 }, 150 { 0x36, 0x00 }, 151 { 0x37, 0x00 }, 152 { 0x38, 0x3C }, 153 { 0x39, 0x35 }, 154 { 0x3A, 0x01 }, 155 { 0x3B, 0x40 }, 156 { 0x3C, 0x00 }, 157 { 0x3D, 0x01 }, 158 { 0x3E, 0x00 }, 159 { 0x3F, 0x00 }, 160 { 0x40, 0x00 }, 161 { 0x41, 0x88 }, 162 { 0x42, 0x00 }, 163 { 0x43, 0x00 }, 164 { 0x44, 0x1F }, 165 { 0x50, 0x01 }, 166 { 0x51, 0x23 }, 167 { 0x52, 0x45 }, 168 { 0x53, 0x67 }, 169 { 0x54, 0x89 }, 170 { 0x55, 0xab }, 171 { 0x56, 0x01 }, 172 { 0x57, 0x23 }, 173 { 0x58, 0x45 }, 174 { 0x59, 0x67 }, 175 { 0x5a, 0x89 }, 176 { 0x5b, 0xab }, 177 { 0x5c, 0xcd }, 178 { 0x5d, 0xef }, 179 { 0x5e, 0x11 }, 180 { 0x5f, 0x01 }, 181 { 0x60, 0x00 }, 182 { 0x61, 0x15 }, 183 { 0x62, 0x14 }, 184 { 0x63, 0x0E }, 185 { 0x64, 0x0F }, 186 { 0x65, 0x0C }, 187 { 0x66, 0x0D }, 188 { 0x67, 0x06 }, 189 { 0x68, 0x02 }, 190 { 0x69, 0x07 }, 191 { 0x6a, 0x02 }, 192 { 0x6b, 0x02 }, 193 { 0x6c, 0x02 }, 194 { 0x6d, 0x02 }, 195 { 0x6e, 0x02 }, 196 { 0x6f, 0x02 }, 197 { 0x70, 0x02 }, 198 { 0x71, 0x02 }, 199 { 0x72, 0x02 }, 200 { 0x73, 0x02 }, 201 { 0x74, 0x02 }, 202 { 0x75, 0x01 }, 203 { 0x76, 0x00 }, 204 { 0x77, 0x14 }, 205 { 0x78, 0x15 }, 206 { 0x79, 0x0E }, 207 { 0x7a, 0x0F }, 208 { 0x7b, 0x0C }, 209 { 0x7c, 0x0D }, 210 { 0x7d, 0x06 }, 211 { 0x7e, 0x02 }, 212 { 0x7f, 0x07 }, 213 { 0x80, 0x02 }, 214 { 0x81, 0x02 }, 215 { 0x82, 0x02 }, 216 { 0x83, 0x02 }, 217 { 0x84, 0x02 }, 218 { 0x85, 0x02 }, 219 { 0x86, 0x02 }, 220 { 0x87, 0x02 }, 221 { 0x88, 0x02 }, 222 { 0x89, 0x02 }, 223 { 0x8A, 0x02 }, 224}; 225 226static const struct ltk050h3146w_cmd page4_cmds[] = { 227 { 0x70, 0x00 }, 228 { 0x71, 0x00 }, 229 { 0x82, 0x0F }, /* VGH_MOD clamp level=15v */ 230 { 0x84, 0x0F }, /* VGH clamp level 15V */ 231 { 0x85, 0x0D }, /* VGL clamp level (-10V) */ 232 { 0x32, 0xAC }, 233 { 0x8C, 0x80 }, 234 { 0x3C, 0xF5 }, 235 { 0xB5, 0x07 }, /* GAMMA OP */ 236 { 0x31, 0x45 }, /* SOURCE OP */ 237 { 0x3A, 0x24 }, /* PS_EN OFF */ 238 { 0x88, 0x33 }, /* LVD */ 239}; 240 241static inline 242struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel) 243{ 244 return container_of(panel, struct ltk050h3146w, panel); 245} 246 247#define dsi_dcs_write_seq(dsi, cmd, seq...) do { \ 248 static const u8 b[] = { cmd, seq }; \ 249 int ret; \ 250 ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ 251 if (ret < 0) \ 252 return ret; \ 253 } while (0) 254 255static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx) 256{ 257 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 258 int ret; 259 260 /* 261 * Init sequence was supplied by the panel vendor without much 262 * documentation. 263 */ 264 dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8); 265 dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06, 266 0x01); 267 dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5); 268 dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5); 269 dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00); 270 271 dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07); 272 dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f, 273 0x28, 0x04, 0xcc, 0xcc, 0xcc); 274 dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04); 275 dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2); 276 dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03); 277 dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12); 278 dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80, 279 0x80); 280 dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f, 281 0x16, 0x00, 0x00); 282 dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50, 283 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f, 284 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67, 285 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55, 286 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08); 287 dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a, 288 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f, 289 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 290 dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b, 291 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f, 292 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 293 dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05, 294 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f, 295 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 296 dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04, 297 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f, 298 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 299 dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20, 300 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03, 301 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08); 302 dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00, 303 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05, 304 0x21, 0x00, 0x60); 305 dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00); 306 dsi_dcs_write_seq(dsi, 0xde, 0x02); 307 dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c); 308 dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04); 309 dsi_dcs_write_seq(dsi, 0xc1, 0x11); 310 dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37); 311 dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84); 312 dsi_dcs_write_seq(dsi, 0xde, 0x00); 313 314 ret = mipi_dsi_dcs_set_tear_on(dsi, 1); 315 if (ret < 0) { 316 dev_err(ctx->dev, "failed to set tear on: %d\n", ret); 317 return ret; 318 } 319 320 msleep(60); 321 322 return 0; 323} 324 325static const struct drm_display_mode ltk050h3146w_mode = { 326 .hdisplay = 720, 327 .hsync_start = 720 + 42, 328 .hsync_end = 720 + 42 + 8, 329 .htotal = 720 + 42 + 8 + 42, 330 .vdisplay = 1280, 331 .vsync_start = 1280 + 12, 332 .vsync_end = 1280 + 12 + 4, 333 .vtotal = 1280 + 12 + 4 + 18, 334 .clock = 64018, 335 .width_mm = 62, 336 .height_mm = 110, 337}; 338 339static const struct ltk050h3146w_desc ltk050h3146w_data = { 340 .mode = <k050h3146w_mode, 341 .init = ltk050h3146w_init_sequence, 342}; 343 344static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page) 345{ 346 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 347 u8 d[3] = { 0x98, 0x81, page }; 348 349 return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d)); 350} 351 352static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page, 353 const struct ltk050h3146w_cmd *cmds, 354 int num) 355{ 356 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 357 int i, ret; 358 359 ret = ltk050h3146w_a2_select_page(ctx, page); 360 if (ret < 0) { 361 dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret); 362 return ret; 363 } 364 365 for (i = 0; i < num; i++) { 366 ret = mipi_dsi_generic_write(dsi, &cmds[i], 367 sizeof(struct ltk050h3146w_cmd)); 368 if (ret < 0) { 369 dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret); 370 return ret; 371 } 372 } 373 374 return 0; 375} 376 377static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx) 378{ 379 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 380 int ret; 381 382 /* 383 * Init sequence was supplied by the panel vendor without much 384 * documentation. 385 */ 386 ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds, 387 ARRAY_SIZE(page3_cmds)); 388 if (ret < 0) 389 return ret; 390 391 ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds, 392 ARRAY_SIZE(page4_cmds)); 393 if (ret < 0) 394 return ret; 395 396 ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds, 397 ARRAY_SIZE(page1_cmds)); 398 if (ret < 0) 399 return ret; 400 401 ret = ltk050h3146w_a2_select_page(ctx, 0); 402 if (ret < 0) { 403 dev_err(ctx->dev, "failed to select page 0: %d\n", ret); 404 return ret; 405 } 406 407 /* vendor code called this without param, where there should be one */ 408 ret = mipi_dsi_dcs_set_tear_on(dsi, 0); 409 if (ret < 0) { 410 dev_err(ctx->dev, "failed to set tear on: %d\n", ret); 411 return ret; 412 } 413 414 msleep(60); 415 416 return 0; 417} 418 419static const struct drm_display_mode ltk050h3146w_a2_mode = { 420 .hdisplay = 720, 421 .hsync_start = 720 + 42, 422 .hsync_end = 720 + 42 + 10, 423 .htotal = 720 + 42 + 10 + 60, 424 .vdisplay = 1280, 425 .vsync_start = 1280 + 18, 426 .vsync_end = 1280 + 18 + 4, 427 .vtotal = 1280 + 18 + 4 + 12, 428 .clock = 65595, 429 .width_mm = 62, 430 .height_mm = 110, 431}; 432 433static const struct ltk050h3146w_desc ltk050h3146w_a2_data = { 434 .mode = <k050h3146w_a2_mode, 435 .init = ltk050h3146w_a2_init_sequence, 436}; 437 438static int ltk050h3146w_unprepare(struct drm_panel *panel) 439{ 440 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 441 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 442 int ret; 443 444 if (!ctx->prepared) 445 return 0; 446 447 ret = mipi_dsi_dcs_set_display_off(dsi); 448 if (ret < 0) { 449 dev_err(ctx->dev, "failed to set display off: %d\n", ret); 450 return ret; 451 } 452 453 mipi_dsi_dcs_enter_sleep_mode(dsi); 454 if (ret < 0) { 455 dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret); 456 return ret; 457 } 458 459 regulator_disable(ctx->iovcc); 460 regulator_disable(ctx->vci); 461 462 ctx->prepared = false; 463 464 return 0; 465} 466 467static int ltk050h3146w_prepare(struct drm_panel *panel) 468{ 469 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 470 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 471 int ret; 472 473 if (ctx->prepared) 474 return 0; 475 476 dev_dbg(ctx->dev, "Resetting the panel\n"); 477 ret = regulator_enable(ctx->vci); 478 if (ret < 0) { 479 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret); 480 return ret; 481 } 482 ret = regulator_enable(ctx->iovcc); 483 if (ret < 0) { 484 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret); 485 goto disable_vci; 486 } 487 488 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 489 usleep_range(5000, 6000); 490 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 491 msleep(20); 492 493 ret = ctx->panel_desc->init(ctx); 494 if (ret < 0) { 495 dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret); 496 goto disable_iovcc; 497 } 498 499 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 500 if (ret < 0) { 501 dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret); 502 goto disable_iovcc; 503 } 504 505 /* T9: 120ms */ 506 msleep(120); 507 508 ret = mipi_dsi_dcs_set_display_on(dsi); 509 if (ret < 0) { 510 dev_err(ctx->dev, "Failed to set display on: %d\n", ret); 511 goto disable_iovcc; 512 } 513 514 msleep(50); 515 516 ctx->prepared = true; 517 518 return 0; 519 520disable_iovcc: 521 regulator_disable(ctx->iovcc); 522disable_vci: 523 regulator_disable(ctx->vci); 524 return ret; 525} 526 527static int ltk050h3146w_get_modes(struct drm_panel *panel, 528 struct drm_connector *connector) 529{ 530 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 531 struct drm_display_mode *mode; 532 533 mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode); 534 if (!mode) 535 return -ENOMEM; 536 537 drm_mode_set_name(mode); 538 539 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 540 connector->display_info.width_mm = mode->width_mm; 541 connector->display_info.height_mm = mode->height_mm; 542 drm_mode_probed_add(connector, mode); 543 544 return 1; 545} 546 547static const struct drm_panel_funcs ltk050h3146w_funcs = { 548 .unprepare = ltk050h3146w_unprepare, 549 .prepare = ltk050h3146w_prepare, 550 .get_modes = ltk050h3146w_get_modes, 551}; 552 553static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) 554{ 555 struct device *dev = &dsi->dev; 556 struct ltk050h3146w *ctx; 557 int ret; 558 559 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 560 if (!ctx) 561 return -ENOMEM; 562 563 ctx->panel_desc = of_device_get_match_data(dev); 564 if (!ctx->panel_desc) 565 return -EINVAL; 566 567 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 568 if (IS_ERR(ctx->reset_gpio)) { 569 dev_err(dev, "cannot get reset gpio\n"); 570 return PTR_ERR(ctx->reset_gpio); 571 } 572 573 ctx->vci = devm_regulator_get(dev, "vci"); 574 if (IS_ERR(ctx->vci)) { 575 ret = PTR_ERR(ctx->vci); 576 if (ret != -EPROBE_DEFER) 577 dev_err(dev, "Failed to request vci regulator: %d\n", ret); 578 return ret; 579 } 580 581 ctx->iovcc = devm_regulator_get(dev, "iovcc"); 582 if (IS_ERR(ctx->iovcc)) { 583 ret = PTR_ERR(ctx->iovcc); 584 if (ret != -EPROBE_DEFER) 585 dev_err(dev, "Failed to request iovcc regulator: %d\n", ret); 586 return ret; 587 } 588 589 mipi_dsi_set_drvdata(dsi, ctx); 590 591 ctx->dev = dev; 592 593 dsi->lanes = 4; 594 dsi->format = MIPI_DSI_FMT_RGB888; 595 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 596 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; 597 598 drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs, 599 DRM_MODE_CONNECTOR_DSI); 600 601 ret = drm_panel_of_backlight(&ctx->panel); 602 if (ret) 603 return ret; 604 605 drm_panel_add(&ctx->panel); 606 607 ret = mipi_dsi_attach(dsi); 608 if (ret < 0) { 609 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 610 drm_panel_remove(&ctx->panel); 611 return ret; 612 } 613 614 return 0; 615} 616 617static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi) 618{ 619 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); 620 int ret; 621 622 ret = drm_panel_unprepare(&ctx->panel); 623 if (ret < 0) 624 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret); 625 626 ret = drm_panel_disable(&ctx->panel); 627 if (ret < 0) 628 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); 629} 630 631static int ltk050h3146w_remove(struct mipi_dsi_device *dsi) 632{ 633 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); 634 int ret; 635 636 ltk050h3146w_shutdown(dsi); 637 638 ret = mipi_dsi_detach(dsi); 639 if (ret < 0) 640 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 641 642 drm_panel_remove(&ctx->panel); 643 644 return 0; 645} 646 647static const struct of_device_id ltk050h3146w_of_match[] = { 648 { 649 .compatible = "leadtek,ltk050h3146w", 650 .data = <k050h3146w_data, 651 }, 652 { 653 .compatible = "leadtek,ltk050h3146w-a2", 654 .data = <k050h3146w_a2_data, 655 }, 656 { /* sentinel */ } 657}; 658MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match); 659 660static struct mipi_dsi_driver ltk050h3146w_driver = { 661 .driver = { 662 .name = "panel-leadtek-ltk050h3146w", 663 .of_match_table = ltk050h3146w_of_match, 664 }, 665 .probe = ltk050h3146w_probe, 666 .remove = ltk050h3146w_remove, 667 .shutdown = ltk050h3146w_shutdown, 668}; 669module_mipi_dsi_driver(ltk050h3146w_driver); 670 671MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 672MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel"); 673MODULE_LICENSE("GPL v2");