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