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