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/backlight.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 <drm/drmP.h>
  13#include <drm/drm_crtc.h>
  14#include <drm/drm_mipi_dsi.h>
  15#include <drm/drm_panel.h>
  16
  17#include <video/mipi_display.h>
  18
  19struct kingdisplay_panel {
  20        struct drm_panel base;
  21        struct mipi_dsi_device *link;
  22
  23        struct backlight_device *backlight;
  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        backlight_disable(kingdisplay->backlight);
 192
 193        err = mipi_dsi_dcs_set_display_off(kingdisplay->link);
 194        if (err < 0)
 195                DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
 196                              err);
 197
 198        kingdisplay->enabled = false;
 199
 200        return 0;
 201}
 202
 203static int kingdisplay_panel_unprepare(struct drm_panel *panel)
 204{
 205        struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
 206        int err;
 207
 208        if (!kingdisplay->prepared)
 209                return 0;
 210
 211        err = mipi_dsi_dcs_enter_sleep_mode(kingdisplay->link);
 212        if (err < 0) {
 213                DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
 214                              err);
 215                return err;
 216        }
 217
 218        /* T15: 120ms */
 219        msleep(120);
 220
 221        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);
 222
 223        err = regulator_disable(kingdisplay->supply);
 224        if (err < 0)
 225                return err;
 226
 227        kingdisplay->prepared = false;
 228
 229        return 0;
 230}
 231
 232static int kingdisplay_panel_prepare(struct drm_panel *panel)
 233{
 234        struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
 235        int err, regulator_err;
 236        unsigned int i;
 237
 238        if (kingdisplay->prepared)
 239                return 0;
 240
 241        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);
 242
 243        err = regulator_enable(kingdisplay->supply);
 244        if (err < 0)
 245                return err;
 246
 247        /* T2: 15ms */
 248        usleep_range(15000, 16000);
 249
 250        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 1);
 251
 252        /* T4: 15ms */
 253        usleep_range(15000, 16000);
 254
 255        for (i = 0; i < ARRAY_SIZE(init_code); i++) {
 256                err = mipi_dsi_generic_write(kingdisplay->link, &init_code[i],
 257                                        sizeof(struct kingdisplay_panel_cmd));
 258                if (err < 0) {
 259                        DRM_DEV_ERROR(panel->dev, "failed write init cmds: %d\n",
 260                                      err);
 261                        goto poweroff;
 262                }
 263        }
 264
 265        err = mipi_dsi_dcs_exit_sleep_mode(kingdisplay->link);
 266        if (err < 0) {
 267                DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
 268                              err);
 269                goto poweroff;
 270        }
 271
 272        /* T6: 120ms */
 273        msleep(120);
 274
 275        err = mipi_dsi_dcs_set_display_on(kingdisplay->link);
 276        if (err < 0) {
 277                DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
 278                              err);
 279                goto poweroff;
 280        }
 281
 282        /* T7: 10ms */
 283        usleep_range(10000, 11000);
 284
 285        kingdisplay->prepared = true;
 286
 287        return 0;
 288
 289poweroff:
 290        gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);
 291
 292        regulator_err = regulator_disable(kingdisplay->supply);
 293        if (regulator_err)
 294                DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
 295                              regulator_err);
 296
 297        return err;
 298}
 299
 300static int kingdisplay_panel_enable(struct drm_panel *panel)
 301{
 302        struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
 303        int ret;
 304
 305        if (kingdisplay->enabled)
 306                return 0;
 307
 308        ret = backlight_enable(kingdisplay->backlight);
 309        if (ret) {
 310                DRM_DEV_ERROR(panel->drm->dev,
 311                              "Failed to enable backlight %d\n", ret);
 312                return ret;
 313        }
 314
 315        kingdisplay->enabled = true;
 316
 317        return 0;
 318}
 319
 320static const struct drm_display_mode default_mode = {
 321        .clock = 229000,
 322        .hdisplay = 1536,
 323        .hsync_start = 1536 + 100,
 324        .hsync_end = 1536 + 100 + 24,
 325        .htotal = 1536 + 100 + 24 + 100,
 326        .vdisplay = 2048,
 327        .vsync_start = 2048 + 95,
 328        .vsync_end = 2048 + 95 + 2,
 329        .vtotal = 2048 + 95 + 2 + 23,
 330        .vrefresh = 60,
 331};
 332
 333static int kingdisplay_panel_get_modes(struct drm_panel *panel)
 334{
 335        struct drm_display_mode *mode;
 336
 337        mode = drm_mode_duplicate(panel->drm, &default_mode);
 338        if (!mode) {
 339                DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
 340                              default_mode.hdisplay, default_mode.vdisplay,
 341                              default_mode.vrefresh);
 342                return -ENOMEM;
 343        }
 344
 345        drm_mode_set_name(mode);
 346
 347        drm_mode_probed_add(panel->connector, mode);
 348
 349        panel->connector->display_info.width_mm = 147;
 350        panel->connector->display_info.height_mm = 196;
 351        panel->connector->display_info.bpc = 8;
 352
 353        return 1;
 354}
 355
 356static const struct drm_panel_funcs kingdisplay_panel_funcs = {
 357        .disable = kingdisplay_panel_disable,
 358        .unprepare = kingdisplay_panel_unprepare,
 359        .prepare = kingdisplay_panel_prepare,
 360        .enable = kingdisplay_panel_enable,
 361        .get_modes = kingdisplay_panel_get_modes,
 362};
 363
 364static const struct of_device_id kingdisplay_of_match[] = {
 365        { .compatible = "kingdisplay,kd097d04", },
 366        { }
 367};
 368MODULE_DEVICE_TABLE(of, kingdisplay_of_match);
 369
 370static int kingdisplay_panel_add(struct kingdisplay_panel *kingdisplay)
 371{
 372        struct device *dev = &kingdisplay->link->dev;
 373        int err;
 374
 375        kingdisplay->supply = devm_regulator_get(dev, "power");
 376        if (IS_ERR(kingdisplay->supply))
 377                return PTR_ERR(kingdisplay->supply);
 378
 379        kingdisplay->enable_gpio = devm_gpiod_get_optional(dev, "enable",
 380                                                           GPIOD_OUT_HIGH);
 381        if (IS_ERR(kingdisplay->enable_gpio)) {
 382                err = PTR_ERR(kingdisplay->enable_gpio);
 383                dev_dbg(dev, "failed to get enable gpio: %d\n", err);
 384                kingdisplay->enable_gpio = NULL;
 385        }
 386
 387        kingdisplay->backlight = devm_of_find_backlight(dev);
 388        if (IS_ERR(kingdisplay->backlight))
 389                return PTR_ERR(kingdisplay->backlight);
 390
 391        drm_panel_init(&kingdisplay->base);
 392        kingdisplay->base.funcs = &kingdisplay_panel_funcs;
 393        kingdisplay->base.dev = &kingdisplay->link->dev;
 394
 395        return drm_panel_add(&kingdisplay->base);
 396}
 397
 398static void kingdisplay_panel_del(struct kingdisplay_panel *kingdisplay)
 399{
 400        drm_panel_remove(&kingdisplay->base);
 401}
 402
 403static int kingdisplay_panel_probe(struct mipi_dsi_device *dsi)
 404{
 405        struct kingdisplay_panel *kingdisplay;
 406        int err;
 407
 408        dsi->lanes = 4;
 409        dsi->format = MIPI_DSI_FMT_RGB888;
 410        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
 411                          MIPI_DSI_MODE_LPM;
 412
 413        kingdisplay = devm_kzalloc(&dsi->dev, sizeof(*kingdisplay), GFP_KERNEL);
 414        if (!kingdisplay)
 415                return -ENOMEM;
 416
 417        mipi_dsi_set_drvdata(dsi, kingdisplay);
 418        kingdisplay->link = dsi;
 419
 420        err = kingdisplay_panel_add(kingdisplay);
 421        if (err < 0)
 422                return err;
 423
 424        return mipi_dsi_attach(dsi);
 425}
 426
 427static int kingdisplay_panel_remove(struct mipi_dsi_device *dsi)
 428{
 429        struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);
 430        int err;
 431
 432        err = kingdisplay_panel_unprepare(&kingdisplay->base);
 433        if (err < 0)
 434                DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
 435                              err);
 436
 437        err = kingdisplay_panel_disable(&kingdisplay->base);
 438        if (err < 0)
 439                DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
 440
 441        err = mipi_dsi_detach(dsi);
 442        if (err < 0)
 443                DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
 444                              err);
 445
 446        kingdisplay_panel_del(kingdisplay);
 447
 448        return 0;
 449}
 450
 451static void kingdisplay_panel_shutdown(struct mipi_dsi_device *dsi)
 452{
 453        struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);
 454
 455        kingdisplay_panel_unprepare(&kingdisplay->base);
 456        kingdisplay_panel_disable(&kingdisplay->base);
 457}
 458
 459static struct mipi_dsi_driver kingdisplay_panel_driver = {
 460        .driver = {
 461                .name = "panel-kingdisplay-kd097d04",
 462                .of_match_table = kingdisplay_of_match,
 463        },
 464        .probe = kingdisplay_panel_probe,
 465        .remove = kingdisplay_panel_remove,
 466        .shutdown = kingdisplay_panel_shutdown,
 467};
 468module_mipi_dsi_driver(kingdisplay_panel_driver);
 469
 470MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
 471MODULE_AUTHOR("Nickey Yang <nickey.yang@rock-chips.com>");
 472MODULE_DESCRIPTION("kingdisplay KD097D04 panel driver");
 473MODULE_LICENSE("GPL v2");
 474