s6e63m0.c revision 97a4d9d53a2c63706cb5bf222c37bf273939a7eb
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 msleep(wbuf[i+1]); 411 } 412 i += 2; 413 } 414 415 return ret; 416} 417 418static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma) 419{ 420 unsigned int i = 0; 421 int ret = 0; 422 423 /* disable gamma table updating. */ 424 ret = s6e63m0_spi_write(lcd, 0xfa, 0x00); 425 if (ret) { 426 dev_err(lcd->dev, "failed to disable gamma table updating.\n"); 427 goto gamma_err; 428 } 429 430 for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) { 431 ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]); 432 if (ret) { 433 dev_err(lcd->dev, "failed to set gamma table.\n"); 434 goto gamma_err; 435 } 436 } 437 438 /* update gamma table. */ 439 ret = s6e63m0_spi_write(lcd, 0xfa, 0x01); 440 if (ret) 441 dev_err(lcd->dev, "failed to update gamma table.\n"); 442 443gamma_err: 444 return ret; 445} 446 447static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma) 448{ 449 int ret = 0; 450 451 ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); 452 453 return ret; 454} 455 456 457static int s6e63m0_ldi_init(struct s6e63m0 *lcd) 458{ 459 int ret, i; 460 const unsigned short *init_seq[] = { 461 seq_panel_condition_set, 462 seq_display_condition_set, 463 seq_gamma_setting, 464 seq_etc_condition_set, 465 seq_acl_on, 466 seq_elvss_on, 467 }; 468 469 for (i = 0; i < ARRAY_SIZE(init_seq); i++) { 470 ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]); 471 if (ret) 472 break; 473 } 474 475 return ret; 476} 477 478static int s6e63m0_ldi_enable(struct s6e63m0 *lcd) 479{ 480 int ret = 0, i; 481 const unsigned short *enable_seq[] = { 482 seq_stand_by_off, 483 seq_display_on, 484 }; 485 486 for (i = 0; i < ARRAY_SIZE(enable_seq); i++) { 487 ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]); 488 if (ret) 489 break; 490 } 491 492 return ret; 493} 494 495static int s6e63m0_ldi_disable(struct s6e63m0 *lcd) 496{ 497 int ret; 498 499 ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on); 500 501 return ret; 502} 503 504static int s6e63m0_power_on(struct s6e63m0 *lcd) 505{ 506 int ret = 0; 507 struct lcd_platform_data *pd = NULL; 508 struct backlight_device *bd = NULL; 509 510 pd = lcd->lcd_pd; 511 if (!pd) { 512 dev_err(lcd->dev, "platform data is NULL.\n"); 513 return -EFAULT; 514 } 515 516 bd = lcd->bd; 517 if (!bd) { 518 dev_err(lcd->dev, "backlight device is NULL.\n"); 519 return -EFAULT; 520 } 521 522 if (!pd->power_on) { 523 dev_err(lcd->dev, "power_on is NULL.\n"); 524 return -EFAULT; 525 } else { 526 pd->power_on(lcd->ld, 1); 527 msleep(pd->power_on_delay); 528 } 529 530 if (!pd->reset) { 531 dev_err(lcd->dev, "reset is NULL.\n"); 532 return -EFAULT; 533 } else { 534 pd->reset(lcd->ld); 535 msleep(pd->reset_delay); 536 } 537 538 ret = s6e63m0_ldi_init(lcd); 539 if (ret) { 540 dev_err(lcd->dev, "failed to initialize ldi.\n"); 541 return ret; 542 } 543 544 ret = s6e63m0_ldi_enable(lcd); 545 if (ret) { 546 dev_err(lcd->dev, "failed to enable ldi.\n"); 547 return ret; 548 } 549 550 /* set brightness to current value after power on or resume. */ 551 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness); 552 if (ret) { 553 dev_err(lcd->dev, "lcd gamma setting failed.\n"); 554 return ret; 555 } 556 557 return 0; 558} 559 560static int s6e63m0_power_off(struct s6e63m0 *lcd) 561{ 562 int ret = 0; 563 struct lcd_platform_data *pd = NULL; 564 565 pd = lcd->lcd_pd; 566 if (!pd) { 567 dev_err(lcd->dev, "platform data is NULL.\n"); 568 return -EFAULT; 569 } 570 571 ret = s6e63m0_ldi_disable(lcd); 572 if (ret) { 573 dev_err(lcd->dev, "lcd setting failed.\n"); 574 return -EIO; 575 } 576 577 msleep(pd->power_off_delay); 578 579 if (!pd->power_on) { 580 dev_err(lcd->dev, "power_on is NULL.\n"); 581 return -EFAULT; 582 } else 583 pd->power_on(lcd->ld, 0); 584 585 return 0; 586} 587 588static int s6e63m0_power(struct s6e63m0 *lcd, int power) 589{ 590 int ret = 0; 591 592 if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) 593 ret = s6e63m0_power_on(lcd); 594 else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) 595 ret = s6e63m0_power_off(lcd); 596 597 if (!ret) 598 lcd->power = power; 599 600 return ret; 601} 602 603static int s6e63m0_set_power(struct lcd_device *ld, int power) 604{ 605 struct s6e63m0 *lcd = lcd_get_data(ld); 606 607 if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && 608 power != FB_BLANK_NORMAL) { 609 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); 610 return -EINVAL; 611 } 612 613 return s6e63m0_power(lcd, power); 614} 615 616static int s6e63m0_get_power(struct lcd_device *ld) 617{ 618 struct s6e63m0 *lcd = lcd_get_data(ld); 619 620 return lcd->power; 621} 622 623static int s6e63m0_get_brightness(struct backlight_device *bd) 624{ 625 return bd->props.brightness; 626} 627 628static int s6e63m0_set_brightness(struct backlight_device *bd) 629{ 630 int ret = 0, brightness = bd->props.brightness; 631 struct s6e63m0 *lcd = bl_get_data(bd); 632 633 if (brightness < MIN_BRIGHTNESS || 634 brightness > bd->props.max_brightness) { 635 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n", 636 MIN_BRIGHTNESS, MAX_BRIGHTNESS); 637 return -EINVAL; 638 } 639 640 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness); 641 if (ret) { 642 dev_err(&bd->dev, "lcd brightness setting failed.\n"); 643 return -EIO; 644 } 645 646 return ret; 647} 648 649static struct lcd_ops s6e63m0_lcd_ops = { 650 .set_power = s6e63m0_set_power, 651 .get_power = s6e63m0_get_power, 652}; 653 654static const struct backlight_ops s6e63m0_backlight_ops = { 655 .get_brightness = s6e63m0_get_brightness, 656 .update_status = s6e63m0_set_brightness, 657}; 658 659static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev, 660 struct device_attribute *attr, char *buf) 661{ 662 struct s6e63m0 *lcd = dev_get_drvdata(dev); 663 char temp[10]; 664 665 switch (lcd->gamma_mode) { 666 case 0: 667 sprintf(temp, "2.2 mode\n"); 668 strcat(buf, temp); 669 break; 670 case 1: 671 sprintf(temp, "1.9 mode\n"); 672 strcat(buf, temp); 673 break; 674 case 2: 675 sprintf(temp, "1.7 mode\n"); 676 strcat(buf, temp); 677 break; 678 default: 679 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n"); 680 break; 681 } 682 683 return strlen(buf); 684} 685 686static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev, 687 struct device_attribute *attr, 688 const char *buf, size_t len) 689{ 690 struct s6e63m0 *lcd = dev_get_drvdata(dev); 691 struct backlight_device *bd = NULL; 692 int brightness, rc; 693 694 rc = kstrtouint(buf, 0, &lcd->gamma_mode); 695 if (rc < 0) 696 return rc; 697 698 bd = lcd->bd; 699 700 brightness = bd->props.brightness; 701 702 switch (lcd->gamma_mode) { 703 case 0: 704 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]); 705 break; 706 case 1: 707 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]); 708 break; 709 case 2: 710 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]); 711 break; 712 default: 713 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n"); 714 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]); 715 break; 716 } 717 return len; 718} 719 720static DEVICE_ATTR(gamma_mode, 0644, 721 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode); 722 723static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev, 724 struct device_attribute *attr, char *buf) 725{ 726 struct s6e63m0 *lcd = dev_get_drvdata(dev); 727 char temp[3]; 728 729 sprintf(temp, "%d\n", lcd->gamma_table_count); 730 strcpy(buf, temp); 731 732 return strlen(buf); 733} 734static DEVICE_ATTR(gamma_table, 0444, 735 s6e63m0_sysfs_show_gamma_table, NULL); 736 737static int s6e63m0_probe(struct spi_device *spi) 738{ 739 int ret = 0; 740 struct s6e63m0 *lcd = NULL; 741 struct lcd_device *ld = NULL; 742 struct backlight_device *bd = NULL; 743 struct backlight_properties props; 744 745 lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL); 746 if (!lcd) 747 return -ENOMEM; 748 749 /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */ 750 spi->bits_per_word = 9; 751 752 ret = spi_setup(spi); 753 if (ret < 0) { 754 dev_err(&spi->dev, "spi setup failed.\n"); 755 return ret; 756 } 757 758 lcd->spi = spi; 759 lcd->dev = &spi->dev; 760 761 lcd->lcd_pd = spi->dev.platform_data; 762 if (!lcd->lcd_pd) { 763 dev_err(&spi->dev, "platform data is NULL.\n"); 764 return -EFAULT; 765 } 766 767 ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops); 768 if (IS_ERR(ld)) 769 return PTR_ERR(ld); 770 771 lcd->ld = ld; 772 773 memset(&props, 0, sizeof(struct backlight_properties)); 774 props.type = BACKLIGHT_RAW; 775 props.max_brightness = MAX_BRIGHTNESS; 776 777 bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd, 778 &s6e63m0_backlight_ops, &props); 779 if (IS_ERR(bd)) { 780 ret = PTR_ERR(bd); 781 goto out_lcd_unregister; 782 } 783 784 bd->props.brightness = MAX_BRIGHTNESS; 785 lcd->bd = bd; 786 787 /* 788 * it gets gamma table count available so it gets user 789 * know that. 790 */ 791 lcd->gamma_table_count = 792 sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int)); 793 794 ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode); 795 if (ret < 0) 796 dev_err(&(spi->dev), "failed to add sysfs entries\n"); 797 798 ret = device_create_file(&(spi->dev), &dev_attr_gamma_table); 799 if (ret < 0) 800 dev_err(&(spi->dev), "failed to add sysfs entries\n"); 801 802 /* 803 * if lcd panel was on from bootloader like u-boot then 804 * do not lcd on. 805 */ 806 if (!lcd->lcd_pd->lcd_enabled) { 807 /* 808 * if lcd panel was off from bootloader then 809 * current lcd status is powerdown and then 810 * it enables lcd panel. 811 */ 812 lcd->power = FB_BLANK_POWERDOWN; 813 814 s6e63m0_power(lcd, FB_BLANK_UNBLANK); 815 } else 816 lcd->power = FB_BLANK_UNBLANK; 817 818 dev_set_drvdata(&spi->dev, lcd); 819 820 dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n"); 821 822 return 0; 823 824out_lcd_unregister: 825 lcd_device_unregister(ld); 826 return ret; 827} 828 829static int s6e63m0_remove(struct spi_device *spi) 830{ 831 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); 832 833 s6e63m0_power(lcd, FB_BLANK_POWERDOWN); 834 device_remove_file(&spi->dev, &dev_attr_gamma_table); 835 device_remove_file(&spi->dev, &dev_attr_gamma_mode); 836 backlight_device_unregister(lcd->bd); 837 lcd_device_unregister(lcd->ld); 838 839 return 0; 840} 841 842#if defined(CONFIG_PM) 843static unsigned int before_power; 844 845static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg) 846{ 847 int ret = 0; 848 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); 849 850 dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); 851 852 before_power = lcd->power; 853 854 /* 855 * when lcd panel is suspend, lcd panel becomes off 856 * regardless of status. 857 */ 858 ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN); 859 860 return ret; 861} 862 863static int s6e63m0_resume(struct spi_device *spi) 864{ 865 int ret = 0; 866 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); 867 868 /* 869 * after suspended, if lcd panel status is FB_BLANK_UNBLANK 870 * (at that time, before_power is FB_BLANK_UNBLANK) then 871 * it changes that status to FB_BLANK_POWERDOWN to get lcd on. 872 */ 873 if (before_power == FB_BLANK_UNBLANK) 874 lcd->power = FB_BLANK_POWERDOWN; 875 876 dev_dbg(&spi->dev, "before_power = %d\n", before_power); 877 878 ret = s6e63m0_power(lcd, before_power); 879 880 return ret; 881} 882#else 883#define s6e63m0_suspend NULL 884#define s6e63m0_resume NULL 885#endif 886 887/* Power down all displays on reboot, poweroff or halt. */ 888static void s6e63m0_shutdown(struct spi_device *spi) 889{ 890 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); 891 892 s6e63m0_power(lcd, FB_BLANK_POWERDOWN); 893} 894 895static struct spi_driver s6e63m0_driver = { 896 .driver = { 897 .name = "s6e63m0", 898 .owner = THIS_MODULE, 899 }, 900 .probe = s6e63m0_probe, 901 .remove = s6e63m0_remove, 902 .shutdown = s6e63m0_shutdown, 903 .suspend = s6e63m0_suspend, 904 .resume = s6e63m0_resume, 905}; 906 907module_spi_driver(s6e63m0_driver); 908 909MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); 910MODULE_DESCRIPTION("S6E63M0 LCD Driver"); 911MODULE_LICENSE("GPL"); 912 913