linux/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2016 InforceComputing
   4 * Author: Vinay Simha BN <simhavcs@gmail.com>
   5 *
   6 * Copyright (C) 2016 Linaro Ltd
   7 * Author: Sumit Semwal <sumit.semwal@linaro.org>
   8 *
   9 * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
  10 * JDI model LT070ME05000, and its data sheet is at:
  11 * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
  12 */
  13
  14#include <linux/backlight.h>
  15#include <linux/delay.h>
  16#include <linux/gpio/consumer.h>
  17#include <linux/module.h>
  18#include <linux/of.h>
  19#include <linux/regulator/consumer.h>
  20
  21#include <video/mipi_display.h>
  22
  23#include <drm/drm_crtc.h>
  24#include <drm/drm_mipi_dsi.h>
  25#include <drm/drm_modes.h>
  26#include <drm/drm_panel.h>
  27
  28static const char * const regulator_names[] = {
  29        "vddp",
  30        "iovcc"
  31};
  32
  33struct jdi_panel {
  34        struct drm_panel base;
  35        struct mipi_dsi_device *dsi;
  36
  37        struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
  38
  39        struct gpio_desc *enable_gpio;
  40        struct gpio_desc *reset_gpio;
  41        struct gpio_desc *dcdc_en_gpio;
  42        struct backlight_device *backlight;
  43
  44        bool prepared;
  45        bool enabled;
  46
  47        const struct drm_display_mode *mode;
  48};
  49
  50static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
  51{
  52        return container_of(panel, struct jdi_panel, base);
  53}
  54
  55static int jdi_panel_init(struct jdi_panel *jdi)
  56{
  57        struct mipi_dsi_device *dsi = jdi->dsi;
  58        struct device *dev = &jdi->dsi->dev;
  59        int ret;
  60
  61        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
  62
  63        ret = mipi_dsi_dcs_soft_reset(dsi);
  64        if (ret < 0)
  65                return ret;
  66
  67        usleep_range(10000, 20000);
  68
  69        ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4);
  70        if (ret < 0) {
  71                dev_err(dev, "failed to set pixel format: %d\n", ret);
  72                return ret;
  73        }
  74
  75        ret = mipi_dsi_dcs_set_column_address(dsi, 0, jdi->mode->hdisplay - 1);
  76        if (ret < 0) {
  77                dev_err(dev, "failed to set column address: %d\n", ret);
  78                return ret;
  79        }
  80
  81        ret = mipi_dsi_dcs_set_page_address(dsi, 0, jdi->mode->vdisplay - 1);
  82        if (ret < 0) {
  83                dev_err(dev, "failed to set page address: %d\n", ret);
  84                return ret;
  85        }
  86
  87        /*
  88         * BIT(5) BCTRL = 1 Backlight Control Block On, Brightness registers
  89         *                  are active
  90         * BIT(3) BL = 1    Backlight Control On
  91         * BIT(2) DD = 0    Display Dimming is Off
  92         */
  93        ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
  94                                 (u8[]){ 0x24 }, 1);
  95        if (ret < 0) {
  96                dev_err(dev, "failed to write control display: %d\n", ret);
  97                return ret;
  98        }
  99
 100        /* CABC off */
 101        ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
 102                                 (u8[]){ 0x00 }, 1);
 103        if (ret < 0) {
 104                dev_err(dev, "failed to set cabc off: %d\n", ret);
 105                return ret;
 106        }
 107
 108        ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
 109        if (ret < 0) {
 110                dev_err(dev, "failed to set exit sleep mode: %d\n", ret);
 111                return ret;
 112        }
 113
 114        msleep(120);
 115
 116        ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2);
 117        if (ret < 0) {
 118                dev_err(dev, "failed to set mcap: %d\n", ret);
 119                return ret;
 120        }
 121
 122        mdelay(10);
 123
 124        /* Interface setting, video mode */
 125        ret = mipi_dsi_generic_write(dsi, (u8[])
 126                                     {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6);
 127        if (ret < 0) {
 128                dev_err(dev, "failed to set display interface setting: %d\n"
 129                        , ret);
 130                return ret;
 131        }
 132
 133        mdelay(20);
 134
 135        ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2);
 136        if (ret < 0) {
 137                dev_err(dev, "failed to set default values for mcap: %d\n"
 138                        , ret);
 139                return ret;
 140        }
 141
 142        return 0;
 143}
 144
 145static int jdi_panel_on(struct jdi_panel *jdi)
 146{
 147        struct mipi_dsi_device *dsi = jdi->dsi;
 148        struct device *dev = &jdi->dsi->dev;
 149        int ret;
 150
 151        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
 152
 153        ret = mipi_dsi_dcs_set_display_on(dsi);
 154        if (ret < 0)
 155                dev_err(dev, "failed to set display on: %d\n", ret);
 156
 157        return ret;
 158}
 159
 160static void jdi_panel_off(struct jdi_panel *jdi)
 161{
 162        struct mipi_dsi_device *dsi = jdi->dsi;
 163        struct device *dev = &jdi->dsi->dev;
 164        int ret;
 165
 166        dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
 167
 168        ret = mipi_dsi_dcs_set_display_off(dsi);
 169        if (ret < 0)
 170                dev_err(dev, "failed to set display off: %d\n", ret);
 171
 172        ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
 173        if (ret < 0)
 174                dev_err(dev, "failed to enter sleep mode: %d\n", ret);
 175
 176        msleep(100);
 177}
 178
 179static int jdi_panel_disable(struct drm_panel *panel)
 180{
 181        struct jdi_panel *jdi = to_jdi_panel(panel);
 182
 183        if (!jdi->enabled)
 184                return 0;
 185
 186        backlight_disable(jdi->backlight);
 187
 188        jdi->enabled = false;
 189
 190        return 0;
 191}
 192
 193static int jdi_panel_unprepare(struct drm_panel *panel)
 194{
 195        struct jdi_panel *jdi = to_jdi_panel(panel);
 196        struct device *dev = &jdi->dsi->dev;
 197        int ret;
 198
 199        if (!jdi->prepared)
 200                return 0;
 201
 202        jdi_panel_off(jdi);
 203
 204        ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
 205        if (ret < 0)
 206                dev_err(dev, "regulator disable failed, %d\n", ret);
 207
 208        gpiod_set_value(jdi->enable_gpio, 0);
 209
 210        gpiod_set_value(jdi->reset_gpio, 1);
 211
 212        gpiod_set_value(jdi->dcdc_en_gpio, 0);
 213
 214        jdi->prepared = false;
 215
 216        return 0;
 217}
 218
 219static int jdi_panel_prepare(struct drm_panel *panel)
 220{
 221        struct jdi_panel *jdi = to_jdi_panel(panel);
 222        struct device *dev = &jdi->dsi->dev;
 223        int ret;
 224
 225        if (jdi->prepared)
 226                return 0;
 227
 228        ret = regulator_bulk_enable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
 229        if (ret < 0) {
 230                dev_err(dev, "regulator enable failed, %d\n", ret);
 231                return ret;
 232        }
 233
 234        msleep(20);
 235
 236        gpiod_set_value(jdi->dcdc_en_gpio, 1);
 237        usleep_range(10, 20);
 238
 239        gpiod_set_value(jdi->reset_gpio, 0);
 240        usleep_range(10, 20);
 241
 242        gpiod_set_value(jdi->enable_gpio, 1);
 243        usleep_range(10, 20);
 244
 245        ret = jdi_panel_init(jdi);
 246        if (ret < 0) {
 247                dev_err(dev, "failed to init panel: %d\n", ret);
 248                goto poweroff;
 249        }
 250
 251        ret = jdi_panel_on(jdi);
 252        if (ret < 0) {
 253                dev_err(dev, "failed to set panel on: %d\n", ret);
 254                goto poweroff;
 255        }
 256
 257        jdi->prepared = true;
 258
 259        return 0;
 260
 261poweroff:
 262        ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
 263        if (ret < 0)
 264                dev_err(dev, "regulator disable failed, %d\n", ret);
 265
 266        gpiod_set_value(jdi->enable_gpio, 0);
 267
 268        gpiod_set_value(jdi->reset_gpio, 1);
 269
 270        gpiod_set_value(jdi->dcdc_en_gpio, 0);
 271
 272        return ret;
 273}
 274
 275static int jdi_panel_enable(struct drm_panel *panel)
 276{
 277        struct jdi_panel *jdi = to_jdi_panel(panel);
 278
 279        if (jdi->enabled)
 280                return 0;
 281
 282        backlight_enable(jdi->backlight);
 283
 284        jdi->enabled = true;
 285
 286        return 0;
 287}
 288
 289static const struct drm_display_mode default_mode = {
 290                .clock = 155493,
 291                .hdisplay = 1200,
 292                .hsync_start = 1200 + 48,
 293                .hsync_end = 1200 + 48 + 32,
 294                .htotal = 1200 + 48 + 32 + 60,
 295                .vdisplay = 1920,
 296                .vsync_start = 1920 + 3,
 297                .vsync_end = 1920 + 3 + 5,
 298                .vtotal = 1920 + 3 + 5 + 6,
 299                .flags = 0,
 300};
 301
 302static int jdi_panel_get_modes(struct drm_panel *panel,
 303                               struct drm_connector *connector)
 304{
 305        struct drm_display_mode *mode;
 306        struct jdi_panel *jdi = to_jdi_panel(panel);
 307        struct device *dev = &jdi->dsi->dev;
 308
 309        mode = drm_mode_duplicate(connector->dev, &default_mode);
 310        if (!mode) {
 311                dev_err(dev, "failed to add mode %ux%ux@%u\n",
 312                        default_mode.hdisplay, default_mode.vdisplay,
 313                        drm_mode_vrefresh(&default_mode));
 314                return -ENOMEM;
 315        }
 316
 317        drm_mode_set_name(mode);
 318
 319        drm_mode_probed_add(connector, mode);
 320
 321        connector->display_info.width_mm = 95;
 322        connector->display_info.height_mm = 151;
 323
 324        return 1;
 325}
 326
 327static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
 328{
 329        struct mipi_dsi_device *dsi = bl_get_data(bl);
 330        int ret;
 331        u16 brightness = bl->props.brightness;
 332
 333        dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
 334
 335        ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
 336        if (ret < 0)
 337                return ret;
 338
 339        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
 340
 341        return brightness & 0xff;
 342}
 343
 344static int dsi_dcs_bl_update_status(struct backlight_device *bl)
 345{
 346        struct mipi_dsi_device *dsi = bl_get_data(bl);
 347        int ret;
 348
 349        dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
 350
 351        ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
 352        if (ret < 0)
 353                return ret;
 354
 355        dsi->mode_flags |= MIPI_DSI_MODE_LPM;
 356
 357        return 0;
 358}
 359
 360static const struct backlight_ops dsi_bl_ops = {
 361        .update_status = dsi_dcs_bl_update_status,
 362        .get_brightness = dsi_dcs_bl_get_brightness,
 363};
 364
 365static struct backlight_device *
 366drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
 367{
 368        struct device *dev = &dsi->dev;
 369        struct backlight_properties props;
 370
 371        memset(&props, 0, sizeof(props));
 372        props.type = BACKLIGHT_RAW;
 373        props.brightness = 255;
 374        props.max_brightness = 255;
 375
 376        return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
 377                                              &dsi_bl_ops, &props);
 378}
 379
 380static const struct drm_panel_funcs jdi_panel_funcs = {
 381        .disable = jdi_panel_disable,
 382        .unprepare = jdi_panel_unprepare,
 383        .prepare = jdi_panel_prepare,
 384        .enable = jdi_panel_enable,
 385        .get_modes = jdi_panel_get_modes,
 386};
 387
 388static const struct of_device_id jdi_of_match[] = {
 389        { .compatible = "jdi,lt070me05000", },
 390        { }
 391};
 392MODULE_DEVICE_TABLE(of, jdi_of_match);
 393
 394static int jdi_panel_add(struct jdi_panel *jdi)
 395{
 396        struct device *dev = &jdi->dsi->dev;
 397        int ret;
 398        unsigned int i;
 399
 400        jdi->mode = &default_mode;
 401
 402        for (i = 0; i < ARRAY_SIZE(jdi->supplies); i++)
 403                jdi->supplies[i].supply = regulator_names[i];
 404
 405        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(jdi->supplies),
 406                                      jdi->supplies);
 407        if (ret < 0) {
 408                dev_err(dev, "failed to init regulator, ret=%d\n", ret);
 409                return ret;
 410        }
 411
 412        jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
 413        if (IS_ERR(jdi->enable_gpio)) {
 414                ret = PTR_ERR(jdi->enable_gpio);
 415                dev_err(dev, "cannot get enable-gpio %d\n", ret);
 416                return ret;
 417        }
 418
 419        jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 420        if (IS_ERR(jdi->reset_gpio)) {
 421                ret = PTR_ERR(jdi->reset_gpio);
 422                dev_err(dev, "cannot get reset-gpios %d\n", ret);
 423                return ret;
 424        }
 425
 426        jdi->dcdc_en_gpio = devm_gpiod_get(dev, "dcdc-en", GPIOD_OUT_LOW);
 427        if (IS_ERR(jdi->dcdc_en_gpio)) {
 428                ret = PTR_ERR(jdi->dcdc_en_gpio);
 429                dev_err(dev, "cannot get dcdc-en-gpio %d\n", ret);
 430                return ret;
 431        }
 432
 433        jdi->backlight = drm_panel_create_dsi_backlight(jdi->dsi);
 434        if (IS_ERR(jdi->backlight)) {
 435                ret = PTR_ERR(jdi->backlight);
 436                dev_err(dev, "failed to register backlight %d\n", ret);
 437                return ret;
 438        }
 439
 440        drm_panel_init(&jdi->base, &jdi->dsi->dev, &jdi_panel_funcs,
 441                       DRM_MODE_CONNECTOR_DSI);
 442
 443        drm_panel_add(&jdi->base);
 444
 445        return 0;
 446}
 447
 448static void jdi_panel_del(struct jdi_panel *jdi)
 449{
 450        if (jdi->base.dev)
 451                drm_panel_remove(&jdi->base);
 452}
 453
 454static int jdi_panel_probe(struct mipi_dsi_device *dsi)
 455{
 456        struct jdi_panel *jdi;
 457        int ret;
 458
 459        dsi->lanes = 4;
 460        dsi->format = MIPI_DSI_FMT_RGB888;
 461        dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
 462                           MIPI_DSI_CLOCK_NON_CONTINUOUS;
 463
 464        jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
 465        if (!jdi)
 466                return -ENOMEM;
 467
 468        mipi_dsi_set_drvdata(dsi, jdi);
 469
 470        jdi->dsi = dsi;
 471
 472        ret = jdi_panel_add(jdi);
 473        if (ret < 0)
 474                return ret;
 475
 476        return mipi_dsi_attach(dsi);
 477}
 478
 479static int jdi_panel_remove(struct mipi_dsi_device *dsi)
 480{
 481        struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
 482        int ret;
 483
 484        ret = jdi_panel_disable(&jdi->base);
 485        if (ret < 0)
 486                dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
 487
 488        ret = mipi_dsi_detach(dsi);
 489        if (ret < 0)
 490                dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
 491                        ret);
 492
 493        jdi_panel_del(jdi);
 494
 495        return 0;
 496}
 497
 498static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
 499{
 500        struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
 501
 502        jdi_panel_disable(&jdi->base);
 503}
 504
 505static struct mipi_dsi_driver jdi_panel_driver = {
 506        .driver = {
 507                .name = "panel-jdi-lt070me05000",
 508                .of_match_table = jdi_of_match,
 509        },
 510        .probe = jdi_panel_probe,
 511        .remove = jdi_panel_remove,
 512        .shutdown = jdi_panel_shutdown,
 513};
 514module_mipi_dsi_driver(jdi_panel_driver);
 515
 516MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
 517MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
 518MODULE_DESCRIPTION("JDI LT070ME05000 WUXGA");
 519MODULE_LICENSE("GPL v2");
 520