linux/drivers/video/backlight/s6e63m0.c
<<
>>
Prefs
   1/*
   2 * S6E63M0 AMOLED LCD panel driver.
   3 *
   4 * Author: InKi Dae  <inki.dae@samsung.com>
   5 *
   6 * Derived from drivers/video/omap/lcd-apollon.c
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License as published by the
  10 * Free Software Foundation; either version 2 of the License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/backlight.h>
  15#include <linux/delay.h>
  16#include <linux/fb.h>
  17#include <linux/gpio.h>
  18#include <linux/interrupt.h>
  19#include <linux/irq.h>
  20#include <linux/kernel.h>
  21#include <linux/lcd.h>
  22#include <linux/module.h>
  23#include <linux/spi/spi.h>
  24#include <linux/wait.h>
  25
  26#include "s6e63m0_gamma.h"
  27
  28#define SLEEPMSEC               0x1000
  29#define ENDDEF                  0x2000
  30#define DEFMASK                 0xFF00
  31#define COMMAND_ONLY            0xFE
  32#define DATA_ONLY               0xFF
  33
  34#define MIN_BRIGHTNESS          0
  35#define MAX_BRIGHTNESS          10
  36
  37struct s6e63m0 {
  38        struct device                   *dev;
  39        struct spi_device               *spi;
  40        unsigned int                    power;
  41        unsigned int                    current_brightness;
  42        unsigned int                    gamma_mode;
  43        unsigned int                    gamma_table_count;
  44        struct lcd_device               *ld;
  45        struct backlight_device         *bd;
  46        struct lcd_platform_data        *lcd_pd;
  47};
  48
  49static const unsigned short seq_panel_condition_set[] = {
  50        0xF8, 0x01,
  51        DATA_ONLY, 0x27,
  52        DATA_ONLY, 0x27,
  53        DATA_ONLY, 0x07,
  54        DATA_ONLY, 0x07,
  55        DATA_ONLY, 0x54,
  56        DATA_ONLY, 0x9f,
  57        DATA_ONLY, 0x63,
  58        DATA_ONLY, 0x86,
  59        DATA_ONLY, 0x1a,
  60        DATA_ONLY, 0x33,
  61        DATA_ONLY, 0x0d,
  62        DATA_ONLY, 0x00,
  63        DATA_ONLY, 0x00,
  64
  65        ENDDEF, 0x0000
  66};
  67
  68static const unsigned short seq_display_condition_set[] = {
  69        0xf2, 0x02,
  70        DATA_ONLY, 0x03,
  71        DATA_ONLY, 0x1c,
  72        DATA_ONLY, 0x10,
  73        DATA_ONLY, 0x10,
  74
  75        0xf7, 0x03,
  76        DATA_ONLY, 0x00,
  77        DATA_ONLY, 0x00,
  78
  79        ENDDEF, 0x0000
  80};
  81
  82static const unsigned short seq_gamma_setting[] = {
  83        0xfa, 0x00,
  84        DATA_ONLY, 0x18,
  85        DATA_ONLY, 0x08,
  86        DATA_ONLY, 0x24,
  87        DATA_ONLY, 0x64,
  88        DATA_ONLY, 0x56,
  89        DATA_ONLY, 0x33,
  90        DATA_ONLY, 0xb6,
  91        DATA_ONLY, 0xba,
  92        DATA_ONLY, 0xa8,
  93        DATA_ONLY, 0xac,
  94        DATA_ONLY, 0xb1,
  95        DATA_ONLY, 0x9d,
  96        DATA_ONLY, 0xc1,
  97        DATA_ONLY, 0xc1,
  98        DATA_ONLY, 0xb7,
  99        DATA_ONLY, 0x00,
 100        DATA_ONLY, 0x9c,
 101        DATA_ONLY, 0x00,
 102        DATA_ONLY, 0x9f,
 103        DATA_ONLY, 0x00,
 104        DATA_ONLY, 0xd6,
 105
 106        0xfa, 0x01,
 107
 108        ENDDEF, 0x0000
 109};
 110
 111static const unsigned short seq_etc_condition_set[] = {
 112        0xf6, 0x00,
 113        DATA_ONLY, 0x8c,
 114        DATA_ONLY, 0x07,
 115
 116        0xb3, 0xc,
 117
 118        0xb5, 0x2c,
 119        DATA_ONLY, 0x12,
 120        DATA_ONLY, 0x0c,
 121        DATA_ONLY, 0x0a,
 122        DATA_ONLY, 0x10,
 123        DATA_ONLY, 0x0e,
 124        DATA_ONLY, 0x17,
 125        DATA_ONLY, 0x13,
 126        DATA_ONLY, 0x1f,
 127        DATA_ONLY, 0x1a,
 128        DATA_ONLY, 0x2a,
 129        DATA_ONLY, 0x24,
 130        DATA_ONLY, 0x1f,
 131        DATA_ONLY, 0x1b,
 132        DATA_ONLY, 0x1a,
 133        DATA_ONLY, 0x17,
 134
 135        DATA_ONLY, 0x2b,
 136        DATA_ONLY, 0x26,
 137        DATA_ONLY, 0x22,
 138        DATA_ONLY, 0x20,
 139        DATA_ONLY, 0x3a,
 140        DATA_ONLY, 0x34,
 141        DATA_ONLY, 0x30,
 142        DATA_ONLY, 0x2c,
 143        DATA_ONLY, 0x29,
 144        DATA_ONLY, 0x26,
 145        DATA_ONLY, 0x25,
 146        DATA_ONLY, 0x23,
 147        DATA_ONLY, 0x21,
 148        DATA_ONLY, 0x20,
 149        DATA_ONLY, 0x1e,
 150        DATA_ONLY, 0x1e,
 151
 152        0xb6, 0x00,
 153        DATA_ONLY, 0x00,
 154        DATA_ONLY, 0x11,
 155        DATA_ONLY, 0x22,
 156        DATA_ONLY, 0x33,
 157        DATA_ONLY, 0x44,
 158        DATA_ONLY, 0x44,
 159        DATA_ONLY, 0x44,
 160
 161        DATA_ONLY, 0x55,
 162        DATA_ONLY, 0x55,
 163        DATA_ONLY, 0x66,
 164        DATA_ONLY, 0x66,
 165        DATA_ONLY, 0x66,
 166        DATA_ONLY, 0x66,
 167        DATA_ONLY, 0x66,
 168        DATA_ONLY, 0x66,
 169
 170        0xb7, 0x2c,
 171        DATA_ONLY, 0x12,
 172        DATA_ONLY, 0x0c,
 173        DATA_ONLY, 0x0a,
 174        DATA_ONLY, 0x10,
 175        DATA_ONLY, 0x0e,
 176        DATA_ONLY, 0x17,
 177        DATA_ONLY, 0x13,
 178        DATA_ONLY, 0x1f,
 179        DATA_ONLY, 0x1a,
 180        DATA_ONLY, 0x2a,
 181        DATA_ONLY, 0x24,
 182        DATA_ONLY, 0x1f,
 183        DATA_ONLY, 0x1b,
 184        DATA_ONLY, 0x1a,
 185        DATA_ONLY, 0x17,
 186
 187        DATA_ONLY, 0x2b,
 188        DATA_ONLY, 0x26,
 189        DATA_ONLY, 0x22,
 190        DATA_ONLY, 0x20,
 191        DATA_ONLY, 0x3a,
 192        DATA_ONLY, 0x34,
 193        DATA_ONLY, 0x30,
 194        DATA_ONLY, 0x2c,
 195        DATA_ONLY, 0x29,
 196        DATA_ONLY, 0x26,
 197        DATA_ONLY, 0x25,
 198        DATA_ONLY, 0x23,
 199        DATA_ONLY, 0x21,
 200        DATA_ONLY, 0x20,
 201        DATA_ONLY, 0x1e,
 202        DATA_ONLY, 0x1e,
 203
 204        0xb8, 0x00,
 205        DATA_ONLY, 0x00,
 206        DATA_ONLY, 0x11,
 207        DATA_ONLY, 0x22,
 208        DATA_ONLY, 0x33,
 209        DATA_ONLY, 0x44,
 210        DATA_ONLY, 0x44,
 211        DATA_ONLY, 0x44,
 212
 213        DATA_ONLY, 0x55,
 214        DATA_ONLY, 0x55,
 215        DATA_ONLY, 0x66,
 216        DATA_ONLY, 0x66,
 217        DATA_ONLY, 0x66,
 218        DATA_ONLY, 0x66,
 219        DATA_ONLY, 0x66,
 220        DATA_ONLY, 0x66,
 221
 222        0xb9, 0x2c,
 223        DATA_ONLY, 0x12,
 224        DATA_ONLY, 0x0c,
 225        DATA_ONLY, 0x0a,
 226        DATA_ONLY, 0x10,
 227        DATA_ONLY, 0x0e,
 228        DATA_ONLY, 0x17,
 229        DATA_ONLY, 0x13,
 230        DATA_ONLY, 0x1f,
 231        DATA_ONLY, 0x1a,
 232        DATA_ONLY, 0x2a,
 233        DATA_ONLY, 0x24,
 234        DATA_ONLY, 0x1f,
 235        DATA_ONLY, 0x1b,
 236        DATA_ONLY, 0x1a,
 237        DATA_ONLY, 0x17,
 238
 239        DATA_ONLY, 0x2b,
 240        DATA_ONLY, 0x26,
 241        DATA_ONLY, 0x22,
 242        DATA_ONLY, 0x20,
 243        DATA_ONLY, 0x3a,
 244        DATA_ONLY, 0x34,
 245        DATA_ONLY, 0x30,
 246        DATA_ONLY, 0x2c,
 247        DATA_ONLY, 0x29,
 248        DATA_ONLY, 0x26,
 249        DATA_ONLY, 0x25,
 250        DATA_ONLY, 0x23,
 251        DATA_ONLY, 0x21,
 252        DATA_ONLY, 0x20,
 253        DATA_ONLY, 0x1e,
 254        DATA_ONLY, 0x1e,
 255
 256        0xba, 0x00,
 257        DATA_ONLY, 0x00,
 258        DATA_ONLY, 0x11,
 259        DATA_ONLY, 0x22,
 260        DATA_ONLY, 0x33,
 261        DATA_ONLY, 0x44,
 262        DATA_ONLY, 0x44,
 263        DATA_ONLY, 0x44,
 264
 265        DATA_ONLY, 0x55,
 266        DATA_ONLY, 0x55,
 267        DATA_ONLY, 0x66,
 268        DATA_ONLY, 0x66,
 269        DATA_ONLY, 0x66,
 270        DATA_ONLY, 0x66,
 271        DATA_ONLY, 0x66,
 272        DATA_ONLY, 0x66,
 273
 274        0xc1, 0x4d,
 275        DATA_ONLY, 0x96,
 276        DATA_ONLY, 0x1d,
 277        DATA_ONLY, 0x00,
 278        DATA_ONLY, 0x00,
 279        DATA_ONLY, 0x01,
 280        DATA_ONLY, 0xdf,
 281        DATA_ONLY, 0x00,
 282        DATA_ONLY, 0x00,
 283        DATA_ONLY, 0x03,
 284        DATA_ONLY, 0x1f,
 285        DATA_ONLY, 0x00,
 286        DATA_ONLY, 0x00,
 287        DATA_ONLY, 0x00,
 288        DATA_ONLY, 0x00,
 289        DATA_ONLY, 0x00,
 290        DATA_ONLY, 0x00,
 291        DATA_ONLY, 0x00,
 292        DATA_ONLY, 0x00,
 293        DATA_ONLY, 0x03,
 294        DATA_ONLY, 0x06,
 295        DATA_ONLY, 0x09,
 296        DATA_ONLY, 0x0d,
 297        DATA_ONLY, 0x0f,
 298        DATA_ONLY, 0x12,
 299        DATA_ONLY, 0x15,
 300        DATA_ONLY, 0x18,
 301
 302        0xb2, 0x10,
 303        DATA_ONLY, 0x10,
 304        DATA_ONLY, 0x0b,
 305        DATA_ONLY, 0x05,
 306
 307        ENDDEF, 0x0000
 308};
 309
 310static const unsigned short seq_acl_on[] = {
 311        /* ACL on */
 312        0xc0, 0x01,
 313
 314        ENDDEF, 0x0000
 315};
 316
 317static const unsigned short seq_acl_off[] = {
 318        /* ACL off */
 319        0xc0, 0x00,
 320
 321        ENDDEF, 0x0000
 322};
 323
 324static const unsigned short seq_elvss_on[] = {
 325        /* ELVSS on */
 326        0xb1, 0x0b,
 327
 328        ENDDEF, 0x0000
 329};
 330
 331static const unsigned short seq_elvss_off[] = {
 332        /* ELVSS off */
 333        0xb1, 0x0a,
 334
 335        ENDDEF, 0x0000
 336};
 337
 338static const unsigned short seq_stand_by_off[] = {
 339        0x11, COMMAND_ONLY,
 340
 341        ENDDEF, 0x0000
 342};
 343
 344static const unsigned short seq_stand_by_on[] = {
 345        0x10, COMMAND_ONLY,
 346
 347        ENDDEF, 0x0000
 348};
 349
 350static const unsigned short seq_display_on[] = {
 351        0x29, COMMAND_ONLY,
 352
 353        ENDDEF, 0x0000
 354};
 355
 356
 357static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
 358{
 359        u16 buf[1];
 360        struct spi_message msg;
 361
 362        struct spi_transfer xfer = {
 363                .len            = 2,
 364                .tx_buf         = buf,
 365        };
 366
 367        buf[0] = (addr << 8) | data;
 368
 369        spi_message_init(&msg);
 370        spi_message_add_tail(&xfer, &msg);
 371
 372        return spi_sync(lcd->spi, &msg);
 373}
 374
 375static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
 376        unsigned char command)
 377{
 378        int ret = 0;
 379
 380        if (address != DATA_ONLY)
 381                ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
 382        if (command != COMMAND_ONLY)
 383                ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
 384
 385        return ret;
 386}
 387
 388static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
 389        const unsigned short *wbuf)
 390{
 391        int ret = 0, i = 0;
 392
 393        while ((wbuf[i] & DEFMASK) != ENDDEF) {
 394                if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
 395                        ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
 396                        if (ret)
 397                                break;
 398                } else {
 399                        msleep(wbuf[i+1]);
 400                }
 401                i += 2;
 402        }
 403
 404        return ret;
 405}
 406
 407static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
 408{
 409        unsigned int i = 0;
 410        int ret = 0;
 411
 412        /* disable gamma table updating. */
 413        ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
 414        if (ret) {
 415                dev_err(lcd->dev, "failed to disable gamma table updating.\n");
 416                goto gamma_err;
 417        }
 418
 419        for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
 420                ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
 421                if (ret) {
 422                        dev_err(lcd->dev, "failed to set gamma table.\n");
 423                        goto gamma_err;
 424                }
 425        }
 426
 427        /* update gamma table. */
 428        ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
 429        if (ret)
 430                dev_err(lcd->dev, "failed to update gamma table.\n");
 431
 432gamma_err:
 433        return ret;
 434}
 435
 436static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
 437{
 438        int ret = 0;
 439
 440        ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
 441
 442        return ret;
 443}
 444
 445
 446static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
 447{
 448        int ret, i;
 449        const unsigned short *init_seq[] = {
 450                seq_panel_condition_set,
 451                seq_display_condition_set,
 452                seq_gamma_setting,
 453                seq_etc_condition_set,
 454                seq_acl_on,
 455                seq_elvss_on,
 456        };
 457
 458        for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
 459                ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
 460                if (ret)
 461                        break;
 462        }
 463
 464        return ret;
 465}
 466
 467static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
 468{
 469        int ret = 0, i;
 470        const unsigned short *enable_seq[] = {
 471                seq_stand_by_off,
 472                seq_display_on,
 473        };
 474
 475        for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
 476                ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
 477                if (ret)
 478                        break;
 479        }
 480
 481        return ret;
 482}
 483
 484static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
 485{
 486        int ret;
 487
 488        ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on);
 489
 490        return ret;
 491}
 492
 493static int s6e63m0_power_is_on(int power)
 494{
 495        return power <= FB_BLANK_NORMAL;
 496}
 497
 498static int s6e63m0_power_on(struct s6e63m0 *lcd)
 499{
 500        int ret = 0;
 501        struct lcd_platform_data *pd;
 502        struct backlight_device *bd;
 503
 504        pd = lcd->lcd_pd;
 505        bd = lcd->bd;
 506
 507        if (!pd->power_on) {
 508                dev_err(lcd->dev, "power_on is NULL.\n");
 509                return -EINVAL;
 510        } else {
 511                pd->power_on(lcd->ld, 1);
 512                msleep(pd->power_on_delay);
 513        }
 514
 515        if (!pd->reset) {
 516                dev_err(lcd->dev, "reset is NULL.\n");
 517                return -EINVAL;
 518        } else {
 519                pd->reset(lcd->ld);
 520                msleep(pd->reset_delay);
 521        }
 522
 523        ret = s6e63m0_ldi_init(lcd);
 524        if (ret) {
 525                dev_err(lcd->dev, "failed to initialize ldi.\n");
 526                return ret;
 527        }
 528
 529        ret = s6e63m0_ldi_enable(lcd);
 530        if (ret) {
 531                dev_err(lcd->dev, "failed to enable ldi.\n");
 532                return ret;
 533        }
 534
 535        /* set brightness to current value after power on or resume. */
 536        ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
 537        if (ret) {
 538                dev_err(lcd->dev, "lcd gamma setting failed.\n");
 539                return ret;
 540        }
 541
 542        return 0;
 543}
 544
 545static int s6e63m0_power_off(struct s6e63m0 *lcd)
 546{
 547        int ret;
 548        struct lcd_platform_data *pd;
 549
 550        pd = lcd->lcd_pd;
 551
 552        ret = s6e63m0_ldi_disable(lcd);
 553        if (ret) {
 554                dev_err(lcd->dev, "lcd setting failed.\n");
 555                return -EIO;
 556        }
 557
 558        msleep(pd->power_off_delay);
 559
 560        pd->power_on(lcd->ld, 0);
 561
 562        return 0;
 563}
 564
 565static int s6e63m0_power(struct s6e63m0 *lcd, int power)
 566{
 567        int ret = 0;
 568
 569        if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power))
 570                ret = s6e63m0_power_on(lcd);
 571        else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power))
 572                ret = s6e63m0_power_off(lcd);
 573
 574        if (!ret)
 575                lcd->power = power;
 576
 577        return ret;
 578}
 579
 580static int s6e63m0_set_power(struct lcd_device *ld, int power)
 581{
 582        struct s6e63m0 *lcd = lcd_get_data(ld);
 583
 584        if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
 585                power != FB_BLANK_NORMAL) {
 586                dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
 587                return -EINVAL;
 588        }
 589
 590        return s6e63m0_power(lcd, power);
 591}
 592
 593static int s6e63m0_get_power(struct lcd_device *ld)
 594{
 595        struct s6e63m0 *lcd = lcd_get_data(ld);
 596
 597        return lcd->power;
 598}
 599
 600static int s6e63m0_get_brightness(struct backlight_device *bd)
 601{
 602        return bd->props.brightness;
 603}
 604
 605static int s6e63m0_set_brightness(struct backlight_device *bd)
 606{
 607        int ret = 0, brightness = bd->props.brightness;
 608        struct s6e63m0 *lcd = bl_get_data(bd);
 609
 610        if (brightness < MIN_BRIGHTNESS ||
 611                brightness > bd->props.max_brightness) {
 612                dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
 613                        MIN_BRIGHTNESS, MAX_BRIGHTNESS);
 614                return -EINVAL;
 615        }
 616
 617        ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
 618        if (ret) {
 619                dev_err(&bd->dev, "lcd brightness setting failed.\n");
 620                return -EIO;
 621        }
 622
 623        return ret;
 624}
 625
 626static struct lcd_ops s6e63m0_lcd_ops = {
 627        .set_power = s6e63m0_set_power,
 628        .get_power = s6e63m0_get_power,
 629};
 630
 631static const struct backlight_ops s6e63m0_backlight_ops  = {
 632        .get_brightness = s6e63m0_get_brightness,
 633        .update_status = s6e63m0_set_brightness,
 634};
 635
 636static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
 637                                      struct device_attribute *attr, char *buf)
 638{
 639        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 640        char temp[10];
 641
 642        switch (lcd->gamma_mode) {
 643        case 0:
 644                sprintf(temp, "2.2 mode\n");
 645                strcat(buf, temp);
 646                break;
 647        case 1:
 648                sprintf(temp, "1.9 mode\n");
 649                strcat(buf, temp);
 650                break;
 651        case 2:
 652                sprintf(temp, "1.7 mode\n");
 653                strcat(buf, temp);
 654                break;
 655        default:
 656                dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
 657                break;
 658        }
 659
 660        return strlen(buf);
 661}
 662
 663static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
 664                                       struct device_attribute *attr,
 665                                       const char *buf, size_t len)
 666{
 667        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 668        struct backlight_device *bd = NULL;
 669        int brightness, rc;
 670
 671        rc = kstrtouint(buf, 0, &lcd->gamma_mode);
 672        if (rc < 0)
 673                return rc;
 674
 675        bd = lcd->bd;
 676
 677        brightness = bd->props.brightness;
 678
 679        switch (lcd->gamma_mode) {
 680        case 0:
 681                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
 682                break;
 683        case 1:
 684                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
 685                break;
 686        case 2:
 687                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
 688                break;
 689        default:
 690                dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
 691                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
 692                break;
 693        }
 694        return len;
 695}
 696
 697static DEVICE_ATTR(gamma_mode, 0644,
 698                s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
 699
 700static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
 701                                      struct device_attribute *attr, char *buf)
 702{
 703        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 704        char temp[3];
 705
 706        sprintf(temp, "%d\n", lcd->gamma_table_count);
 707        strcpy(buf, temp);
 708
 709        return strlen(buf);
 710}
 711static DEVICE_ATTR(gamma_table, 0444,
 712                s6e63m0_sysfs_show_gamma_table, NULL);
 713
 714static int s6e63m0_probe(struct spi_device *spi)
 715{
 716        int ret = 0;
 717        struct s6e63m0 *lcd = NULL;
 718        struct lcd_device *ld = NULL;
 719        struct backlight_device *bd = NULL;
 720        struct backlight_properties props;
 721
 722        lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
 723        if (!lcd)
 724                return -ENOMEM;
 725
 726        /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
 727        spi->bits_per_word = 9;
 728
 729        ret = spi_setup(spi);
 730        if (ret < 0) {
 731                dev_err(&spi->dev, "spi setup failed.\n");
 732                return ret;
 733        }
 734
 735        lcd->spi = spi;
 736        lcd->dev = &spi->dev;
 737
 738        lcd->lcd_pd = spi->dev.platform_data;
 739        if (!lcd->lcd_pd) {
 740                dev_err(&spi->dev, "platform data is NULL.\n");
 741                return -EINVAL;
 742        }
 743
 744        ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
 745        if (IS_ERR(ld))
 746                return PTR_ERR(ld);
 747
 748        lcd->ld = ld;
 749
 750        memset(&props, 0, sizeof(struct backlight_properties));
 751        props.type = BACKLIGHT_RAW;
 752        props.max_brightness = MAX_BRIGHTNESS;
 753
 754        bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
 755                &s6e63m0_backlight_ops, &props);
 756        if (IS_ERR(bd)) {
 757                ret =  PTR_ERR(bd);
 758                goto out_lcd_unregister;
 759        }
 760
 761        bd->props.brightness = MAX_BRIGHTNESS;
 762        lcd->bd = bd;
 763
 764        /*
 765         * it gets gamma table count available so it gets user
 766         * know that.
 767         */
 768        lcd->gamma_table_count =
 769            sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int *));
 770
 771        ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
 772        if (ret < 0)
 773                dev_err(&(spi->dev), "failed to add sysfs entries\n");
 774
 775        ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
 776        if (ret < 0)
 777                dev_err(&(spi->dev), "failed to add sysfs entries\n");
 778
 779        /*
 780         * if lcd panel was on from bootloader like u-boot then
 781         * do not lcd on.
 782         */
 783        if (!lcd->lcd_pd->lcd_enabled) {
 784                /*
 785                 * if lcd panel was off from bootloader then
 786                 * current lcd status is powerdown and then
 787                 * it enables lcd panel.
 788                 */
 789                lcd->power = FB_BLANK_POWERDOWN;
 790
 791                s6e63m0_power(lcd, FB_BLANK_UNBLANK);
 792        } else {
 793                lcd->power = FB_BLANK_UNBLANK;
 794        }
 795
 796        spi_set_drvdata(spi, lcd);
 797
 798        dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
 799
 800        return 0;
 801
 802out_lcd_unregister:
 803        lcd_device_unregister(ld);
 804        return ret;
 805}
 806
 807static int s6e63m0_remove(struct spi_device *spi)
 808{
 809        struct s6e63m0 *lcd = spi_get_drvdata(spi);
 810
 811        s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
 812        device_remove_file(&spi->dev, &dev_attr_gamma_table);
 813        device_remove_file(&spi->dev, &dev_attr_gamma_mode);
 814        backlight_device_unregister(lcd->bd);
 815        lcd_device_unregister(lcd->ld);
 816
 817        return 0;
 818}
 819
 820#ifdef CONFIG_PM_SLEEP
 821static int s6e63m0_suspend(struct device *dev)
 822{
 823        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 824
 825        dev_dbg(dev, "lcd->power = %d\n", lcd->power);
 826
 827        /*
 828         * when lcd panel is suspend, lcd panel becomes off
 829         * regardless of status.
 830         */
 831        return s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
 832}
 833
 834static int s6e63m0_resume(struct device *dev)
 835{
 836        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 837
 838        lcd->power = FB_BLANK_POWERDOWN;
 839
 840        return s6e63m0_power(lcd, FB_BLANK_UNBLANK);
 841}
 842#endif
 843
 844static SIMPLE_DEV_PM_OPS(s6e63m0_pm_ops, s6e63m0_suspend, s6e63m0_resume);
 845
 846/* Power down all displays on reboot, poweroff or halt. */
 847static void s6e63m0_shutdown(struct spi_device *spi)
 848{
 849        struct s6e63m0 *lcd = spi_get_drvdata(spi);
 850
 851        s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
 852}
 853
 854static struct spi_driver s6e63m0_driver = {
 855        .driver = {
 856                .name   = "s6e63m0",
 857                .owner  = THIS_MODULE,
 858                .pm     = &s6e63m0_pm_ops,
 859        },
 860        .probe          = s6e63m0_probe,
 861        .remove         = s6e63m0_remove,
 862        .shutdown       = s6e63m0_shutdown,
 863};
 864
 865module_spi_driver(s6e63m0_driver);
 866
 867MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
 868MODULE_DESCRIPTION("S6E63M0 LCD Driver");
 869MODULE_LICENSE("GPL");
 870
 871