1 /* 2 * ams369fg06 AMOLED LCD panel driver. 3 * 4 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 5 * Author: Jingoo Han <jg1.han@samsung.com> 6 * 7 * Derived from drivers/video/s6e63m0.c 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 15 #include <linux/backlight.h> 16 #include <linux/delay.h> 17 #include <linux/fb.h> 18 #include <linux/gpio.h> 19 #include <linux/lcd.h> 20 #include <linux/module.h> 21 #include <linux/spi/spi.h> 22 #include <linux/wait.h> 23 24 #define SLEEPMSEC 0x1000 25 #define ENDDEF 0x2000 26 #define DEFMASK 0xFF00 27 #define COMMAND_ONLY 0xFE 28 #define DATA_ONLY 0xFF 29 30 #define MAX_GAMMA_LEVEL 5 31 #define GAMMA_TABLE_COUNT 21 32 33 #define MIN_BRIGHTNESS 0 34 #define MAX_BRIGHTNESS 255 35 #define DEFAULT_BRIGHTNESS 150 36 37 struct ams369fg06 { 38 struct device *dev; 39 struct spi_device *spi; 40 unsigned int power; 41 struct lcd_device *ld; 42 struct backlight_device *bd; 43 struct lcd_platform_data *lcd_pd; 44 }; 45 46 static const unsigned short seq_display_on[] = { 47 0x14, 0x03, 48 ENDDEF, 0x0000 49 }; 50 51 static const unsigned short seq_display_off[] = { 52 0x14, 0x00, 53 ENDDEF, 0x0000 54 }; 55 56 static const unsigned short seq_stand_by_on[] = { 57 0x1D, 0xA1, 58 SLEEPMSEC, 200, 59 ENDDEF, 0x0000 60 }; 61 62 static const unsigned short seq_stand_by_off[] = { 63 0x1D, 0xA0, 64 SLEEPMSEC, 250, 65 ENDDEF, 0x0000 66 }; 67 68 static const unsigned short seq_setting[] = { 69 0x31, 0x08, 70 0x32, 0x14, 71 0x30, 0x02, 72 0x27, 0x01, 73 0x12, 0x08, 74 0x13, 0x08, 75 0x15, 0x00, 76 0x16, 0x00, 77 78 0xef, 0xd0, 79 DATA_ONLY, 0xe8, 80 81 0x39, 0x44, 82 0x40, 0x00, 83 0x41, 0x3f, 84 0x42, 0x2a, 85 0x43, 0x27, 86 0x44, 0x27, 87 0x45, 0x1f, 88 0x46, 0x44, 89 0x50, 0x00, 90 0x51, 0x00, 91 0x52, 0x17, 92 0x53, 0x24, 93 0x54, 0x26, 94 0x55, 0x1f, 95 0x56, 0x43, 96 0x60, 0x00, 97 0x61, 0x3f, 98 0x62, 0x2a, 99 0x63, 0x25, 100 0x64, 0x24, 101 0x65, 0x1b, 102 0x66, 0x5c, 103 104 0x17, 0x22, 105 0x18, 0x33, 106 0x19, 0x03, 107 0x1a, 0x01, 108 0x22, 0xa4, 109 0x23, 0x00, 110 0x26, 0xa0, 111 112 0x1d, 0xa0, 113 SLEEPMSEC, 300, 114 115 0x14, 0x03, 116 117 ENDDEF, 0x0000 118 }; 119 120 /* gamma value: 2.2 */ 121 static const unsigned int ams369fg06_22_250[] = { 122 0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44, 123 0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43, 124 0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c, 125 }; 126 127 static const unsigned int ams369fg06_22_200[] = { 128 0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e, 129 0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d, 130 0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53, 131 }; 132 133 static const unsigned int ams369fg06_22_150[] = { 134 0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37, 135 0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36, 136 0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a, 137 }; 138 139 static const unsigned int ams369fg06_22_100[] = { 140 0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f, 141 0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e, 142 0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f, 143 }; 144 145 static const unsigned int ams369fg06_22_50[] = { 146 0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24, 147 0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23, 148 0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31, 149 }; 150 151 struct ams369fg06_gamma { 152 unsigned int *gamma_22_table[MAX_GAMMA_LEVEL]; 153 }; 154 155 static struct ams369fg06_gamma gamma_table = { 156 .gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50, 157 .gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100, 158 .gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150, 159 .gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200, 160 .gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250, 161 }; 162 163 static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data) 164 { 165 u16 buf[1]; 166 struct spi_message msg; 167 168 struct spi_transfer xfer = { 169 .len = 2, 170 .tx_buf = buf, 171 }; 172 173 buf[0] = (addr << 8) | data; 174 175 spi_message_init(&msg); 176 spi_message_add_tail(&xfer, &msg); 177 178 return spi_sync(lcd->spi, &msg); 179 } 180 181 static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address, 182 unsigned char command) 183 { 184 int ret = 0; 185 186 if (address != DATA_ONLY) 187 ret = ams369fg06_spi_write_byte(lcd, 0x70, address); 188 if (command != COMMAND_ONLY) 189 ret = ams369fg06_spi_write_byte(lcd, 0x72, command); 190 191 return ret; 192 } 193 194 static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd, 195 const unsigned short *wbuf) 196 { 197 int ret = 0, i = 0; 198 199 while ((wbuf[i] & DEFMASK) != ENDDEF) { 200 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) { 201 ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]); 202 if (ret) 203 break; 204 } else { 205 msleep(wbuf[i+1]); 206 } 207 i += 2; 208 } 209 210 return ret; 211 } 212 213 static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd, 214 const unsigned int *gamma) 215 { 216 unsigned int i = 0; 217 int ret = 0; 218 219 for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) { 220 ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]); 221 ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]); 222 ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]); 223 if (ret) { 224 dev_err(lcd->dev, "failed to set gamma table.\n"); 225 goto gamma_err; 226 } 227 } 228 229 gamma_err: 230 return ret; 231 } 232 233 static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness) 234 { 235 int ret = 0; 236 int gamma = 0; 237 238 if ((brightness >= 0) && (brightness <= 50)) 239 gamma = 0; 240 else if ((brightness > 50) && (brightness <= 100)) 241 gamma = 1; 242 else if ((brightness > 100) && (brightness <= 150)) 243 gamma = 2; 244 else if ((brightness > 150) && (brightness <= 200)) 245 gamma = 3; 246 else if ((brightness > 200) && (brightness <= 255)) 247 gamma = 4; 248 249 ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); 250 251 return ret; 252 } 253 254 static int ams369fg06_ldi_init(struct ams369fg06 *lcd) 255 { 256 int ret, i; 257 static const unsigned short *init_seq[] = { 258 seq_setting, 259 seq_stand_by_off, 260 }; 261 262 for (i = 0; i < ARRAY_SIZE(init_seq); i++) { 263 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); 264 if (ret) 265 break; 266 } 267 268 return ret; 269 } 270 271 static int ams369fg06_ldi_enable(struct ams369fg06 *lcd) 272 { 273 int ret, i; 274 static const unsigned short *init_seq[] = { 275 seq_stand_by_off, 276 seq_display_on, 277 }; 278 279 for (i = 0; i < ARRAY_SIZE(init_seq); i++) { 280 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); 281 if (ret) 282 break; 283 } 284 285 return ret; 286 } 287 288 static int ams369fg06_ldi_disable(struct ams369fg06 *lcd) 289 { 290 int ret, i; 291 292 static const unsigned short *init_seq[] = { 293 seq_display_off, 294 seq_stand_by_on, 295 }; 296 297 for (i = 0; i < ARRAY_SIZE(init_seq); i++) { 298 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); 299 if (ret) 300 break; 301 } 302 303 return ret; 304 } 305 306 static int ams369fg06_power_is_on(int power) 307 { 308 return power <= FB_BLANK_NORMAL; 309 } 310 311 static int ams369fg06_power_on(struct ams369fg06 *lcd) 312 { 313 int ret = 0; 314 struct lcd_platform_data *pd; 315 struct backlight_device *bd; 316 317 pd = lcd->lcd_pd; 318 bd = lcd->bd; 319 320 if (pd->power_on) { 321 pd->power_on(lcd->ld, 1); 322 msleep(pd->power_on_delay); 323 } 324 325 if (!pd->reset) { 326 dev_err(lcd->dev, "reset is NULL.\n"); 327 return -EINVAL; 328 } else { 329 pd->reset(lcd->ld); 330 msleep(pd->reset_delay); 331 } 332 333 ret = ams369fg06_ldi_init(lcd); 334 if (ret) { 335 dev_err(lcd->dev, "failed to initialize ldi.\n"); 336 return ret; 337 } 338 339 ret = ams369fg06_ldi_enable(lcd); 340 if (ret) { 341 dev_err(lcd->dev, "failed to enable ldi.\n"); 342 return ret; 343 } 344 345 /* set brightness to current value after power on or resume. */ 346 ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness); 347 if (ret) { 348 dev_err(lcd->dev, "lcd gamma setting failed.\n"); 349 return ret; 350 } 351 352 return 0; 353 } 354 355 static int ams369fg06_power_off(struct ams369fg06 *lcd) 356 { 357 int ret; 358 struct lcd_platform_data *pd; 359 360 pd = lcd->lcd_pd; 361 362 ret = ams369fg06_ldi_disable(lcd); 363 if (ret) { 364 dev_err(lcd->dev, "lcd setting failed.\n"); 365 return -EIO; 366 } 367 368 msleep(pd->power_off_delay); 369 370 if (pd->power_on) 371 pd->power_on(lcd->ld, 0); 372 373 return 0; 374 } 375 376 static int ams369fg06_power(struct ams369fg06 *lcd, int power) 377 { 378 int ret = 0; 379 380 if (ams369fg06_power_is_on(power) && 381 !ams369fg06_power_is_on(lcd->power)) 382 ret = ams369fg06_power_on(lcd); 383 else if (!ams369fg06_power_is_on(power) && 384 ams369fg06_power_is_on(lcd->power)) 385 ret = ams369fg06_power_off(lcd); 386 387 if (!ret) 388 lcd->power = power; 389 390 return ret; 391 } 392 393 static int ams369fg06_get_power(struct lcd_device *ld) 394 { 395 struct ams369fg06 *lcd = lcd_get_data(ld); 396 397 return lcd->power; 398 } 399 400 static int ams369fg06_set_power(struct lcd_device *ld, int power) 401 { 402 struct ams369fg06 *lcd = lcd_get_data(ld); 403 404 if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && 405 power != FB_BLANK_NORMAL) { 406 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); 407 return -EINVAL; 408 } 409 410 return ams369fg06_power(lcd, power); 411 } 412 413 static int ams369fg06_get_brightness(struct backlight_device *bd) 414 { 415 return bd->props.brightness; 416 } 417 418 static int ams369fg06_set_brightness(struct backlight_device *bd) 419 { 420 int ret = 0; 421 int brightness = bd->props.brightness; 422 struct ams369fg06 *lcd = bl_get_data(bd); 423 424 if (brightness < MIN_BRIGHTNESS || 425 brightness > bd->props.max_brightness) { 426 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n", 427 MIN_BRIGHTNESS, MAX_BRIGHTNESS); 428 return -EINVAL; 429 } 430 431 ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness); 432 if (ret) { 433 dev_err(&bd->dev, "lcd brightness setting failed.\n"); 434 return -EIO; 435 } 436 437 return ret; 438 } 439 440 static struct lcd_ops ams369fg06_lcd_ops = { 441 .get_power = ams369fg06_get_power, 442 .set_power = ams369fg06_set_power, 443 }; 444 445 static const struct backlight_ops ams369fg06_backlight_ops = { 446 .get_brightness = ams369fg06_get_brightness, 447 .update_status = ams369fg06_set_brightness, 448 }; 449 450 static int ams369fg06_probe(struct spi_device *spi) 451 { 452 int ret = 0; 453 struct ams369fg06 *lcd = NULL; 454 struct lcd_device *ld = NULL; 455 struct backlight_device *bd = NULL; 456 struct backlight_properties props; 457 458 lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL); 459 if (!lcd) 460 return -ENOMEM; 461 462 /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */ 463 spi->bits_per_word = 16; 464 465 ret = spi_setup(spi); 466 if (ret < 0) { 467 dev_err(&spi->dev, "spi setup failed.\n"); 468 return ret; 469 } 470 471 lcd->spi = spi; 472 lcd->dev = &spi->dev; 473 474 lcd->lcd_pd = dev_get_platdata(&spi->dev); 475 if (!lcd->lcd_pd) { 476 dev_err(&spi->dev, "platform data is NULL\n"); 477 return -EINVAL; 478 } 479 480 ld = devm_lcd_device_register(&spi->dev, "ams369fg06", &spi->dev, lcd, 481 &ams369fg06_lcd_ops); 482 if (IS_ERR(ld)) 483 return PTR_ERR(ld); 484 485 lcd->ld = ld; 486 487 memset(&props, 0, sizeof(struct backlight_properties)); 488 props.type = BACKLIGHT_RAW; 489 props.max_brightness = MAX_BRIGHTNESS; 490 491 bd = devm_backlight_device_register(&spi->dev, "ams369fg06-bl", 492 &spi->dev, lcd, 493 &ams369fg06_backlight_ops, &props); 494 if (IS_ERR(bd)) 495 return PTR_ERR(bd); 496 497 bd->props.brightness = DEFAULT_BRIGHTNESS; 498 lcd->bd = bd; 499 500 if (!lcd->lcd_pd->lcd_enabled) { 501 /* 502 * if lcd panel was off from bootloader then 503 * current lcd status is powerdown and then 504 * it enables lcd panel. 505 */ 506 lcd->power = FB_BLANK_POWERDOWN; 507 508 ams369fg06_power(lcd, FB_BLANK_UNBLANK); 509 } else { 510 lcd->power = FB_BLANK_UNBLANK; 511 } 512 513 spi_set_drvdata(spi, lcd); 514 515 dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n"); 516 517 return 0; 518 } 519 520 static int ams369fg06_remove(struct spi_device *spi) 521 { 522 struct ams369fg06 *lcd = spi_get_drvdata(spi); 523 524 ams369fg06_power(lcd, FB_BLANK_POWERDOWN); 525 return 0; 526 } 527 528 #ifdef CONFIG_PM_SLEEP 529 static int ams369fg06_suspend(struct device *dev) 530 { 531 struct ams369fg06 *lcd = dev_get_drvdata(dev); 532 533 dev_dbg(dev, "lcd->power = %d\n", lcd->power); 534 535 /* 536 * when lcd panel is suspend, lcd panel becomes off 537 * regardless of status. 538 */ 539 return ams369fg06_power(lcd, FB_BLANK_POWERDOWN); 540 } 541 542 static int ams369fg06_resume(struct device *dev) 543 { 544 struct ams369fg06 *lcd = dev_get_drvdata(dev); 545 546 lcd->power = FB_BLANK_POWERDOWN; 547 548 return ams369fg06_power(lcd, FB_BLANK_UNBLANK); 549 } 550 #endif 551 552 static SIMPLE_DEV_PM_OPS(ams369fg06_pm_ops, ams369fg06_suspend, 553 ams369fg06_resume); 554 555 static void ams369fg06_shutdown(struct spi_device *spi) 556 { 557 struct ams369fg06 *lcd = spi_get_drvdata(spi); 558 559 ams369fg06_power(lcd, FB_BLANK_POWERDOWN); 560 } 561 562 static struct spi_driver ams369fg06_driver = { 563 .driver = { 564 .name = "ams369fg06", 565 .owner = THIS_MODULE, 566 .pm = &ams369fg06_pm_ops, 567 }, 568 .probe = ams369fg06_probe, 569 .remove = ams369fg06_remove, 570 .shutdown = ams369fg06_shutdown, 571 }; 572 573 module_spi_driver(ams369fg06_driver); 574 575 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); 576 MODULE_DESCRIPTION("ams369fg06 LCD Driver"); 577 MODULE_LICENSE("GPL"); 578