linux/drivers/video/backlight/lms501kf03.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * lms501kf03 TFT LCD panel driver.
   4 *
   5 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
   6 * Author: Jingoo Han  <jg1.han@samsung.com>
   7 */
   8
   9#include <linux/backlight.h>
  10#include <linux/delay.h>
  11#include <linux/fb.h>
  12#include <linux/lcd.h>
  13#include <linux/module.h>
  14#include <linux/spi/spi.h>
  15#include <linux/wait.h>
  16
  17#define COMMAND_ONLY            0x00
  18#define DATA_ONLY               0x01
  19
  20struct lms501kf03 {
  21        struct device                   *dev;
  22        struct spi_device               *spi;
  23        unsigned int                    power;
  24        struct lcd_device               *ld;
  25        struct lcd_platform_data        *lcd_pd;
  26};
  27
  28static const unsigned char seq_password[] = {
  29        0xb9, 0xff, 0x83, 0x69,
  30};
  31
  32static const unsigned char seq_power[] = {
  33        0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
  34        0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
  35};
  36
  37static const unsigned char seq_display[] = {
  38        0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
  39        0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
  40};
  41
  42static const unsigned char seq_rgb_if[] = {
  43        0xb3, 0x09,
  44};
  45
  46static const unsigned char seq_display_inv[] = {
  47        0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06,
  48};
  49
  50static const unsigned char seq_vcom[] = {
  51        0xb6, 0x4c, 0x2e,
  52};
  53
  54static const unsigned char seq_gate[] = {
  55        0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13,
  56        0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75,
  57        0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07,
  58};
  59
  60static const unsigned char seq_panel[] = {
  61        0xcc, 0x02,
  62};
  63
  64static const unsigned char seq_col_mod[] = {
  65        0x3a, 0x77,
  66};
  67
  68static const unsigned char seq_w_gamma[] = {
  69        0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a,
  70        0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04,
  71        0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16,
  72        0x18, 0x16, 0x17, 0x0d, 0x15,
  73};
  74
  75static const unsigned char seq_rgb_gamma[] = {
  76        0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c,
  77        0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92,
  78        0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde,
  79        0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb,
  80        0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a,
  81        0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b,
  82        0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca,
  83        0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe,
  84        0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17,
  85        0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63,
  86        0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0,
  87        0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00,
  88        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  89};
  90
  91static const unsigned char seq_sleep_out[] = {
  92        0x11,
  93};
  94
  95static const unsigned char seq_display_on[] = {
  96        0x29,
  97};
  98
  99static const unsigned char seq_display_off[] = {
 100        0x10,
 101};
 102
 103static int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data)
 104{
 105        u16 buf[1];
 106        struct spi_message msg;
 107
 108        struct spi_transfer xfer = {
 109                .len            = 2,
 110                .tx_buf         = buf,
 111        };
 112
 113        buf[0] = (addr << 8) | data;
 114
 115        spi_message_init(&msg);
 116        spi_message_add_tail(&xfer, &msg);
 117
 118        return spi_sync(lcd->spi, &msg);
 119}
 120
 121static int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address,
 122                                unsigned char command)
 123{
 124        return lms501kf03_spi_write_byte(lcd, address, command);
 125}
 126
 127static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd,
 128                                        const unsigned char *wbuf,
 129                                        unsigned int len)
 130{
 131        int ret = 0, i = 0;
 132
 133        while (i < len) {
 134                if (i == 0)
 135                        ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]);
 136                else
 137                        ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]);
 138                if (ret)
 139                        break;
 140                i += 1;
 141        }
 142
 143        return ret;
 144}
 145
 146static int lms501kf03_ldi_init(struct lms501kf03 *lcd)
 147{
 148        int ret, i;
 149        static const unsigned char *init_seq[] = {
 150                seq_password,
 151                seq_power,
 152                seq_display,
 153                seq_rgb_if,
 154                seq_display_inv,
 155                seq_vcom,
 156                seq_gate,
 157                seq_panel,
 158                seq_col_mod,
 159                seq_w_gamma,
 160                seq_rgb_gamma,
 161                seq_sleep_out,
 162        };
 163
 164        static const unsigned int size_seq[] = {
 165                ARRAY_SIZE(seq_password),
 166                ARRAY_SIZE(seq_power),
 167                ARRAY_SIZE(seq_display),
 168                ARRAY_SIZE(seq_rgb_if),
 169                ARRAY_SIZE(seq_display_inv),
 170                ARRAY_SIZE(seq_vcom),
 171                ARRAY_SIZE(seq_gate),
 172                ARRAY_SIZE(seq_panel),
 173                ARRAY_SIZE(seq_col_mod),
 174                ARRAY_SIZE(seq_w_gamma),
 175                ARRAY_SIZE(seq_rgb_gamma),
 176                ARRAY_SIZE(seq_sleep_out),
 177        };
 178
 179        for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
 180                ret = lms501kf03_panel_send_sequence(lcd, init_seq[i],
 181                                                size_seq[i]);
 182                if (ret)
 183                        break;
 184        }
 185        /*
 186         * According to the datasheet, 120ms delay time is required.
 187         * After sleep out sequence, command is blocked for 120ms.
 188         * Thus, LDI should wait for 120ms.
 189         */
 190        msleep(120);
 191
 192        return ret;
 193}
 194
 195static int lms501kf03_ldi_enable(struct lms501kf03 *lcd)
 196{
 197        return lms501kf03_panel_send_sequence(lcd, seq_display_on,
 198                                        ARRAY_SIZE(seq_display_on));
 199}
 200
 201static int lms501kf03_ldi_disable(struct lms501kf03 *lcd)
 202{
 203        return lms501kf03_panel_send_sequence(lcd, seq_display_off,
 204                                        ARRAY_SIZE(seq_display_off));
 205}
 206
 207static int lms501kf03_power_is_on(int power)
 208{
 209        return (power) <= FB_BLANK_NORMAL;
 210}
 211
 212static int lms501kf03_power_on(struct lms501kf03 *lcd)
 213{
 214        int ret = 0;
 215        struct lcd_platform_data *pd;
 216
 217        pd = lcd->lcd_pd;
 218
 219        if (!pd->power_on) {
 220                dev_err(lcd->dev, "power_on is NULL.\n");
 221                return -EINVAL;
 222        }
 223
 224        pd->power_on(lcd->ld, 1);
 225        msleep(pd->power_on_delay);
 226
 227        if (!pd->reset) {
 228                dev_err(lcd->dev, "reset is NULL.\n");
 229                return -EINVAL;
 230        }
 231
 232        pd->reset(lcd->ld);
 233        msleep(pd->reset_delay);
 234
 235        ret = lms501kf03_ldi_init(lcd);
 236        if (ret) {
 237                dev_err(lcd->dev, "failed to initialize ldi.\n");
 238                return ret;
 239        }
 240
 241        ret = lms501kf03_ldi_enable(lcd);
 242        if (ret) {
 243                dev_err(lcd->dev, "failed to enable ldi.\n");
 244                return ret;
 245        }
 246
 247        return 0;
 248}
 249
 250static int lms501kf03_power_off(struct lms501kf03 *lcd)
 251{
 252        int ret = 0;
 253        struct lcd_platform_data *pd;
 254
 255        pd = lcd->lcd_pd;
 256
 257        ret = lms501kf03_ldi_disable(lcd);
 258        if (ret) {
 259                dev_err(lcd->dev, "lcd setting failed.\n");
 260                return -EIO;
 261        }
 262
 263        msleep(pd->power_off_delay);
 264
 265        pd->power_on(lcd->ld, 0);
 266
 267        return 0;
 268}
 269
 270static int lms501kf03_power(struct lms501kf03 *lcd, int power)
 271{
 272        int ret = 0;
 273
 274        if (lms501kf03_power_is_on(power) &&
 275                !lms501kf03_power_is_on(lcd->power))
 276                ret = lms501kf03_power_on(lcd);
 277        else if (!lms501kf03_power_is_on(power) &&
 278                lms501kf03_power_is_on(lcd->power))
 279                ret = lms501kf03_power_off(lcd);
 280
 281        if (!ret)
 282                lcd->power = power;
 283
 284        return ret;
 285}
 286
 287static int lms501kf03_get_power(struct lcd_device *ld)
 288{
 289        struct lms501kf03 *lcd = lcd_get_data(ld);
 290
 291        return lcd->power;
 292}
 293
 294static int lms501kf03_set_power(struct lcd_device *ld, int power)
 295{
 296        struct lms501kf03 *lcd = lcd_get_data(ld);
 297
 298        if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
 299                power != FB_BLANK_NORMAL) {
 300                dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
 301                return -EINVAL;
 302        }
 303
 304        return lms501kf03_power(lcd, power);
 305}
 306
 307static struct lcd_ops lms501kf03_lcd_ops = {
 308        .get_power = lms501kf03_get_power,
 309        .set_power = lms501kf03_set_power,
 310};
 311
 312static int lms501kf03_probe(struct spi_device *spi)
 313{
 314        struct lms501kf03 *lcd = NULL;
 315        struct lcd_device *ld = NULL;
 316        int ret = 0;
 317
 318        lcd = devm_kzalloc(&spi->dev, sizeof(struct lms501kf03), GFP_KERNEL);
 319        if (!lcd)
 320                return -ENOMEM;
 321
 322        /* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */
 323        spi->bits_per_word = 9;
 324
 325        ret = spi_setup(spi);
 326        if (ret < 0) {
 327                dev_err(&spi->dev, "spi setup failed.\n");
 328                return ret;
 329        }
 330
 331        lcd->spi = spi;
 332        lcd->dev = &spi->dev;
 333
 334        lcd->lcd_pd = dev_get_platdata(&spi->dev);
 335        if (!lcd->lcd_pd) {
 336                dev_err(&spi->dev, "platform data is NULL\n");
 337                return -EINVAL;
 338        }
 339
 340        ld = devm_lcd_device_register(&spi->dev, "lms501kf03", &spi->dev, lcd,
 341                                        &lms501kf03_lcd_ops);
 342        if (IS_ERR(ld))
 343                return PTR_ERR(ld);
 344
 345        lcd->ld = ld;
 346
 347        if (!lcd->lcd_pd->lcd_enabled) {
 348                /*
 349                 * if lcd panel was off from bootloader then
 350                 * current lcd status is powerdown and then
 351                 * it enables lcd panel.
 352                 */
 353                lcd->power = FB_BLANK_POWERDOWN;
 354
 355                lms501kf03_power(lcd, FB_BLANK_UNBLANK);
 356        } else {
 357                lcd->power = FB_BLANK_UNBLANK;
 358        }
 359
 360        spi_set_drvdata(spi, lcd);
 361
 362        dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n");
 363
 364        return 0;
 365}
 366
 367static int lms501kf03_remove(struct spi_device *spi)
 368{
 369        struct lms501kf03 *lcd = spi_get_drvdata(spi);
 370
 371        lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
 372        return 0;
 373}
 374
 375#ifdef CONFIG_PM_SLEEP
 376static int lms501kf03_suspend(struct device *dev)
 377{
 378        struct lms501kf03 *lcd = dev_get_drvdata(dev);
 379
 380        dev_dbg(dev, "lcd->power = %d\n", lcd->power);
 381
 382        /*
 383         * when lcd panel is suspend, lcd panel becomes off
 384         * regardless of status.
 385         */
 386        return lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
 387}
 388
 389static int lms501kf03_resume(struct device *dev)
 390{
 391        struct lms501kf03 *lcd = dev_get_drvdata(dev);
 392
 393        lcd->power = FB_BLANK_POWERDOWN;
 394
 395        return lms501kf03_power(lcd, FB_BLANK_UNBLANK);
 396}
 397#endif
 398
 399static SIMPLE_DEV_PM_OPS(lms501kf03_pm_ops, lms501kf03_suspend,
 400                        lms501kf03_resume);
 401
 402static void lms501kf03_shutdown(struct spi_device *spi)
 403{
 404        struct lms501kf03 *lcd = spi_get_drvdata(spi);
 405
 406        lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
 407}
 408
 409static struct spi_driver lms501kf03_driver = {
 410        .driver = {
 411                .name   = "lms501kf03",
 412                .pm     = &lms501kf03_pm_ops,
 413        },
 414        .probe          = lms501kf03_probe,
 415        .remove         = lms501kf03_remove,
 416        .shutdown       = lms501kf03_shutdown,
 417};
 418
 419module_spi_driver(lms501kf03_driver);
 420
 421MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
 422MODULE_DESCRIPTION("lms501kf03 LCD Driver");
 423MODULE_LICENSE("GPL");
 424