linux/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   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
  20struct kingdisplay_panel {
  21        struct drm_panel base;
  22        struct mipi_dsi_device *link;
  23
  24        struct regulator *supply;
  25        struct gpio_desc *enable_gpio;
  26
  27        bool prepared;
  28        bool enabled;
  29};
  30
  31struct kingdisplay_panel_cmd {
  32        char cmd;
  33        char data;
  34};
  35
  36/*
  37 * According to the discussion on
  38 * https://review.coreboot.org/#/c/coreboot/+/22472/
  39 * the panel init array is not part of the panels datasheet but instead
  40 * just came in this form from the panel vendor.
  41 */
  42static const struct kingdisplay_panel_cmd init_code[] = {
  43        /* voltage setting */
  44        { 0xB0, 0x00 },
  45        { 0xB2, 0x02 },
  46        { 0xB3, 0x11 },
  47        { 0xB4, 0x00 },
  48        { 0xB6, 0x80 },
  49        /* VCOM disable */
  50        { 0xB7, 0x02 },
  51        { 0xB8, 0x80 },
  52        { 0xBA, 0x43 },
  53        /* VCOM setting */
  54        { 0xBB, 0x53 },
  55        /* VSP setting */
  56        { 0xBC, 0x0A },
  57        /* VSN setting */
  58        { 0xBD, 0x4A },
  59        /* VGH setting */
  60        { 0xBE, 0x2F },
  61        /* VGL setting */
  62        { 0xBF, 0x1A },
  63        { 0xF0, 0x39 },
  64        { 0xF1, 0x22 },
  65        /* Gamma setting */
  66        { 0xB0, 0x02 },
  67        { 0xC0, 0x00 },
  68        { 0xC1, 0x01 },
  69        { 0xC2, 0x0B },
  70        { 0xC3, 0x15 },
  71        { 0xC4, 0x22 },
  72        { 0xC5, 0x11 },
  73        { 0xC6, 0x15 },
  74        { 0xC7, 0x19 },
  75        { 0xC8, 0x1A },
  76        { 0xC9, 0x16 },
  77        { 0xCA, 0x18 },
  78        { 0xCB, 0x13 },
  79        { 0xCC, 0x18 },
  80        { 0xCD, 0x13 },
  81        { 0xCE, 0x1C },
  82        { 0xCF, 0x19 },
  83        { 0xD0, 0x21 },
  84        { 0xD1, 0x2C },
  85        { 0xD2, 0x2F },
  86        { 0xD3, 0x30 },
  87        { 0xD4, 0x19 },
  88        { 0xD5, 0x1F },
  89        { 0xD6, 0x00 },
  90        { 0xD7, 0x01 },
  91        { 0xD8, 0x0B },
  92        { 0xD9, 0x15 },
  93        { 0xDA, 0x22 },
  94        { 0xDB, 0x11 },
  95        { 0xDC, 0x15 },
  96        { 0xDD, 0x19 },
  97        { 0xDE, 0x1A },
  98        { 0xDF, 0x16 },
  99        { 0xE0, 0x18 },
 100        { 0xE1, 0x13 },
 101        { 0xE2, 0x18 },
 102        { 0xE3, 0x13 },
 103        { 0xE4, 0x1C },
 104        { 0xE5, 0x19 },
 105        { 0xE6, 0x21 },
 106        { 0xE7, 0x2C },
 107        { 0xE8, 0x2F },
 108        { 0xE9, 0x30 },
 109        { 0xEA, 0x19 },
 110        { 0xEB, 0x1F },
 111        /* GOA MUX setting */
 112        { 0xB0, 0x01 },
 113        { 0xC0, 0x10 },
 114        { 0xC1, 0x0F },
 115        { 0xC2, 0x0E },
 116        { 0xC3, 0x0D },
 117        { 0xC4, 0x0C },
 118        { 0xC5, 0x0B },
 119        { 0xC6, 0x0A },
 120        { 0xC7, 0x09 },
 121        { 0xC8, 0x08 },
 122        { 0xC9, 0x07 },
 123        { 0xCA, 0x06 },
 124        { 0xCB, 0x05 },
 125        { 0xCC, 0x00 },
 126        { 0xCD, 0x01 },
 127        { 0xCE, 0x02 },
 128        { 0xCF, 0x03 },
 129        { 0xD0, 0x04 },
 130        { 0xD6, 0x10 },
 131        { 0xD7, 0x0F },
 132        { 0xD8, 0x0E },
 133        { 0xD9, 0x0D },
 134        { 0xDA, 0x0C },
 135        { 0xDB, 0x0B },
 136        { 0xDC, 0x0A },
 137        { 0xDD, 0x09 },
 138        { 0xDE, 0x08 },
 139        { 0xDF, 0x07 },
 140        { 0xE0, 0x06 },
 141        { 0xE1, 0x05 },
 142        { 0xE2, 0x00 },
 143        { 0xE3, 0x01 },
 144        { 0xE4, 0x02 },
 145        { 0xE5, 0x03 },
 146        { 0xE6, 0x04 },
 147        { 0xE7, 0x00 },
 148        { 0xEC, 0xC0 },
 149        /* GOA timing setting */
 150        { 0xB0, 0x03 },
 151        { 0xC0, 0x01 },
 152        { 0xC2, 0x6F },
 153        { 0xC3, 0x6F },
 154        { 0xC5, 0x36 },
 155        { 0xC8, 0x08 },
 156        { 0xC9, 0x04 },
 157        { 0xCA, 0x41 },
 158        { 0xCC, 0x43 },
 159        { 0xCF, 0x60 },
 160        { 0xD2, 0x04 },
 161        { 0xD3, 0x04 },
 162        { 0xD4, 0x03 },
 163        { 0xD5, 0x02 },
 164        { 0xD6, 0x01 },
 165        { 0xD7, 0x00 },
 166        { 0xDB, 0x01 },
 167        { 0xDE, 0x36 },
 168        { 0xE6, 0x6F },
 169        { 0xE7, 0x6F },
 170        /* GOE setting */
 171        { 0xB0, 0x06 },
 172        { 0xB8, 0xA5 },
 173        { 0xC0, 0xA5 },
 174        { 0xD5, 0x3F },
 175};
 176
 177static inline
 178struct kingdisplay_panel *to_kingdisplay_panel(struct drm_panel *panel)
 179{
 180        return container_of(panel, struct kingdisplay_panel, base);
 181}
 182
 183static int kingdisplay_panel_disable(struct drm_panel *panel)
 184{
 185        struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
 186        int err;
 187
 188        if (!kingdisplay->enabled)
 189                return 0;
 190
 191        err = mipi_dsi_dcs_set_display_off(kingdisplay->link);
 192        if (err < 0)
 193                dev_err(panel->dev, "failed to set display off: %d\n", err);
 194
 195        kingdisplay->enabled = false;
 196
 197        return 0;
 198}
 199
 200static int kingdisplay_panel_unprepare(struct drm_panel *panel)
 201{
 202        struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
 203        int err;
 204
 205        if (!kingdisplay->prepared)
 206                return 0;
 207
 208        err = mipi_dsi_dcs_enter_sleep_mode(kingdisplay->link);
 209        if (err < 0) {
 210                dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
 211                return err;
 212        }
 213
 214        /* T15: 120ms */
 215        msleep(120);
 216
 217        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);
 218
 219        err = regulator_disable(kingdisplay->supply);
 220        if (err < 0)
 221                return err;
 222
 223        kingdisplay->prepared = false;
 224
 225        return 0;
 226}
 227
 228static int kingdisplay_panel_prepare(struct drm_panel *panel)
 229{
 230        struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
 231        int err, regulator_err;
 232        unsigned int i;
 233
 234        if (kingdisplay->prepared)
 235                return 0;
 236
 237        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);
 238
 239        err = regulator_enable(kingdisplay->supply);
 240        if (err < 0)
 241                return err;
 242
 243        /* T2: 15ms */
 244        usleep_range(15000, 16000);
 245
 246        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 1);
 247
 248        /* T4: 15ms */
 249        usleep_range(15000, 16000);
 250
 251        for (i = 0; i < ARRAY_SIZE(init_code); i++) {
 252                err = mipi_dsi_generic_write(kingdisplay->link, &init_code[i],
 253                                        sizeof(struct kingdisplay_panel_cmd));
 254                if (err < 0) {
 255                        dev_err(panel->dev, "failed write init cmds: %d\n", err);
 256                        goto poweroff;
 257                }
 258        }
 259
 260        err = mipi_dsi_dcs_exit_sleep_mode(kingdisplay->link);
 261        if (err < 0) {
 262                dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
 263                goto poweroff;
 264        }
 265
 266        /* T6: 120ms */
 267        msleep(120);
 268
 269        err = mipi_dsi_dcs_set_display_on(kingdisplay->link);
 270        if (err < 0) {
 271                dev_err(panel->dev, "failed to set display on: %d\n", err);
 272                goto poweroff;
 273        }
 274
 275        /* T7: 10ms */
 276        usleep_range(10000, 11000);
 277
 278        kingdisplay->prepared = true;
 279
 280        return 0;
 281
 282poweroff:
 283        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);
 284
 285        regulator_err = regulator_disable(kingdisplay->supply);
 286        if (regulator_err)
 287                dev_err(panel->dev, "failed to disable regulator: %d\n", regulator_err);
 288
 289        return err;
 290}
 291
 292static int kingdisplay_panel_enable(struct drm_panel *panel)
 293{
 294        struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
 295
 296        if (kingdisplay->enabled)
 297                return 0;
 298
 299        kingdisplay->enabled = true;
 300
 301        return 0;
 302}
 303
 304static const struct drm_display_mode default_mode = {
 305        .clock = 229000,
 306        .hdisplay = 1536,
 307        .hsync_start = 1536 + 100,
 308        .hsync_end = 1536 + 100 + 24,
 309        .htotal = 1536 + 100 + 24 + 100,
 310        .vdisplay = 2048,
 311        .vsync_start = 2048 + 95,
 312        .vsync_end = 2048 + 95 + 2,
 313        .vtotal = 2048 + 95 + 2 + 23,
 314};
 315
 316static int kingdisplay_panel_get_modes(struct drm_panel *panel,
 317                                       struct drm_connector *connector)
 318{
 319        struct drm_display_mode *mode;
 320
 321        mode = drm_mode_duplicate(connector->dev, &default_mode);
 322        if (!mode) {
 323                dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
 324                        default_mode.hdisplay, default_mode.vdisplay,
 325                        drm_mode_vrefresh(&default_mode));
 326                return -ENOMEM;
 327        }
 328
 329        drm_mode_set_name(mode);
 330
 331        drm_mode_probed_add(connector, mode);
 332
 333        connector->display_info.width_mm = 147;
 334        connector->display_info.height_mm = 196;
 335        connector->display_info.bpc = 8;
 336
 337        return 1;
 338}
 339
 340static const struct drm_panel_funcs kingdisplay_panel_funcs = {
 341        .disable = kingdisplay_panel_disable,
 342        .unprepare = kingdisplay_panel_unprepare,
 343        .prepare = kingdisplay_panel_prepare,
 344        .enable = kingdisplay_panel_enable,
 345        .get_modes = kingdisplay_panel_get_modes,
 346};
 347
 348static const struct of_device_id kingdisplay_of_match[] = {
 349        { .compatible = "kingdisplay,kd097d04", },
 350        { }
 351};
 352MODULE_DEVICE_TABLE(of, kingdisplay_of_match);
 353
 354static int kingdisplay_panel_add(struct kingdisplay_panel *kingdisplay)
 355{
 356        struct device *dev = &kingdisplay->link->dev;
 357        int err;
 358
 359        kingdisplay->supply = devm_regulator_get(dev, "power");
 360        if (IS_ERR(kingdisplay->supply))
 361                return PTR_ERR(kingdisplay->supply);
 362
 363        kingdisplay->enable_gpio = devm_gpiod_get_optional(dev, "enable",
 364                                                           GPIOD_OUT_HIGH);
 365        if (IS_ERR(kingdisplay->enable_gpio)) {
 366                err = PTR_ERR(kingdisplay->enable_gpio);
 367                dev_dbg(dev, "failed to get enable gpio: %d\n", err);
 368                kingdisplay->enable_gpio = NULL;
 369        }
 370
 371        drm_panel_init(&kingdisplay->base, &kingdisplay->link->dev,
 372                       &kingdisplay_panel_funcs, DRM_MODE_CONNECTOR_DSI);
 373
 374        err = drm_panel_of_backlight(&kingdisplay->base);
 375        if (err)
 376                return err;
 377
 378        drm_panel_add(&kingdisplay->base);
 379
 380        return 0;
 381}
 382
 383static void kingdisplay_panel_del(struct kingdisplay_panel *kingdisplay)
 384{
 385        drm_panel_remove(&kingdisplay->base);
 386}
 387
 388static int kingdisplay_panel_probe(struct mipi_dsi_device *dsi)
 389{
 390        struct kingdisplay_panel *kingdisplay;
 391        int err;
 392
 393        dsi->lanes = 4;
 394        dsi->format = MIPI_DSI_FMT_RGB888;
 395        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
 396                          MIPI_DSI_MODE_LPM;
 397
 398        kingdisplay = devm_kzalloc(&dsi->dev, sizeof(*kingdisplay), GFP_KERNEL);
 399        if (!kingdisplay)
 400                return -ENOMEM;
 401
 402        mipi_dsi_set_drvdata(dsi, kingdisplay);
 403        kingdisplay->link = dsi;
 404
 405        err = kingdisplay_panel_add(kingdisplay);
 406        if (err < 0)
 407                return err;
 408
 409        return mipi_dsi_attach(dsi);
 410}
 411
 412static int kingdisplay_panel_remove(struct mipi_dsi_device *dsi)
 413{
 414        struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);
 415        int err;
 416
 417        err = drm_panel_unprepare(&kingdisplay->base);
 418        if (err < 0)
 419                dev_err(&dsi->dev, "failed to unprepare panel: %d\n", err);
 420
 421        err = drm_panel_disable(&kingdisplay->base);
 422        if (err < 0)
 423                dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
 424
 425        err = mipi_dsi_detach(dsi);
 426        if (err < 0)
 427                dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
 428
 429        kingdisplay_panel_del(kingdisplay);
 430
 431        return 0;
 432}
 433
 434static void kingdisplay_panel_shutdown(struct mipi_dsi_device *dsi)
 435{
 436        struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);
 437
 438        drm_panel_unprepare(&kingdisplay->base);
 439        drm_panel_disable(&kingdisplay->base);
 440}
 441
 442static struct mipi_dsi_driver kingdisplay_panel_driver = {
 443        .driver = {
 444                .name = "panel-kingdisplay-kd097d04",
 445                .of_match_table = kingdisplay_of_match,
 446        },
 447        .probe = kingdisplay_panel_probe,
 448        .remove = kingdisplay_panel_remove,
 449        .shutdown = kingdisplay_panel_shutdown,
 450};
 451module_mipi_dsi_driver(kingdisplay_panel_driver);
 452
 453MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
 454MODULE_AUTHOR("Nickey Yang <nickey.yang@rock-chips.com>");
 455MODULE_DESCRIPTION("kingdisplay KD097D04 panel driver");
 456MODULE_LICENSE("GPL v2");
 457