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        }
 511
 512        pd->power_on(lcd->ld, 1);
 513        msleep(pd->power_on_delay);
 514
 515        if (!pd->reset) {
 516                dev_err(lcd->dev, "reset is NULL.\n");
 517                return -EINVAL;
 518        }
 519
 520        pd->reset(lcd->ld);
 521        msleep(pd->reset_delay);
 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_set_brightness(struct backlight_device *bd)
 601{
 602        int ret = 0, brightness = bd->props.brightness;
 603        struct s6e63m0 *lcd = bl_get_data(bd);
 604
 605        if (brightness < MIN_BRIGHTNESS ||
 606                brightness > bd->props.max_brightness) {
 607                dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
 608                        MIN_BRIGHTNESS, MAX_BRIGHTNESS);
 609                return -EINVAL;
 610        }
 611
 612        ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
 613        if (ret) {
 614                dev_err(&bd->dev, "lcd brightness setting failed.\n");
 615                return -EIO;
 616        }
 617
 618        return ret;
 619}
 620
 621static struct lcd_ops s6e63m0_lcd_ops = {
 622        .set_power = s6e63m0_set_power,
 623        .get_power = s6e63m0_get_power,
 624};
 625
 626static const struct backlight_ops s6e63m0_backlight_ops  = {
 627        .update_status = s6e63m0_set_brightness,
 628};
 629
 630static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
 631                                      struct device_attribute *attr, char *buf)
 632{
 633        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 634        char temp[10];
 635
 636        switch (lcd->gamma_mode) {
 637        case 0:
 638                sprintf(temp, "2.2 mode\n");
 639                strcat(buf, temp);
 640                break;
 641        case 1:
 642                sprintf(temp, "1.9 mode\n");
 643                strcat(buf, temp);
 644                break;
 645        case 2:
 646                sprintf(temp, "1.7 mode\n");
 647                strcat(buf, temp);
 648                break;
 649        default:
 650                dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
 651                break;
 652        }
 653
 654        return strlen(buf);
 655}
 656
 657static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
 658                                       struct device_attribute *attr,
 659                                       const char *buf, size_t len)
 660{
 661        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 662        struct backlight_device *bd = NULL;
 663        int brightness, rc;
 664
 665        rc = kstrtouint(buf, 0, &lcd->gamma_mode);
 666        if (rc < 0)
 667                return rc;
 668
 669        bd = lcd->bd;
 670
 671        brightness = bd->props.brightness;
 672
 673        switch (lcd->gamma_mode) {
 674        case 0:
 675                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
 676                break;
 677        case 1:
 678                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
 679                break;
 680        case 2:
 681                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
 682                break;
 683        default:
 684                dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
 685                _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
 686                break;
 687        }
 688        return len;
 689}
 690
 691static DEVICE_ATTR(gamma_mode, 0644,
 692                s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
 693
 694static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
 695                                      struct device_attribute *attr, char *buf)
 696{
 697        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 698        char temp[3];
 699
 700        sprintf(temp, "%u\n", lcd->gamma_table_count);
 701        strcpy(buf, temp);
 702
 703        return strlen(buf);
 704}
 705static DEVICE_ATTR(gamma_table, 0444,
 706                s6e63m0_sysfs_show_gamma_table, NULL);
 707
 708static int s6e63m0_probe(struct spi_device *spi)
 709{
 710        int ret = 0;
 711        struct s6e63m0 *lcd = NULL;
 712        struct lcd_device *ld = NULL;
 713        struct backlight_device *bd = NULL;
 714        struct backlight_properties props;
 715
 716        lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
 717        if (!lcd)
 718                return -ENOMEM;
 719
 720        /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
 721        spi->bits_per_word = 9;
 722
 723        ret = spi_setup(spi);
 724        if (ret < 0) {
 725                dev_err(&spi->dev, "spi setup failed.\n");
 726                return ret;
 727        }
 728
 729        lcd->spi = spi;
 730        lcd->dev = &spi->dev;
 731
 732        lcd->lcd_pd = dev_get_platdata(&spi->dev);
 733        if (!lcd->lcd_pd) {
 734                dev_err(&spi->dev, "platform data is NULL.\n");
 735                return -EINVAL;
 736        }
 737
 738        ld = devm_lcd_device_register(&spi->dev, "s6e63m0", &spi->dev, lcd,
 739                                &s6e63m0_lcd_ops);
 740        if (IS_ERR(ld))
 741                return PTR_ERR(ld);
 742
 743        lcd->ld = ld;
 744
 745        memset(&props, 0, sizeof(struct backlight_properties));
 746        props.type = BACKLIGHT_RAW;
 747        props.max_brightness = MAX_BRIGHTNESS;
 748
 749        bd = devm_backlight_device_register(&spi->dev, "s6e63m0bl-bl",
 750                                        &spi->dev, lcd, &s6e63m0_backlight_ops,
 751                                        &props);
 752        if (IS_ERR(bd))
 753                return PTR_ERR(bd);
 754
 755        bd->props.brightness = MAX_BRIGHTNESS;
 756        lcd->bd = bd;
 757
 758        /*
 759         * it gets gamma table count available so it gets user
 760         * know that.
 761         */
 762        lcd->gamma_table_count =
 763            sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int *));
 764
 765        ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
 766        if (ret < 0)
 767                dev_err(&(spi->dev), "failed to add sysfs entries\n");
 768
 769        ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
 770        if (ret < 0)
 771                dev_err(&(spi->dev), "failed to add sysfs entries\n");
 772
 773        /*
 774         * if lcd panel was on from bootloader like u-boot then
 775         * do not lcd on.
 776         */
 777        if (!lcd->lcd_pd->lcd_enabled) {
 778                /*
 779                 * if lcd panel was off from bootloader then
 780                 * current lcd status is powerdown and then
 781                 * it enables lcd panel.
 782                 */
 783                lcd->power = FB_BLANK_POWERDOWN;
 784
 785                s6e63m0_power(lcd, FB_BLANK_UNBLANK);
 786        } else {
 787                lcd->power = FB_BLANK_UNBLANK;
 788        }
 789
 790        spi_set_drvdata(spi, lcd);
 791
 792        dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
 793
 794        return 0;
 795}
 796
 797static int s6e63m0_remove(struct spi_device *spi)
 798{
 799        struct s6e63m0 *lcd = spi_get_drvdata(spi);
 800
 801        s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
 802        device_remove_file(&spi->dev, &dev_attr_gamma_table);
 803        device_remove_file(&spi->dev, &dev_attr_gamma_mode);
 804
 805        return 0;
 806}
 807
 808#ifdef CONFIG_PM_SLEEP
 809static int s6e63m0_suspend(struct device *dev)
 810{
 811        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 812
 813        dev_dbg(dev, "lcd->power = %d\n", lcd->power);
 814
 815        /*
 816         * when lcd panel is suspend, lcd panel becomes off
 817         * regardless of status.
 818         */
 819        return s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
 820}
 821
 822static int s6e63m0_resume(struct device *dev)
 823{
 824        struct s6e63m0 *lcd = dev_get_drvdata(dev);
 825
 826        lcd->power = FB_BLANK_POWERDOWN;
 827
 828        return s6e63m0_power(lcd, FB_BLANK_UNBLANK);
 829}
 830#endif
 831
 832static SIMPLE_DEV_PM_OPS(s6e63m0_pm_ops, s6e63m0_suspend, s6e63m0_resume);
 833
 834/* Power down all displays on reboot, poweroff or halt. */
 835static void s6e63m0_shutdown(struct spi_device *spi)
 836{
 837        struct s6e63m0 *lcd = spi_get_drvdata(spi);
 838
 839        s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
 840}
 841
 842static struct spi_driver s6e63m0_driver = {
 843        .driver = {
 844                .name   = "s6e63m0",
 845                .pm     = &s6e63m0_pm_ops,
 846        },
 847        .probe          = s6e63m0_probe,
 848        .remove         = s6e63m0_remove,
 849        .shutdown       = s6e63m0_shutdown,
 850};
 851
 852module_spi_driver(s6e63m0_driver);
 853
 854MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
 855MODULE_DESCRIPTION("S6E63M0 LCD Driver");
 856MODULE_LICENSE("GPL");
 857
 858