linux/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
<<
>>
Prefs
   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 = &ltk050h3146w_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 = &ltk050h3146w_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, &ltk050h3146w_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 = &ltk050h3146w_data,
 651        },
 652        {
 653                .compatible = "leadtek,ltk050h3146w-a2",
 654                .data = &ltk050h3146w_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");
 674