linux/drivers/gpu/drm/panel/panel-dsi-cm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Generic DSI Command Mode panel driver
   4 *
   5 * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/
   6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
   7 */
   8
   9#include <linux/backlight.h>
  10#include <linux/delay.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/jiffies.h>
  13#include <linux/module.h>
  14#include <linux/of_device.h>
  15#include <linux/regulator/consumer.h>
  16
  17#include <drm/drm_connector.h>
  18#include <drm/drm_mipi_dsi.h>
  19#include <drm/drm_modes.h>
  20#include <drm/drm_panel.h>
  21
  22#include <video/mipi_display.h>
  23
  24#define DCS_GET_ID1             0xda
  25#define DCS_GET_ID2             0xdb
  26#define DCS_GET_ID3             0xdc
  27
  28#define DCS_REGULATOR_SUPPLY_NUM 2
  29
  30static const struct of_device_id dsicm_of_match[];
  31
  32struct dsic_panel_data {
  33        u32 xres;
  34        u32 yres;
  35        u32 refresh;
  36        u32 width_mm;
  37        u32 height_mm;
  38        u32 max_hs_rate;
  39        u32 max_lp_rate;
  40        bool te_support;
  41};
  42
  43struct panel_drv_data {
  44        struct mipi_dsi_device *dsi;
  45        struct drm_panel panel;
  46        struct drm_display_mode mode;
  47
  48        struct mutex lock;
  49
  50        struct backlight_device *bldev;
  51        struct backlight_device *extbldev;
  52
  53        unsigned long   hw_guard_end;   /* next value of jiffies when we can
  54                                         * issue the next sleep in/out command
  55                                         */
  56        unsigned long   hw_guard_wait;  /* max guard time in jiffies */
  57
  58        const struct dsic_panel_data *panel_data;
  59
  60        struct gpio_desc *reset_gpio;
  61
  62        struct regulator_bulk_data supplies[DCS_REGULATOR_SUPPLY_NUM];
  63
  64        bool use_dsi_backlight;
  65
  66        /* runtime variables */
  67        bool enabled;
  68
  69        bool intro_printed;
  70};
  71
  72static inline struct panel_drv_data *panel_to_ddata(struct drm_panel *panel)
  73{
  74        return container_of(panel, struct panel_drv_data, panel);
  75}
  76
  77static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)
  78{
  79        struct backlight_device *backlight;
  80
  81        if (ddata->bldev)
  82                backlight = ddata->bldev;
  83        else if (ddata->extbldev)
  84                backlight = ddata->extbldev;
  85        else
  86                return;
  87
  88        if (enable) {
  89                backlight->props.fb_blank = FB_BLANK_UNBLANK;
  90                backlight->props.state = ~(BL_CORE_FBBLANK | BL_CORE_SUSPENDED);
  91                backlight->props.power = FB_BLANK_UNBLANK;
  92        } else {
  93                backlight->props.fb_blank = FB_BLANK_NORMAL;
  94                backlight->props.power = FB_BLANK_POWERDOWN;
  95                backlight->props.state |= BL_CORE_FBBLANK | BL_CORE_SUSPENDED;
  96        }
  97
  98        backlight_update_status(backlight);
  99}
 100
 101static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
 102{
 103        ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
 104        ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
 105}
 106
 107static void hw_guard_wait(struct panel_drv_data *ddata)
 108{
 109        unsigned long wait = ddata->hw_guard_end - jiffies;
 110
 111        if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
 112                set_current_state(TASK_UNINTERRUPTIBLE);
 113                schedule_timeout(wait);
 114        }
 115}
 116
 117static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
 118{
 119        return mipi_dsi_dcs_read(ddata->dsi, dcs_cmd, data, 1);
 120}
 121
 122static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
 123{
 124        return mipi_dsi_dcs_write(ddata->dsi, dcs_cmd, &param, 1);
 125}
 126
 127static int dsicm_sleep_in(struct panel_drv_data *ddata)
 128
 129{
 130        int r;
 131
 132        hw_guard_wait(ddata);
 133
 134        r = mipi_dsi_dcs_enter_sleep_mode(ddata->dsi);
 135        if (r)
 136                return r;
 137
 138        hw_guard_start(ddata, 120);
 139
 140        usleep_range(5000, 10000);
 141
 142        return 0;
 143}
 144
 145static int dsicm_sleep_out(struct panel_drv_data *ddata)
 146{
 147        int r;
 148
 149        hw_guard_wait(ddata);
 150
 151        r = mipi_dsi_dcs_exit_sleep_mode(ddata->dsi);
 152        if (r)
 153                return r;
 154
 155        hw_guard_start(ddata, 120);
 156
 157        usleep_range(5000, 10000);
 158
 159        return 0;
 160}
 161
 162static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
 163{
 164        int r;
 165
 166        r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
 167        if (r)
 168                return r;
 169        r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
 170        if (r)
 171                return r;
 172        r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
 173        if (r)
 174                return r;
 175
 176        return 0;
 177}
 178
 179static int dsicm_set_update_window(struct panel_drv_data *ddata)
 180{
 181        struct mipi_dsi_device *dsi = ddata->dsi;
 182        int r;
 183
 184        r = mipi_dsi_dcs_set_column_address(dsi, 0, ddata->mode.hdisplay - 1);
 185        if (r < 0)
 186                return r;
 187
 188        r = mipi_dsi_dcs_set_page_address(dsi, 0, ddata->mode.vdisplay - 1);
 189        if (r < 0)
 190                return r;
 191
 192        return 0;
 193}
 194
 195static int dsicm_bl_update_status(struct backlight_device *dev)
 196{
 197        struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
 198        int r = 0;
 199        int level;
 200
 201        if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
 202                        dev->props.power == FB_BLANK_UNBLANK)
 203                level = dev->props.brightness;
 204        else
 205                level = 0;
 206
 207        dev_dbg(&ddata->dsi->dev, "update brightness to %d\n", level);
 208
 209        mutex_lock(&ddata->lock);
 210
 211        if (ddata->enabled)
 212                r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
 213                                      level);
 214
 215        mutex_unlock(&ddata->lock);
 216
 217        return r;
 218}
 219
 220static int dsicm_bl_get_intensity(struct backlight_device *dev)
 221{
 222        if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
 223                        dev->props.power == FB_BLANK_UNBLANK)
 224                return dev->props.brightness;
 225
 226        return 0;
 227}
 228
 229static const struct backlight_ops dsicm_bl_ops = {
 230        .get_brightness = dsicm_bl_get_intensity,
 231        .update_status  = dsicm_bl_update_status,
 232};
 233
 234static ssize_t num_dsi_errors_show(struct device *dev,
 235                struct device_attribute *attr, char *buf)
 236{
 237        struct panel_drv_data *ddata = dev_get_drvdata(dev);
 238        u8 errors = 0;
 239        int r = -ENODEV;
 240
 241        mutex_lock(&ddata->lock);
 242
 243        if (ddata->enabled)
 244                r = dsicm_dcs_read_1(ddata, MIPI_DCS_GET_ERROR_COUNT_ON_DSI, &errors);
 245
 246        mutex_unlock(&ddata->lock);
 247
 248        if (r)
 249                return r;
 250
 251        return snprintf(buf, PAGE_SIZE, "%d\n", errors);
 252}
 253
 254static ssize_t hw_revision_show(struct device *dev,
 255                struct device_attribute *attr, char *buf)
 256{
 257        struct panel_drv_data *ddata = dev_get_drvdata(dev);
 258        u8 id1, id2, id3;
 259        int r = -ENODEV;
 260
 261        mutex_lock(&ddata->lock);
 262
 263        if (ddata->enabled)
 264                r = dsicm_get_id(ddata, &id1, &id2, &id3);
 265
 266        mutex_unlock(&ddata->lock);
 267
 268        if (r)
 269                return r;
 270
 271        return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
 272}
 273
 274static DEVICE_ATTR_RO(num_dsi_errors);
 275static DEVICE_ATTR_RO(hw_revision);
 276
 277static struct attribute *dsicm_attrs[] = {
 278        &dev_attr_num_dsi_errors.attr,
 279        &dev_attr_hw_revision.attr,
 280        NULL,
 281};
 282
 283static const struct attribute_group dsicm_attr_group = {
 284        .attrs = dsicm_attrs,
 285};
 286
 287static void dsicm_hw_reset(struct panel_drv_data *ddata)
 288{
 289        gpiod_set_value(ddata->reset_gpio, 1);
 290        udelay(10);
 291        /* reset the panel */
 292        gpiod_set_value(ddata->reset_gpio, 0);
 293        /* assert reset */
 294        udelay(10);
 295        gpiod_set_value(ddata->reset_gpio, 1);
 296        /* wait after releasing reset */
 297        usleep_range(5000, 10000);
 298}
 299
 300static int dsicm_power_on(struct panel_drv_data *ddata)
 301{
 302        u8 id1, id2, id3;
 303        int r;
 304
 305        dsicm_hw_reset(ddata);
 306
 307        ddata->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
 308
 309        r = dsicm_sleep_out(ddata);
 310        if (r)
 311                goto err;
 312
 313        r = dsicm_get_id(ddata, &id1, &id2, &id3);
 314        if (r)
 315                goto err;
 316
 317        r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
 318        if (r)
 319                goto err;
 320
 321        r = dsicm_dcs_write_1(ddata, MIPI_DCS_WRITE_CONTROL_DISPLAY,
 322                        (1<<2) | (1<<5));       /* BL | BCTRL */
 323        if (r)
 324                goto err;
 325
 326        r = mipi_dsi_dcs_set_pixel_format(ddata->dsi, MIPI_DCS_PIXEL_FMT_24BIT);
 327        if (r)
 328                goto err;
 329
 330        r = dsicm_set_update_window(ddata);
 331        if (r)
 332                goto err;
 333
 334        r = mipi_dsi_dcs_set_display_on(ddata->dsi);
 335        if (r)
 336                goto err;
 337
 338        if (ddata->panel_data->te_support) {
 339                r = mipi_dsi_dcs_set_tear_on(ddata->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
 340                if (r)
 341                        goto err;
 342        }
 343
 344        /* possible panel bug */
 345        msleep(100);
 346
 347        ddata->enabled = true;
 348
 349        if (!ddata->intro_printed) {
 350                dev_info(&ddata->dsi->dev, "panel revision %02x.%02x.%02x\n",
 351                        id1, id2, id3);
 352                ddata->intro_printed = true;
 353        }
 354
 355        ddata->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
 356
 357        return 0;
 358err:
 359        dev_err(&ddata->dsi->dev, "error while enabling panel, issuing HW reset\n");
 360
 361        dsicm_hw_reset(ddata);
 362
 363        return r;
 364}
 365
 366static int dsicm_power_off(struct panel_drv_data *ddata)
 367{
 368        int r;
 369
 370        ddata->enabled = false;
 371
 372        r = mipi_dsi_dcs_set_display_off(ddata->dsi);
 373        if (!r)
 374                r = dsicm_sleep_in(ddata);
 375
 376        if (r) {
 377                dev_err(&ddata->dsi->dev,
 378                                "error disabling panel, issuing HW reset\n");
 379                dsicm_hw_reset(ddata);
 380        }
 381
 382        return r;
 383}
 384
 385static int dsicm_prepare(struct drm_panel *panel)
 386{
 387        struct panel_drv_data *ddata = panel_to_ddata(panel);
 388        int r;
 389
 390        r = regulator_bulk_enable(ARRAY_SIZE(ddata->supplies), ddata->supplies);
 391        if (r)
 392                dev_err(&ddata->dsi->dev, "failed to enable supplies: %d\n", r);
 393
 394        return r;
 395}
 396
 397static int dsicm_enable(struct drm_panel *panel)
 398{
 399        struct panel_drv_data *ddata = panel_to_ddata(panel);
 400        int r;
 401
 402        mutex_lock(&ddata->lock);
 403
 404        r = dsicm_power_on(ddata);
 405        if (r)
 406                goto err;
 407
 408        mutex_unlock(&ddata->lock);
 409
 410        dsicm_bl_power(ddata, true);
 411
 412        return 0;
 413err:
 414        dev_err(&ddata->dsi->dev, "enable failed (%d)\n", r);
 415        mutex_unlock(&ddata->lock);
 416        return r;
 417}
 418
 419static int dsicm_unprepare(struct drm_panel *panel)
 420{
 421        struct panel_drv_data *ddata = panel_to_ddata(panel);
 422        int r;
 423
 424        r = regulator_bulk_disable(ARRAY_SIZE(ddata->supplies), ddata->supplies);
 425        if (r)
 426                dev_err(&ddata->dsi->dev, "failed to disable supplies: %d\n", r);
 427
 428        return r;
 429}
 430
 431static int dsicm_disable(struct drm_panel *panel)
 432{
 433        struct panel_drv_data *ddata = panel_to_ddata(panel);
 434        int r;
 435
 436        dsicm_bl_power(ddata, false);
 437
 438        mutex_lock(&ddata->lock);
 439
 440        r = dsicm_power_off(ddata);
 441
 442        mutex_unlock(&ddata->lock);
 443
 444        return r;
 445}
 446
 447static int dsicm_get_modes(struct drm_panel *panel,
 448                           struct drm_connector *connector)
 449{
 450        struct panel_drv_data *ddata = panel_to_ddata(panel);
 451        struct drm_display_mode *mode;
 452
 453        mode = drm_mode_duplicate(connector->dev, &ddata->mode);
 454        if (!mode) {
 455                dev_err(&ddata->dsi->dev, "failed to add mode %ux%ux@%u kHz\n",
 456                        ddata->mode.hdisplay, ddata->mode.vdisplay,
 457                        ddata->mode.clock);
 458                return -ENOMEM;
 459        }
 460
 461        connector->display_info.width_mm = ddata->panel_data->width_mm;
 462        connector->display_info.height_mm = ddata->panel_data->height_mm;
 463
 464        drm_mode_probed_add(connector, mode);
 465
 466        return 1;
 467}
 468
 469static const struct drm_panel_funcs dsicm_panel_funcs = {
 470        .unprepare = dsicm_unprepare,
 471        .disable = dsicm_disable,
 472        .prepare = dsicm_prepare,
 473        .enable = dsicm_enable,
 474        .get_modes = dsicm_get_modes,
 475};
 476
 477static int dsicm_probe_of(struct mipi_dsi_device *dsi)
 478{
 479        struct backlight_device *backlight;
 480        struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);
 481        int err;
 482        struct drm_display_mode *mode = &ddata->mode;
 483
 484        ddata->reset_gpio = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
 485        if (IS_ERR(ddata->reset_gpio)) {
 486                err = PTR_ERR(ddata->reset_gpio);
 487                dev_err(&dsi->dev, "reset gpio request failed: %d", err);
 488                return err;
 489        }
 490
 491        mode->hdisplay = mode->hsync_start = mode->hsync_end = mode->htotal =
 492                ddata->panel_data->xres;
 493        mode->vdisplay = mode->vsync_start = mode->vsync_end = mode->vtotal =
 494                ddata->panel_data->yres;
 495        mode->clock = ddata->panel_data->xres * ddata->panel_data->yres *
 496                ddata->panel_data->refresh / 1000;
 497        mode->width_mm = ddata->panel_data->width_mm;
 498        mode->height_mm = ddata->panel_data->height_mm;
 499        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 500        drm_mode_set_name(mode);
 501
 502        ddata->supplies[0].supply = "vpnl";
 503        ddata->supplies[1].supply = "vddi";
 504        err = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ddata->supplies),
 505                                      ddata->supplies);
 506        if (err)
 507                return err;
 508
 509        backlight = devm_of_find_backlight(&dsi->dev);
 510        if (IS_ERR(backlight))
 511                return PTR_ERR(backlight);
 512
 513        /* If no backlight device is found assume native backlight support */
 514        if (backlight)
 515                ddata->extbldev = backlight;
 516        else
 517                ddata->use_dsi_backlight = true;
 518
 519        return 0;
 520}
 521
 522static int dsicm_probe(struct mipi_dsi_device *dsi)
 523{
 524        struct panel_drv_data *ddata;
 525        struct backlight_device *bldev = NULL;
 526        struct device *dev = &dsi->dev;
 527        int r;
 528
 529        dev_dbg(dev, "probe\n");
 530
 531        ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
 532        if (!ddata)
 533                return -ENOMEM;
 534
 535        mipi_dsi_set_drvdata(dsi, ddata);
 536        ddata->dsi = dsi;
 537
 538        ddata->panel_data = of_device_get_match_data(dev);
 539        if (!ddata->panel_data)
 540                return -ENODEV;
 541
 542        r = dsicm_probe_of(dsi);
 543        if (r)
 544                return r;
 545
 546        mutex_init(&ddata->lock);
 547
 548        dsicm_hw_reset(ddata);
 549
 550        drm_panel_init(&ddata->panel, dev, &dsicm_panel_funcs,
 551                       DRM_MODE_CONNECTOR_DSI);
 552
 553        if (ddata->use_dsi_backlight) {
 554                struct backlight_properties props = { 0 };
 555                props.max_brightness = 255;
 556                props.type = BACKLIGHT_RAW;
 557
 558                bldev = devm_backlight_device_register(dev, dev_name(dev),
 559                        dev, ddata, &dsicm_bl_ops, &props);
 560                if (IS_ERR(bldev)) {
 561                        r = PTR_ERR(bldev);
 562                        goto err_bl;
 563                }
 564
 565                ddata->bldev = bldev;
 566        }
 567
 568        r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
 569        if (r) {
 570                dev_err(dev, "failed to create sysfs files\n");
 571                goto err_bl;
 572        }
 573
 574        dsi->lanes = 2;
 575        dsi->format = MIPI_DSI_FMT_RGB888;
 576        dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |
 577                          MIPI_DSI_MODE_EOT_PACKET;
 578        dsi->hs_rate = ddata->panel_data->max_hs_rate;
 579        dsi->lp_rate = ddata->panel_data->max_lp_rate;
 580
 581        drm_panel_add(&ddata->panel);
 582
 583        r = mipi_dsi_attach(dsi);
 584        if (r < 0)
 585                goto err_dsi_attach;
 586
 587        return 0;
 588
 589err_dsi_attach:
 590        drm_panel_remove(&ddata->panel);
 591        sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);
 592err_bl:
 593        if (ddata->extbldev)
 594                put_device(&ddata->extbldev->dev);
 595
 596        return r;
 597}
 598
 599static int dsicm_remove(struct mipi_dsi_device *dsi)
 600{
 601        struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);
 602
 603        dev_dbg(&dsi->dev, "remove\n");
 604
 605        mipi_dsi_detach(dsi);
 606
 607        drm_panel_remove(&ddata->panel);
 608
 609        sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);
 610
 611        if (ddata->extbldev)
 612                put_device(&ddata->extbldev->dev);
 613
 614        return 0;
 615}
 616
 617static const struct dsic_panel_data taal_data = {
 618        .xres = 864,
 619        .yres = 480,
 620        .refresh = 60,
 621        .width_mm = 0,
 622        .height_mm = 0,
 623        .max_hs_rate = 300000000,
 624        .max_lp_rate = 10000000,
 625        .te_support = true,
 626};
 627
 628static const struct dsic_panel_data himalaya_data = {
 629        .xres = 480,
 630        .yres = 864,
 631        .refresh = 60,
 632        .width_mm = 49,
 633        .height_mm = 88,
 634        .max_hs_rate = 300000000,
 635        .max_lp_rate = 10000000,
 636        .te_support = false,
 637};
 638
 639static const struct dsic_panel_data droid4_data = {
 640        .xres = 540,
 641        .yres = 960,
 642        .refresh = 60,
 643        .width_mm = 50,
 644        .height_mm = 89,
 645        .max_hs_rate = 300000000,
 646        .max_lp_rate = 10000000,
 647        .te_support = false,
 648};
 649
 650static const struct of_device_id dsicm_of_match[] = {
 651        { .compatible = "tpo,taal", .data = &taal_data },
 652        { .compatible = "nokia,himalaya", &himalaya_data },
 653        { .compatible = "motorola,droid4-panel", &droid4_data },
 654        {},
 655};
 656
 657MODULE_DEVICE_TABLE(of, dsicm_of_match);
 658
 659static struct mipi_dsi_driver dsicm_driver = {
 660        .probe = dsicm_probe,
 661        .remove = dsicm_remove,
 662        .driver = {
 663                .name = "panel-dsi-cm",
 664                .of_match_table = dsicm_of_match,
 665        },
 666};
 667module_mipi_dsi_driver(dsicm_driver);
 668
 669MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 670MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
 671MODULE_LICENSE("GPL");
 672