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 .owner = THIS_MODULE, 846 .pm = &s6e63m0_pm_ops, 847 }, 848 .probe = s6e63m0_probe, 849 .remove = s6e63m0_remove, 850 .shutdown = s6e63m0_shutdown, 851}; 852 853module_spi_driver(s6e63m0_driver); 854 855MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); 856MODULE_DESCRIPTION("S6E63M0 LCD Driver"); 857MODULE_LICENSE("GPL"); 858 859