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