linux/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2019 Theobroma Systems Design und Consulting GmbH
   4 *
   5 * base on panel-kingdisplay-kd097d04.c
   6 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
   7 */
   8
   9#include <linux/backlight.h>
  10#include <linux/delay.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/regulator/consumer.h>
  15
  16#include <video/mipi_display.h>
  17
  18#include <drm/drm_crtc.h>
  19#include <drm/drm_device.h>
  20#include <drm/drm_mipi_dsi.h>
  21#include <drm/drm_modes.h>
  22#include <drm/drm_panel.h>
  23
  24struct ltk500hd1829 {
  25        struct device *dev;
  26        struct drm_panel panel;
  27        struct gpio_desc *reset_gpio;
  28        struct regulator *vcc;
  29        struct regulator *iovcc;
  30        bool prepared;
  31};
  32
  33struct ltk500hd1829_cmd {
  34        char cmd;
  35        char data;
  36};
  37
  38/*
  39 * There is no description in the Reference Manual about these commands.
  40 * We received them from the vendor, so just use them as is.
  41 */
  42static const struct ltk500hd1829_cmd init_code[] = {
  43        { 0xE0, 0x00 },
  44        { 0xE1, 0x93 },
  45        { 0xE2, 0x65 },
  46        { 0xE3, 0xF8 },
  47        { 0x80, 0x03 },
  48        { 0xE0, 0x04 },
  49        { 0x2D, 0x03 },
  50        { 0xE0, 0x01 },
  51        { 0x00, 0x00 },
  52        { 0x01, 0xB6 },
  53        { 0x03, 0x00 },
  54        { 0x04, 0xC5 },
  55        { 0x17, 0x00 },
  56        { 0x18, 0xBF },
  57        { 0x19, 0x01 },
  58        { 0x1A, 0x00 },
  59        { 0x1B, 0xBF },
  60        { 0x1C, 0x01 },
  61        { 0x1F, 0x7C },
  62        { 0x20, 0x26 },
  63        { 0x21, 0x26 },
  64        { 0x22, 0x4E },
  65        { 0x37, 0x09 },
  66        { 0x38, 0x04 },
  67        { 0x39, 0x08 },
  68        { 0x3A, 0x1F },
  69        { 0x3B, 0x1F },
  70        { 0x3C, 0x78 },
  71        { 0x3D, 0xFF },
  72        { 0x3E, 0xFF },
  73        { 0x3F, 0x00 },
  74        { 0x40, 0x04 },
  75        { 0x41, 0xA0 },
  76        { 0x43, 0x0F },
  77        { 0x44, 0x0A },
  78        { 0x45, 0x24 },
  79        { 0x55, 0x01 },
  80        { 0x56, 0x01 },
  81        { 0x57, 0xA5 },
  82        { 0x58, 0x0A },
  83        { 0x59, 0x4A },
  84        { 0x5A, 0x38 },
  85        { 0x5B, 0x10 },
  86        { 0x5C, 0x19 },
  87        { 0x5D, 0x7C },
  88        { 0x5E, 0x64 },
  89        { 0x5F, 0x54 },
  90        { 0x60, 0x48 },
  91        { 0x61, 0x44 },
  92        { 0x62, 0x35 },
  93        { 0x63, 0x3A },
  94        { 0x64, 0x24 },
  95        { 0x65, 0x3B },
  96        { 0x66, 0x39 },
  97        { 0x67, 0x37 },
  98        { 0x68, 0x56 },
  99        { 0x69, 0x41 },
 100        { 0x6A, 0x47 },
 101        { 0x6B, 0x2F },
 102        { 0x6C, 0x23 },
 103        { 0x6D, 0x13 },
 104        { 0x6E, 0x02 },
 105        { 0x6F, 0x08 },
 106        { 0x70, 0x7C },
 107        { 0x71, 0x64 },
 108        { 0x72, 0x54 },
 109        { 0x73, 0x48 },
 110        { 0x74, 0x44 },
 111        { 0x75, 0x35 },
 112        { 0x76, 0x3A },
 113        { 0x77, 0x22 },
 114        { 0x78, 0x3B },
 115        { 0x79, 0x39 },
 116        { 0x7A, 0x38 },
 117        { 0x7B, 0x52 },
 118        { 0x7C, 0x41 },
 119        { 0x7D, 0x47 },
 120        { 0x7E, 0x2F },
 121        { 0x7F, 0x23 },
 122        { 0x80, 0x13 },
 123        { 0x81, 0x02 },
 124        { 0x82, 0x08 },
 125        { 0xE0, 0x02 },
 126        { 0x00, 0x57 },
 127        { 0x01, 0x77 },
 128        { 0x02, 0x44 },
 129        { 0x03, 0x46 },
 130        { 0x04, 0x48 },
 131        { 0x05, 0x4A },
 132        { 0x06, 0x4C },
 133        { 0x07, 0x4E },
 134        { 0x08, 0x50 },
 135        { 0x09, 0x55 },
 136        { 0x0A, 0x52 },
 137        { 0x0B, 0x55 },
 138        { 0x0C, 0x55 },
 139        { 0x0D, 0x55 },
 140        { 0x0E, 0x55 },
 141        { 0x0F, 0x55 },
 142        { 0x10, 0x55 },
 143        { 0x11, 0x55 },
 144        { 0x12, 0x55 },
 145        { 0x13, 0x40 },
 146        { 0x14, 0x55 },
 147        { 0x15, 0x55 },
 148        { 0x16, 0x57 },
 149        { 0x17, 0x77 },
 150        { 0x18, 0x45 },
 151        { 0x19, 0x47 },
 152        { 0x1A, 0x49 },
 153        { 0x1B, 0x4B },
 154        { 0x1C, 0x4D },
 155        { 0x1D, 0x4F },
 156        { 0x1E, 0x51 },
 157        { 0x1F, 0x55 },
 158        { 0x20, 0x53 },
 159        { 0x21, 0x55 },
 160        { 0x22, 0x55 },
 161        { 0x23, 0x55 },
 162        { 0x24, 0x55 },
 163        { 0x25, 0x55 },
 164        { 0x26, 0x55 },
 165        { 0x27, 0x55 },
 166        { 0x28, 0x55 },
 167        { 0x29, 0x41 },
 168        { 0x2A, 0x55 },
 169        { 0x2B, 0x55 },
 170        { 0x2C, 0x57 },
 171        { 0x2D, 0x77 },
 172        { 0x2E, 0x4F },
 173        { 0x2F, 0x4D },
 174        { 0x30, 0x4B },
 175        { 0x31, 0x49 },
 176        { 0x32, 0x47 },
 177        { 0x33, 0x45 },
 178        { 0x34, 0x41 },
 179        { 0x35, 0x55 },
 180        { 0x36, 0x53 },
 181        { 0x37, 0x55 },
 182        { 0x38, 0x55 },
 183        { 0x39, 0x55 },
 184        { 0x3A, 0x55 },
 185        { 0x3B, 0x55 },
 186        { 0x3C, 0x55 },
 187        { 0x3D, 0x55 },
 188        { 0x3E, 0x55 },
 189        { 0x3F, 0x51 },
 190        { 0x40, 0x55 },
 191        { 0x41, 0x55 },
 192        { 0x42, 0x57 },
 193        { 0x43, 0x77 },
 194        { 0x44, 0x4E },
 195        { 0x45, 0x4C },
 196        { 0x46, 0x4A },
 197        { 0x47, 0x48 },
 198        { 0x48, 0x46 },
 199        { 0x49, 0x44 },
 200        { 0x4A, 0x40 },
 201        { 0x4B, 0x55 },
 202        { 0x4C, 0x52 },
 203        { 0x4D, 0x55 },
 204        { 0x4E, 0x55 },
 205        { 0x4F, 0x55 },
 206        { 0x50, 0x55 },
 207        { 0x51, 0x55 },
 208        { 0x52, 0x55 },
 209        { 0x53, 0x55 },
 210        { 0x54, 0x55 },
 211        { 0x55, 0x50 },
 212        { 0x56, 0x55 },
 213        { 0x57, 0x55 },
 214        { 0x58, 0x40 },
 215        { 0x59, 0x00 },
 216        { 0x5A, 0x00 },
 217        { 0x5B, 0x10 },
 218        { 0x5C, 0x09 },
 219        { 0x5D, 0x30 },
 220        { 0x5E, 0x01 },
 221        { 0x5F, 0x02 },
 222        { 0x60, 0x30 },
 223        { 0x61, 0x03 },
 224        { 0x62, 0x04 },
 225        { 0x63, 0x06 },
 226        { 0x64, 0x6A },
 227        { 0x65, 0x75 },
 228        { 0x66, 0x0F },
 229        { 0x67, 0xB3 },
 230        { 0x68, 0x0B },
 231        { 0x69, 0x06 },
 232        { 0x6A, 0x6A },
 233        { 0x6B, 0x10 },
 234        { 0x6C, 0x00 },
 235        { 0x6D, 0x04 },
 236        { 0x6E, 0x04 },
 237        { 0x6F, 0x88 },
 238        { 0x70, 0x00 },
 239        { 0x71, 0x00 },
 240        { 0x72, 0x06 },
 241        { 0x73, 0x7B },
 242        { 0x74, 0x00 },
 243        { 0x75, 0xBC },
 244        { 0x76, 0x00 },
 245        { 0x77, 0x05 },
 246        { 0x78, 0x2E },
 247        { 0x79, 0x00 },
 248        { 0x7A, 0x00 },
 249        { 0x7B, 0x00 },
 250        { 0x7C, 0x00 },
 251        { 0x7D, 0x03 },
 252        { 0x7E, 0x7B },
 253        { 0xE0, 0x04 },
 254        { 0x09, 0x10 },
 255        { 0x2B, 0x2B },
 256        { 0x2E, 0x44 },
 257        { 0xE0, 0x00 },
 258        { 0xE6, 0x02 },
 259        { 0xE7, 0x02 },
 260        { 0x35, 0x00 },
 261};
 262
 263static inline
 264struct ltk500hd1829 *panel_to_ltk500hd1829(struct drm_panel *panel)
 265{
 266        return container_of(panel, struct ltk500hd1829, panel);
 267}
 268
 269static int ltk500hd1829_unprepare(struct drm_panel *panel)
 270{
 271        struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);
 272        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 273        int ret;
 274
 275        if (!ctx->prepared)
 276                return 0;
 277
 278        ret = mipi_dsi_dcs_set_display_off(dsi);
 279        if (ret < 0)
 280                dev_err(panel->dev, "failed to set display off: %d\n", ret);
 281
 282        ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
 283        if (ret < 0) {
 284                dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);
 285        }
 286
 287        /* 120ms to enter sleep mode */
 288        msleep(120);
 289
 290        regulator_disable(ctx->iovcc);
 291        regulator_disable(ctx->vcc);
 292
 293        ctx->prepared = false;
 294
 295        return 0;
 296}
 297
 298static int ltk500hd1829_prepare(struct drm_panel *panel)
 299{
 300        struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);
 301        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 302        unsigned int i;
 303        int ret;
 304
 305        if (ctx->prepared)
 306                return 0;
 307
 308        ret = regulator_enable(ctx->vcc);
 309        if (ret < 0) {
 310                dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
 311                return ret;
 312        }
 313        ret = regulator_enable(ctx->iovcc);
 314        if (ret < 0) {
 315                dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
 316                goto disable_vcc;
 317        }
 318
 319        gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 320        /* tRW: 10us */
 321        usleep_range(10, 20);
 322        gpiod_set_value_cansleep(ctx->reset_gpio, 0);
 323
 324        /* tRT: >= 5ms */
 325        usleep_range(5000, 6000);
 326
 327        for (i = 0; i < ARRAY_SIZE(init_code); i++) {
 328                ret = mipi_dsi_generic_write(dsi, &init_code[i],
 329                                             sizeof(struct ltk500hd1829_cmd));
 330                if (ret < 0) {
 331                        dev_err(panel->dev, "failed to write init cmds: %d\n", ret);
 332                        goto disable_iovcc;
 333                }
 334        }
 335
 336        ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
 337        if (ret < 0) {
 338                dev_err(panel->dev, "failed to exit sleep mode: %d\n", ret);
 339                goto disable_iovcc;
 340        }
 341
 342        /* 120ms to exit sleep mode */
 343        msleep(120);
 344
 345        ret = mipi_dsi_dcs_set_display_on(dsi);
 346        if (ret < 0) {
 347                dev_err(panel->dev, "failed to set display on: %d\n", ret);
 348                goto disable_iovcc;
 349        }
 350
 351        ctx->prepared = true;
 352
 353        return 0;
 354
 355disable_iovcc:
 356        regulator_disable(ctx->iovcc);
 357disable_vcc:
 358        regulator_disable(ctx->vcc);
 359        return ret;
 360}
 361
 362static const struct drm_display_mode default_mode = {
 363        .hdisplay       = 720,
 364        .hsync_start    = 720 + 50,
 365        .hsync_end      = 720 + 50 + 50,
 366        .htotal         = 720 + 50 + 50 + 50,
 367        .vdisplay       = 1280,
 368        .vsync_start    = 1280 + 30,
 369        .vsync_end      = 1280 + 30 + 4,
 370        .vtotal         = 1280 + 30 + 4 + 12,
 371        .clock          = 69217,
 372        .width_mm       = 62,
 373        .height_mm      = 110,
 374};
 375
 376static int ltk500hd1829_get_modes(struct drm_panel *panel,
 377                                  struct drm_connector *connector)
 378{
 379        struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);
 380        struct drm_display_mode *mode;
 381
 382        mode = drm_mode_duplicate(connector->dev, &default_mode);
 383        if (!mode) {
 384                dev_err(ctx->dev, "failed to add mode %ux%u@%u\n",
 385                        default_mode.hdisplay, default_mode.vdisplay,
 386                        drm_mode_vrefresh(&default_mode));
 387                return -ENOMEM;
 388        }
 389
 390        drm_mode_set_name(mode);
 391
 392        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 393        connector->display_info.width_mm = mode->width_mm;
 394        connector->display_info.height_mm = mode->height_mm;
 395        drm_mode_probed_add(connector, mode);
 396
 397        return 1;
 398}
 399
 400static const struct drm_panel_funcs ltk500hd1829_funcs = {
 401        .unprepare = ltk500hd1829_unprepare,
 402        .prepare = ltk500hd1829_prepare,
 403        .get_modes = ltk500hd1829_get_modes,
 404};
 405
 406static int ltk500hd1829_probe(struct mipi_dsi_device *dsi)
 407{
 408        struct ltk500hd1829 *ctx;
 409        struct device *dev = &dsi->dev;
 410        int ret;
 411
 412        ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
 413        if (!ctx)
 414                return -ENOMEM;
 415
 416        ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
 417        if (IS_ERR(ctx->reset_gpio)) {
 418                dev_err(dev, "cannot get reset gpio\n");
 419                return PTR_ERR(ctx->reset_gpio);
 420        }
 421
 422        ctx->vcc = devm_regulator_get(dev, "vcc");
 423        if (IS_ERR(ctx->vcc)) {
 424                ret = PTR_ERR(ctx->vcc);
 425                if (ret != -EPROBE_DEFER)
 426                        dev_err(dev, "Failed to request vcc regulator: %d\n", ret);
 427                return ret;
 428        }
 429
 430        ctx->iovcc = devm_regulator_get(dev, "iovcc");
 431        if (IS_ERR(ctx->iovcc)) {
 432                ret = PTR_ERR(ctx->iovcc);
 433                if (ret != -EPROBE_DEFER)
 434                        dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
 435                return ret;
 436        }
 437
 438        mipi_dsi_set_drvdata(dsi, ctx);
 439
 440        ctx->dev = dev;
 441
 442        dsi->lanes = 4;
 443        dsi->format = MIPI_DSI_FMT_RGB888;
 444        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
 445                          MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
 446
 447        drm_panel_init(&ctx->panel, &dsi->dev, &ltk500hd1829_funcs,
 448                       DRM_MODE_CONNECTOR_DSI);
 449
 450        ret = drm_panel_of_backlight(&ctx->panel);
 451        if (ret)
 452                return ret;
 453
 454        drm_panel_add(&ctx->panel);
 455
 456        ret = mipi_dsi_attach(dsi);
 457        if (ret < 0) {
 458                dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
 459                drm_panel_remove(&ctx->panel);
 460                return ret;
 461        }
 462
 463        return 0;
 464}
 465
 466static void ltk500hd1829_shutdown(struct mipi_dsi_device *dsi)
 467{
 468        struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi);
 469        int ret;
 470
 471        ret = drm_panel_unprepare(&ctx->panel);
 472        if (ret < 0)
 473                dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
 474
 475        ret = drm_panel_disable(&ctx->panel);
 476        if (ret < 0)
 477                dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
 478}
 479
 480static int ltk500hd1829_remove(struct mipi_dsi_device *dsi)
 481{
 482        struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi);
 483        int ret;
 484
 485        ltk500hd1829_shutdown(dsi);
 486
 487        ret = mipi_dsi_detach(dsi);
 488        if (ret < 0)
 489                dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
 490
 491        drm_panel_remove(&ctx->panel);
 492
 493        return 0;
 494}
 495
 496static const struct of_device_id ltk500hd1829_of_match[] = {
 497        { .compatible = "leadtek,ltk500hd1829", },
 498        { /* sentinel */ }
 499};
 500MODULE_DEVICE_TABLE(of, ltk500hd1829_of_match);
 501
 502static struct mipi_dsi_driver ltk500hd1829_driver = {
 503        .driver = {
 504                .name = "panel-leadtek-ltk500hd1829",
 505                .of_match_table = ltk500hd1829_of_match,
 506        },
 507        .probe = ltk500hd1829_probe,
 508        .remove = ltk500hd1829_remove,
 509        .shutdown = ltk500hd1829_shutdown,
 510};
 511module_mipi_dsi_driver(ltk500hd1829_driver);
 512
 513MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
 514MODULE_DESCRIPTION("Leadtek LTK500HD1829 panel driver");
 515MODULE_LICENSE("GPL v2");
 516