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