root/drivers/video/backlight/ams369fg06.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ams369fg06_spi_write_byte
  2. ams369fg06_spi_write
  3. ams369fg06_panel_send_sequence
  4. _ams369fg06_gamma_ctl
  5. ams369fg06_gamma_ctl
  6. ams369fg06_ldi_init
  7. ams369fg06_ldi_enable
  8. ams369fg06_ldi_disable
  9. ams369fg06_power_is_on
  10. ams369fg06_power_on
  11. ams369fg06_power_off
  12. ams369fg06_power
  13. ams369fg06_get_power
  14. ams369fg06_set_power
  15. ams369fg06_set_brightness
  16. ams369fg06_probe
  17. ams369fg06_remove
  18. ams369fg06_suspend
  19. ams369fg06_resume
  20. ams369fg06_shutdown

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

/* [<][>][^][v][top][bottom][index][help] */