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