OSDN Git Service

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