spi: sprd: adi: Add a reset reason for watchdog mode
[linux/fpc-iii.git] / drivers / video / backlight / ams369fg06.c
blob94ccb90424409bf71237ed284fb89974a7e5eb81
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * ams369fg06 AMOLED LCD panel driver.
5 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
6 * Author: Jingoo Han <jg1.han@samsung.com>
8 * Derived from drivers/video/s6e63m0.c
9 */
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>
20 #define SLEEPMSEC 0x1000
21 #define ENDDEF 0x2000
22 #define DEFMASK 0xFF00
23 #define COMMAND_ONLY 0xFE
24 #define DATA_ONLY 0xFF
26 #define MAX_GAMMA_LEVEL 5
27 #define GAMMA_TABLE_COUNT 21
29 #define MIN_BRIGHTNESS 0
30 #define MAX_BRIGHTNESS 255
31 #define DEFAULT_BRIGHTNESS 150
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;
42 static const unsigned short seq_display_on[] = {
43 0x14, 0x03,
44 ENDDEF, 0x0000
47 static const unsigned short seq_display_off[] = {
48 0x14, 0x00,
49 ENDDEF, 0x0000
52 static const unsigned short seq_stand_by_on[] = {
53 0x1D, 0xA1,
54 SLEEPMSEC, 200,
55 ENDDEF, 0x0000
58 static const unsigned short seq_stand_by_off[] = {
59 0x1D, 0xA0,
60 SLEEPMSEC, 250,
61 ENDDEF, 0x0000
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,
74 0xef, 0xd0,
75 DATA_ONLY, 0xe8,
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,
100 0x17, 0x22,
101 0x18, 0x33,
102 0x19, 0x03,
103 0x1a, 0x01,
104 0x22, 0xa4,
105 0x23, 0x00,
106 0x26, 0xa0,
108 0x1d, 0xa0,
109 SLEEPMSEC, 300,
111 0x14, 0x03,
113 ENDDEF, 0x0000
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,
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,
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,
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,
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,
147 struct ams369fg06_gamma {
148 unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
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,
159 static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data)
161 u16 buf[1];
162 struct spi_message msg;
164 struct spi_transfer xfer = {
165 .len = 2,
166 .tx_buf = buf,
169 buf[0] = (addr << 8) | data;
171 spi_message_init(&msg);
172 spi_message_add_tail(&xfer, &msg);
174 return spi_sync(lcd->spi, &msg);
177 static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
178 unsigned char command)
180 int ret = 0;
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);
187 return ret;
190 static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
191 const unsigned short *wbuf)
193 int ret = 0, i = 0;
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]);
203 i += 2;
206 return ret;
209 static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
210 const unsigned int *gamma)
212 unsigned int i = 0;
213 int ret = 0;
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;
225 gamma_err:
226 return ret;
229 static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
231 int ret = 0;
232 int gamma = 0;
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;
245 ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
247 return ret;
250 static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
252 int ret, i;
253 static const unsigned short *init_seq[] = {
254 seq_setting,
255 seq_stand_by_off,
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;
264 return ret;
267 static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
269 int ret, i;
270 static const unsigned short *init_seq[] = {
271 seq_stand_by_off,
272 seq_display_on,
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;
281 return ret;
284 static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
286 int ret, i;
288 static const unsigned short *init_seq[] = {
289 seq_display_off,
290 seq_stand_by_on,
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;
299 return ret;
302 static int ams369fg06_power_is_on(int power)
304 return power <= FB_BLANK_NORMAL;
307 static int ams369fg06_power_on(struct ams369fg06 *lcd)
309 int ret = 0;
310 struct lcd_platform_data *pd;
311 struct backlight_device *bd;
313 pd = lcd->lcd_pd;
314 bd = lcd->bd;
316 if (pd->power_on) {
317 pd->power_on(lcd->ld, 1);
318 msleep(pd->power_on_delay);
321 if (!pd->reset) {
322 dev_err(lcd->dev, "reset is NULL.\n");
323 return -EINVAL;
326 pd->reset(lcd->ld);
327 msleep(pd->reset_delay);
329 ret = ams369fg06_ldi_init(lcd);
330 if (ret) {
331 dev_err(lcd->dev, "failed to initialize ldi.\n");
332 return ret;
335 ret = ams369fg06_ldi_enable(lcd);
336 if (ret) {
337 dev_err(lcd->dev, "failed to enable ldi.\n");
338 return ret;
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;
348 return 0;
351 static int ams369fg06_power_off(struct ams369fg06 *lcd)
353 int ret;
354 struct lcd_platform_data *pd;
356 pd = lcd->lcd_pd;
358 ret = ams369fg06_ldi_disable(lcd);
359 if (ret) {
360 dev_err(lcd->dev, "lcd setting failed.\n");
361 return -EIO;
364 msleep(pd->power_off_delay);
366 if (pd->power_on)
367 pd->power_on(lcd->ld, 0);
369 return 0;
372 static int ams369fg06_power(struct ams369fg06 *lcd, int power)
374 int ret = 0;
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);
383 if (!ret)
384 lcd->power = power;
386 return ret;
389 static int ams369fg06_get_power(struct lcd_device *ld)
391 struct ams369fg06 *lcd = lcd_get_data(ld);
393 return lcd->power;
396 static int ams369fg06_set_power(struct lcd_device *ld, int power)
398 struct ams369fg06 *lcd = lcd_get_data(ld);
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;
406 return ams369fg06_power(lcd, power);
409 static int ams369fg06_set_brightness(struct backlight_device *bd)
411 int ret = 0;
412 int brightness = bd->props.brightness;
413 struct ams369fg06 *lcd = bl_get_data(bd);
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;
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;
428 return ret;
431 static struct lcd_ops ams369fg06_lcd_ops = {
432 .get_power = ams369fg06_get_power,
433 .set_power = ams369fg06_set_power,
436 static const struct backlight_ops ams369fg06_backlight_ops = {
437 .update_status = ams369fg06_set_brightness,
440 static int ams369fg06_probe(struct spi_device *spi)
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;
448 lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL);
449 if (!lcd)
450 return -ENOMEM;
452 /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
453 spi->bits_per_word = 16;
455 ret = spi_setup(spi);
456 if (ret < 0) {
457 dev_err(&spi->dev, "spi setup failed.\n");
458 return ret;
461 lcd->spi = spi;
462 lcd->dev = &spi->dev;
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;
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);
475 lcd->ld = ld;
477 memset(&props, 0, sizeof(struct backlight_properties));
478 props.type = BACKLIGHT_RAW;
479 props.max_brightness = MAX_BRIGHTNESS;
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);
487 bd->props.brightness = DEFAULT_BRIGHTNESS;
488 lcd->bd = bd;
490 if (!lcd->lcd_pd->lcd_enabled) {
492 * if lcd panel was off from bootloader then
493 * current lcd status is powerdown and then
494 * it enables lcd panel.
496 lcd->power = FB_BLANK_POWERDOWN;
498 ams369fg06_power(lcd, FB_BLANK_UNBLANK);
499 } else {
500 lcd->power = FB_BLANK_UNBLANK;
503 spi_set_drvdata(spi, lcd);
505 dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
507 return 0;
510 static int ams369fg06_remove(struct spi_device *spi)
512 struct ams369fg06 *lcd = spi_get_drvdata(spi);
514 ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
515 return 0;
518 #ifdef CONFIG_PM_SLEEP
519 static int ams369fg06_suspend(struct device *dev)
521 struct ams369fg06 *lcd = dev_get_drvdata(dev);
523 dev_dbg(dev, "lcd->power = %d\n", lcd->power);
526 * when lcd panel is suspend, lcd panel becomes off
527 * regardless of status.
529 return ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
532 static int ams369fg06_resume(struct device *dev)
534 struct ams369fg06 *lcd = dev_get_drvdata(dev);
536 lcd->power = FB_BLANK_POWERDOWN;
538 return ams369fg06_power(lcd, FB_BLANK_UNBLANK);
540 #endif
542 static SIMPLE_DEV_PM_OPS(ams369fg06_pm_ops, ams369fg06_suspend,
543 ams369fg06_resume);
545 static void ams369fg06_shutdown(struct spi_device *spi)
547 struct ams369fg06 *lcd = spi_get_drvdata(spi);
549 ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
552 static struct spi_driver ams369fg06_driver = {
553 .driver = {
554 .name = "ams369fg06",
555 .pm = &ams369fg06_pm_ops,
557 .probe = ams369fg06_probe,
558 .remove = ams369fg06_remove,
559 .shutdown = ams369fg06_shutdown,
562 module_spi_driver(ams369fg06_driver);
564 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
565 MODULE_DESCRIPTION("ams369fg06 LCD Driver");
566 MODULE_LICENSE("GPL");