s6e63m0.c revision d974e00b955ee390e02ae2f0eeb5ed921599ec07
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 __init 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