Merge tag 'reset-for-v5.3' of git://git.pengutronix.de/git/pza/linux into arm/drivers
[sfrench/cifs-2.6.git] / drivers / video / backlight / ams369fg06.c
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");