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