linux/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Toppoly TD043MTEA1 Panel Driver
   4 *
   5 * Copyright (C) 2019 Texas Instruments Incorporated
   6 *
   7 * Based on the omapdrm-specific panel-tpo-td043mtea1 driver
   8 *
   9 * Author: Gražvydas Ignotas <notasas@gmail.com>
  10 */
  11
  12#include <linux/delay.h>
  13#include <linux/module.h>
  14#include <linux/regulator/consumer.h>
  15#include <linux/spi/spi.h>
  16
  17#include <drm/drm_connector.h>
  18#include <drm/drm_modes.h>
  19#include <drm/drm_panel.h>
  20
  21#define TPO_R02_MODE(x)                 ((x) & 7)
  22#define TPO_R02_MODE_800x480            7
  23#define TPO_R02_NCLK_RISING             BIT(3)
  24#define TPO_R02_HSYNC_HIGH              BIT(4)
  25#define TPO_R02_VSYNC_HIGH              BIT(5)
  26
  27#define TPO_R03_NSTANDBY                BIT(0)
  28#define TPO_R03_EN_CP_CLK               BIT(1)
  29#define TPO_R03_EN_VGL_PUMP             BIT(2)
  30#define TPO_R03_EN_PWM                  BIT(3)
  31#define TPO_R03_DRIVING_CAP_100         BIT(4)
  32#define TPO_R03_EN_PRE_CHARGE           BIT(6)
  33#define TPO_R03_SOFTWARE_CTL            BIT(7)
  34
  35#define TPO_R04_NFLIP_H                 BIT(0)
  36#define TPO_R04_NFLIP_V                 BIT(1)
  37#define TPO_R04_CP_CLK_FREQ_1H          BIT(2)
  38#define TPO_R04_VGL_FREQ_1H             BIT(4)
  39
  40#define TPO_R03_VAL_NORMAL \
  41        (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | TPO_R03_EN_VGL_PUMP | \
  42         TPO_R03_EN_PWM | TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
  43         TPO_R03_SOFTWARE_CTL)
  44
  45#define TPO_R03_VAL_STANDBY \
  46        (TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
  47         TPO_R03_SOFTWARE_CTL)
  48
  49static const u16 td043mtea1_def_gamma[12] = {
  50        105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
  51};
  52
  53struct td043mtea1_panel {
  54        struct drm_panel panel;
  55
  56        struct spi_device *spi;
  57        struct regulator *vcc_reg;
  58        struct gpio_desc *reset_gpio;
  59
  60        unsigned int mode;
  61        u16 gamma[12];
  62        bool vmirror;
  63        bool powered_on;
  64        bool spi_suspended;
  65        bool power_on_resume;
  66};
  67
  68#define to_td043mtea1_device(p) container_of(p, struct td043mtea1_panel, panel)
  69
  70/* -----------------------------------------------------------------------------
  71 * Hardware Access
  72 */
  73
  74static int td043mtea1_write(struct td043mtea1_panel *lcd, u8 addr, u8 value)
  75{
  76        struct spi_message msg;
  77        struct spi_transfer xfer;
  78        u16 data;
  79        int ret;
  80
  81        spi_message_init(&msg);
  82
  83        memset(&xfer, 0, sizeof(xfer));
  84
  85        data = ((u16)addr << 10) | (1 << 8) | value;
  86        xfer.tx_buf = &data;
  87        xfer.bits_per_word = 16;
  88        xfer.len = 2;
  89        spi_message_add_tail(&xfer, &msg);
  90
  91        ret = spi_sync(lcd->spi, &msg);
  92        if (ret < 0)
  93                dev_warn(&lcd->spi->dev, "failed to write to LCD reg (%d)\n",
  94                         ret);
  95
  96        return ret;
  97}
  98
  99static void td043mtea1_write_gamma(struct td043mtea1_panel *lcd)
 100{
 101        const u16 *gamma = lcd->gamma;
 102        unsigned int i;
 103        u8 val;
 104
 105        /* gamma bits [9:8] */
 106        for (val = i = 0; i < 4; i++)
 107                val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
 108        td043mtea1_write(lcd, 0x11, val);
 109
 110        for (val = i = 0; i < 4; i++)
 111                val |= (gamma[i + 4] & 0x300) >> ((i + 1) * 2);
 112        td043mtea1_write(lcd, 0x12, val);
 113
 114        for (val = i = 0; i < 4; i++)
 115                val |= (gamma[i + 8] & 0x300) >> ((i + 1) * 2);
 116        td043mtea1_write(lcd, 0x13, val);
 117
 118        /* gamma bits [7:0] */
 119        for (i = 0; i < 12; i++)
 120                td043mtea1_write(lcd, 0x14 + i, gamma[i] & 0xff);
 121}
 122
 123static int td043mtea1_write_mirror(struct td043mtea1_panel *lcd)
 124{
 125        u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
 126                TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
 127        if (lcd->vmirror)
 128                reg4 &= ~TPO_R04_NFLIP_V;
 129
 130        return td043mtea1_write(lcd, 4, reg4);
 131}
 132
 133static int td043mtea1_power_on(struct td043mtea1_panel *lcd)
 134{
 135        int ret;
 136
 137        if (lcd->powered_on)
 138                return 0;
 139
 140        ret = regulator_enable(lcd->vcc_reg);
 141        if (ret < 0)
 142                return ret;
 143
 144        /* Wait for the panel to stabilize. */
 145        msleep(160);
 146
 147        gpiod_set_value(lcd->reset_gpio, 0);
 148
 149        td043mtea1_write(lcd, 2, TPO_R02_MODE(lcd->mode) | TPO_R02_NCLK_RISING);
 150        td043mtea1_write(lcd, 3, TPO_R03_VAL_NORMAL);
 151        td043mtea1_write(lcd, 0x20, 0xf0);
 152        td043mtea1_write(lcd, 0x21, 0xf0);
 153        td043mtea1_write_mirror(lcd);
 154        td043mtea1_write_gamma(lcd);
 155
 156        lcd->powered_on = true;
 157
 158        return 0;
 159}
 160
 161static void td043mtea1_power_off(struct td043mtea1_panel *lcd)
 162{
 163        if (!lcd->powered_on)
 164                return;
 165
 166        td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
 167
 168        gpiod_set_value(lcd->reset_gpio, 1);
 169
 170        /* wait for at least 2 vsyncs before cutting off power */
 171        msleep(50);
 172
 173        td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY);
 174
 175        regulator_disable(lcd->vcc_reg);
 176
 177        lcd->powered_on = false;
 178}
 179
 180/* -----------------------------------------------------------------------------
 181 * sysfs
 182 */
 183
 184static ssize_t vmirror_show(struct device *dev, struct device_attribute *attr,
 185                            char *buf)
 186{
 187        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 188
 189        return sysfs_emit(buf, "%d\n", lcd->vmirror);
 190}
 191
 192static ssize_t vmirror_store(struct device *dev, struct device_attribute *attr,
 193                             const char *buf, size_t count)
 194{
 195        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 196        int val;
 197        int ret;
 198
 199        ret = kstrtoint(buf, 0, &val);
 200        if (ret < 0)
 201                return ret;
 202
 203        lcd->vmirror = !!val;
 204
 205        ret = td043mtea1_write_mirror(lcd);
 206        if (ret < 0)
 207                return ret;
 208
 209        return count;
 210}
 211
 212static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
 213                         char *buf)
 214{
 215        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 216
 217        return sysfs_emit(buf, "%d\n", lcd->mode);
 218}
 219
 220static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
 221                          const char *buf, size_t count)
 222{
 223        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 224        long val;
 225        int ret;
 226
 227        ret = kstrtol(buf, 0, &val);
 228        if (ret != 0 || val & ~7)
 229                return -EINVAL;
 230
 231        lcd->mode = val;
 232
 233        val |= TPO_R02_NCLK_RISING;
 234        td043mtea1_write(lcd, 2, val);
 235
 236        return count;
 237}
 238
 239static ssize_t gamma_show(struct device *dev, struct device_attribute *attr,
 240                          char *buf)
 241{
 242        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 243        ssize_t len = 0;
 244        unsigned int i;
 245        int ret;
 246
 247        for (i = 0; i < ARRAY_SIZE(lcd->gamma); i++) {
 248                ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
 249                               lcd->gamma[i]);
 250                if (ret < 0)
 251                        return ret;
 252                len += ret;
 253        }
 254        buf[len - 1] = '\n';
 255
 256        return len;
 257}
 258
 259static ssize_t gamma_store(struct device *dev, struct device_attribute *attr,
 260                           const char *buf, size_t count)
 261{
 262        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 263        unsigned int g[12];
 264        unsigned int i;
 265        int ret;
 266
 267        ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
 268                     &g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
 269                     &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
 270        if (ret != 12)
 271                return -EINVAL;
 272
 273        for (i = 0; i < 12; i++)
 274                lcd->gamma[i] = g[i];
 275
 276        td043mtea1_write_gamma(lcd);
 277
 278        return count;
 279}
 280
 281static DEVICE_ATTR_RW(vmirror);
 282static DEVICE_ATTR_RW(mode);
 283static DEVICE_ATTR_RW(gamma);
 284
 285static struct attribute *td043mtea1_attrs[] = {
 286        &dev_attr_vmirror.attr,
 287        &dev_attr_mode.attr,
 288        &dev_attr_gamma.attr,
 289        NULL,
 290};
 291
 292static const struct attribute_group td043mtea1_attr_group = {
 293        .attrs = td043mtea1_attrs,
 294};
 295
 296/* -----------------------------------------------------------------------------
 297 * Panel Operations
 298 */
 299
 300static int td043mtea1_unprepare(struct drm_panel *panel)
 301{
 302        struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
 303
 304        if (!lcd->spi_suspended)
 305                td043mtea1_power_off(lcd);
 306
 307        return 0;
 308}
 309
 310static int td043mtea1_prepare(struct drm_panel *panel)
 311{
 312        struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
 313        int ret;
 314
 315        /*
 316         * If we are resuming from system suspend, SPI might not be enabled
 317         * yet, so we'll program the LCD from SPI PM resume callback.
 318         */
 319        if (lcd->spi_suspended)
 320                return 0;
 321
 322        ret = td043mtea1_power_on(lcd);
 323        if (ret) {
 324                dev_err(&lcd->spi->dev, "%s: power on failed (%d)\n",
 325                        __func__, ret);
 326                return ret;
 327        }
 328
 329        return 0;
 330}
 331
 332static const struct drm_display_mode td043mtea1_mode = {
 333        .clock = 36000,
 334        .hdisplay = 800,
 335        .hsync_start = 800 + 68,
 336        .hsync_end = 800 + 68 + 1,
 337        .htotal = 800 + 68 + 1 + 214,
 338        .vdisplay = 480,
 339        .vsync_start = 480 + 39,
 340        .vsync_end = 480 + 39 + 1,
 341        .vtotal = 480 + 39 + 1 + 34,
 342        .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
 343        .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
 344        .width_mm = 94,
 345        .height_mm = 56,
 346};
 347
 348static int td043mtea1_get_modes(struct drm_panel *panel,
 349                                struct drm_connector *connector)
 350{
 351        struct drm_display_mode *mode;
 352
 353        mode = drm_mode_duplicate(connector->dev, &td043mtea1_mode);
 354        if (!mode)
 355                return -ENOMEM;
 356
 357        drm_mode_set_name(mode);
 358        drm_mode_probed_add(connector, mode);
 359
 360        connector->display_info.width_mm = td043mtea1_mode.width_mm;
 361        connector->display_info.height_mm = td043mtea1_mode.height_mm;
 362        /*
 363         * FIXME: According to the datasheet sync signals are sampled on the
 364         * rising edge of the clock, but the code running on the OMAP3 Pandora
 365         * indicates sampling on the falling edge. This should be tested on a
 366         * real device.
 367         */
 368        connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
 369                                          | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
 370                                          | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE;
 371
 372        return 1;
 373}
 374
 375static const struct drm_panel_funcs td043mtea1_funcs = {
 376        .unprepare = td043mtea1_unprepare,
 377        .prepare = td043mtea1_prepare,
 378        .get_modes = td043mtea1_get_modes,
 379};
 380
 381/* -----------------------------------------------------------------------------
 382 * Power Management, Probe and Remove
 383 */
 384
 385static int __maybe_unused td043mtea1_suspend(struct device *dev)
 386{
 387        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 388
 389        if (lcd->powered_on) {
 390                td043mtea1_power_off(lcd);
 391                lcd->powered_on = true;
 392        }
 393
 394        lcd->spi_suspended = true;
 395
 396        return 0;
 397}
 398
 399static int __maybe_unused td043mtea1_resume(struct device *dev)
 400{
 401        struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
 402        int ret;
 403
 404        lcd->spi_suspended = false;
 405
 406        if (lcd->powered_on) {
 407                lcd->powered_on = false;
 408                ret = td043mtea1_power_on(lcd);
 409                if (ret)
 410                        return ret;
 411        }
 412
 413        return 0;
 414}
 415
 416static SIMPLE_DEV_PM_OPS(td043mtea1_pm_ops, td043mtea1_suspend,
 417                         td043mtea1_resume);
 418
 419static int td043mtea1_probe(struct spi_device *spi)
 420{
 421        struct td043mtea1_panel *lcd;
 422        int ret;
 423
 424        lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
 425        if (lcd == NULL)
 426                return -ENOMEM;
 427
 428        spi_set_drvdata(spi, lcd);
 429        lcd->spi = spi;
 430        lcd->mode = TPO_R02_MODE_800x480;
 431        memcpy(lcd->gamma, td043mtea1_def_gamma, sizeof(lcd->gamma));
 432
 433        lcd->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
 434        if (IS_ERR(lcd->vcc_reg)) {
 435                dev_err(&spi->dev, "failed to get VCC regulator\n");
 436                return PTR_ERR(lcd->vcc_reg);
 437        }
 438
 439        lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
 440        if (IS_ERR(lcd->reset_gpio)) {
 441                dev_err(&spi->dev, "failed to get reset GPIO\n");
 442                return PTR_ERR(lcd->reset_gpio);
 443        }
 444
 445        spi->bits_per_word = 16;
 446        spi->mode = SPI_MODE_0;
 447
 448        ret = spi_setup(spi);
 449        if (ret < 0) {
 450                dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
 451                return ret;
 452        }
 453
 454        ret = sysfs_create_group(&spi->dev.kobj, &td043mtea1_attr_group);
 455        if (ret < 0) {
 456                dev_err(&spi->dev, "failed to create sysfs files\n");
 457                return ret;
 458        }
 459
 460        drm_panel_init(&lcd->panel, &lcd->spi->dev, &td043mtea1_funcs,
 461                       DRM_MODE_CONNECTOR_DPI);
 462
 463        drm_panel_add(&lcd->panel);
 464
 465        return 0;
 466}
 467
 468static int td043mtea1_remove(struct spi_device *spi)
 469{
 470        struct td043mtea1_panel *lcd = spi_get_drvdata(spi);
 471
 472        drm_panel_remove(&lcd->panel);
 473        drm_panel_disable(&lcd->panel);
 474        drm_panel_unprepare(&lcd->panel);
 475
 476        sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group);
 477
 478        return 0;
 479}
 480
 481static const struct of_device_id td043mtea1_of_match[] = {
 482        { .compatible = "tpo,td043mtea1", },
 483        { /* sentinel */ },
 484};
 485
 486MODULE_DEVICE_TABLE(of, td043mtea1_of_match);
 487
 488static const struct spi_device_id td043mtea1_ids[] = {
 489        { "td043mtea1", 0 },
 490        { /* sentinel */ }
 491};
 492
 493MODULE_DEVICE_TABLE(spi, td043mtea1_ids);
 494
 495static struct spi_driver td043mtea1_driver = {
 496        .probe          = td043mtea1_probe,
 497        .remove         = td043mtea1_remove,
 498        .id_table       = td043mtea1_ids,
 499        .driver         = {
 500                .name   = "panel-tpo-td043mtea1",
 501                .pm     = &td043mtea1_pm_ops,
 502                .of_match_table = td043mtea1_of_match,
 503        },
 504};
 505
 506module_spi_driver(td043mtea1_driver);
 507
 508MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
 509MODULE_DESCRIPTION("TPO TD043MTEA1 Panel Driver");
 510MODULE_LICENSE("GPL");
 511