blob: a3128c9cb7ad6b64999eb6e7bffe6a2898c996e0 [file] [log] [blame]
InKi Daeee378a52010-05-24 12:21:36 -07001/*
2 * S6E63M0 AMOLED LCD panel driver.
3 *
4 * Author: InKi Dae <inki.dae@samsung.com>
5 *
6 * Derived from drivers/video/omap/lcd-apollon.c
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23#include <linux/wait.h>
24#include <linux/fb.h>
25#include <linux/delay.h>
26#include <linux/gpio.h>
27#include <linux/spi/spi.h>
28#include <linux/irq.h>
29#include <linux/interrupt.h>
30#include <linux/kernel.h>
31#include <linux/lcd.h>
32#include <linux/backlight.h>
33
34#include "s6e63m0_gamma.h"
35
36#define SLEEPMSEC 0x1000
37#define ENDDEF 0x2000
38#define DEFMASK 0xFF00
39#define COMMAND_ONLY 0xFE
40#define DATA_ONLY 0xFF
41
42#define MIN_BRIGHTNESS 0
43#define MAX_BRIGHTNESS 10
44
45#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
46
47struct s6e63m0 {
48 struct device *dev;
49 struct spi_device *spi;
50 unsigned int power;
51 unsigned int current_brightness;
52 unsigned int gamma_mode;
53 unsigned int gamma_table_count;
54 struct lcd_device *ld;
55 struct backlight_device *bd;
56 struct lcd_platform_data *lcd_pd;
57};
58
59static const unsigned short SEQ_PANEL_CONDITION_SET[] = {
60 0xF8, 0x01,
61 DATA_ONLY, 0x27,
62 DATA_ONLY, 0x27,
63 DATA_ONLY, 0x07,
64 DATA_ONLY, 0x07,
65 DATA_ONLY, 0x54,
66 DATA_ONLY, 0x9f,
67 DATA_ONLY, 0x63,
68 DATA_ONLY, 0x86,
69 DATA_ONLY, 0x1a,
70 DATA_ONLY, 0x33,
71 DATA_ONLY, 0x0d,
72 DATA_ONLY, 0x00,
73 DATA_ONLY, 0x00,
74
75 ENDDEF, 0x0000
76};
77
78static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = {
79 0xf2, 0x02,
80 DATA_ONLY, 0x03,
81 DATA_ONLY, 0x1c,
82 DATA_ONLY, 0x10,
83 DATA_ONLY, 0x10,
84
85 0xf7, 0x03,
86 DATA_ONLY, 0x00,
87 DATA_ONLY, 0x00,
88
89 ENDDEF, 0x0000
90};
91
92static const unsigned short SEQ_GAMMA_SETTING[] = {
93 0xfa, 0x00,
94 DATA_ONLY, 0x18,
95 DATA_ONLY, 0x08,
96 DATA_ONLY, 0x24,
97 DATA_ONLY, 0x64,
98 DATA_ONLY, 0x56,
99 DATA_ONLY, 0x33,
100 DATA_ONLY, 0xb6,
101 DATA_ONLY, 0xba,
102 DATA_ONLY, 0xa8,
103 DATA_ONLY, 0xac,
104 DATA_ONLY, 0xb1,
105 DATA_ONLY, 0x9d,
106 DATA_ONLY, 0xc1,
107 DATA_ONLY, 0xc1,
108 DATA_ONLY, 0xb7,
109 DATA_ONLY, 0x00,
110 DATA_ONLY, 0x9c,
111 DATA_ONLY, 0x00,
112 DATA_ONLY, 0x9f,
113 DATA_ONLY, 0x00,
114 DATA_ONLY, 0xd6,
115
116 0xfa, 0x01,
117
118 ENDDEF, 0x0000
119};
120
121static const unsigned short SEQ_ETC_CONDITION_SET[] = {
122 0xf6, 0x00,
123 DATA_ONLY, 0x8c,
124 DATA_ONLY, 0x07,
125
126 0xb3, 0xc,
127
128 0xb5, 0x2c,
129 DATA_ONLY, 0x12,
130 DATA_ONLY, 0x0c,
131 DATA_ONLY, 0x0a,
132 DATA_ONLY, 0x10,
133 DATA_ONLY, 0x0e,
134 DATA_ONLY, 0x17,
135 DATA_ONLY, 0x13,
136 DATA_ONLY, 0x1f,
137 DATA_ONLY, 0x1a,
138 DATA_ONLY, 0x2a,
139 DATA_ONLY, 0x24,
140 DATA_ONLY, 0x1f,
141 DATA_ONLY, 0x1b,
142 DATA_ONLY, 0x1a,
143 DATA_ONLY, 0x17,
144
145 DATA_ONLY, 0x2b,
146 DATA_ONLY, 0x26,
147 DATA_ONLY, 0x22,
148 DATA_ONLY, 0x20,
149 DATA_ONLY, 0x3a,
150 DATA_ONLY, 0x34,
151 DATA_ONLY, 0x30,
152 DATA_ONLY, 0x2c,
153 DATA_ONLY, 0x29,
154 DATA_ONLY, 0x26,
155 DATA_ONLY, 0x25,
156 DATA_ONLY, 0x23,
157 DATA_ONLY, 0x21,
158 DATA_ONLY, 0x20,
159 DATA_ONLY, 0x1e,
160 DATA_ONLY, 0x1e,
161
162 0xb6, 0x00,
163 DATA_ONLY, 0x00,
164 DATA_ONLY, 0x11,
165 DATA_ONLY, 0x22,
166 DATA_ONLY, 0x33,
167 DATA_ONLY, 0x44,
168 DATA_ONLY, 0x44,
169 DATA_ONLY, 0x44,
170
171 DATA_ONLY, 0x55,
172 DATA_ONLY, 0x55,
173 DATA_ONLY, 0x66,
174 DATA_ONLY, 0x66,
175 DATA_ONLY, 0x66,
176 DATA_ONLY, 0x66,
177 DATA_ONLY, 0x66,
178 DATA_ONLY, 0x66,
179
180 0xb7, 0x2c,
181 DATA_ONLY, 0x12,
182 DATA_ONLY, 0x0c,
183 DATA_ONLY, 0x0a,
184 DATA_ONLY, 0x10,
185 DATA_ONLY, 0x0e,
186 DATA_ONLY, 0x17,
187 DATA_ONLY, 0x13,
188 DATA_ONLY, 0x1f,
189 DATA_ONLY, 0x1a,
190 DATA_ONLY, 0x2a,
191 DATA_ONLY, 0x24,
192 DATA_ONLY, 0x1f,
193 DATA_ONLY, 0x1b,
194 DATA_ONLY, 0x1a,
195 DATA_ONLY, 0x17,
196
197 DATA_ONLY, 0x2b,
198 DATA_ONLY, 0x26,
199 DATA_ONLY, 0x22,
200 DATA_ONLY, 0x20,
201 DATA_ONLY, 0x3a,
202 DATA_ONLY, 0x34,
203 DATA_ONLY, 0x30,
204 DATA_ONLY, 0x2c,
205 DATA_ONLY, 0x29,
206 DATA_ONLY, 0x26,
207 DATA_ONLY, 0x25,
208 DATA_ONLY, 0x23,
209 DATA_ONLY, 0x21,
210 DATA_ONLY, 0x20,
211 DATA_ONLY, 0x1e,
212 DATA_ONLY, 0x1e,
213
214 0xb8, 0x00,
215 DATA_ONLY, 0x00,
216 DATA_ONLY, 0x11,
217 DATA_ONLY, 0x22,
218 DATA_ONLY, 0x33,
219 DATA_ONLY, 0x44,
220 DATA_ONLY, 0x44,
221 DATA_ONLY, 0x44,
222
223 DATA_ONLY, 0x55,
224 DATA_ONLY, 0x55,
225 DATA_ONLY, 0x66,
226 DATA_ONLY, 0x66,
227 DATA_ONLY, 0x66,
228 DATA_ONLY, 0x66,
229 DATA_ONLY, 0x66,
230 DATA_ONLY, 0x66,
231
232 0xb9, 0x2c,
233 DATA_ONLY, 0x12,
234 DATA_ONLY, 0x0c,
235 DATA_ONLY, 0x0a,
236 DATA_ONLY, 0x10,
237 DATA_ONLY, 0x0e,
238 DATA_ONLY, 0x17,
239 DATA_ONLY, 0x13,
240 DATA_ONLY, 0x1f,
241 DATA_ONLY, 0x1a,
242 DATA_ONLY, 0x2a,
243 DATA_ONLY, 0x24,
244 DATA_ONLY, 0x1f,
245 DATA_ONLY, 0x1b,
246 DATA_ONLY, 0x1a,
247 DATA_ONLY, 0x17,
248
249 DATA_ONLY, 0x2b,
250 DATA_ONLY, 0x26,
251 DATA_ONLY, 0x22,
252 DATA_ONLY, 0x20,
253 DATA_ONLY, 0x3a,
254 DATA_ONLY, 0x34,
255 DATA_ONLY, 0x30,
256 DATA_ONLY, 0x2c,
257 DATA_ONLY, 0x29,
258 DATA_ONLY, 0x26,
259 DATA_ONLY, 0x25,
260 DATA_ONLY, 0x23,
261 DATA_ONLY, 0x21,
262 DATA_ONLY, 0x20,
263 DATA_ONLY, 0x1e,
264 DATA_ONLY, 0x1e,
265
266 0xba, 0x00,
267 DATA_ONLY, 0x00,
268 DATA_ONLY, 0x11,
269 DATA_ONLY, 0x22,
270 DATA_ONLY, 0x33,
271 DATA_ONLY, 0x44,
272 DATA_ONLY, 0x44,
273 DATA_ONLY, 0x44,
274
275 DATA_ONLY, 0x55,
276 DATA_ONLY, 0x55,
277 DATA_ONLY, 0x66,
278 DATA_ONLY, 0x66,
279 DATA_ONLY, 0x66,
280 DATA_ONLY, 0x66,
281 DATA_ONLY, 0x66,
282 DATA_ONLY, 0x66,
283
284 0xc1, 0x4d,
285 DATA_ONLY, 0x96,
286 DATA_ONLY, 0x1d,
287 DATA_ONLY, 0x00,
288 DATA_ONLY, 0x00,
289 DATA_ONLY, 0x01,
290 DATA_ONLY, 0xdf,
291 DATA_ONLY, 0x00,
292 DATA_ONLY, 0x00,
293 DATA_ONLY, 0x03,
294 DATA_ONLY, 0x1f,
295 DATA_ONLY, 0x00,
296 DATA_ONLY, 0x00,
297 DATA_ONLY, 0x00,
298 DATA_ONLY, 0x00,
299 DATA_ONLY, 0x00,
300 DATA_ONLY, 0x00,
301 DATA_ONLY, 0x00,
302 DATA_ONLY, 0x00,
303 DATA_ONLY, 0x03,
304 DATA_ONLY, 0x06,
305 DATA_ONLY, 0x09,
306 DATA_ONLY, 0x0d,
307 DATA_ONLY, 0x0f,
308 DATA_ONLY, 0x12,
309 DATA_ONLY, 0x15,
310 DATA_ONLY, 0x18,
311
312 0xb2, 0x10,
313 DATA_ONLY, 0x10,
314 DATA_ONLY, 0x0b,
315 DATA_ONLY, 0x05,
316
317 ENDDEF, 0x0000
318};
319
320static const unsigned short SEQ_ACL_ON[] = {
321 /* ACL on */
322 0xc0, 0x01,
323
324 ENDDEF, 0x0000
325};
326
327static const unsigned short SEQ_ACL_OFF[] = {
328 /* ACL off */
329 0xc0, 0x00,
330
331 ENDDEF, 0x0000
332};
333
334static const unsigned short SEQ_ELVSS_ON[] = {
335 /* ELVSS on */
336 0xb1, 0x0b,
337
338 ENDDEF, 0x0000
339};
340
341static const unsigned short SEQ_ELVSS_OFF[] = {
342 /* ELVSS off */
343 0xb1, 0x0a,
344
345 ENDDEF, 0x0000
346};
347
348static const unsigned short SEQ_STAND_BY_OFF[] = {
349 0x11, COMMAND_ONLY,
350
351 ENDDEF, 0x0000
352};
353
354static const unsigned short SEQ_STAND_BY_ON[] = {
355 0x10, COMMAND_ONLY,
356
357 ENDDEF, 0x0000
358};
359
360static const unsigned short SEQ_DISPLAY_ON[] = {
361 0x29, COMMAND_ONLY,
362
363 ENDDEF, 0x0000
364};
365
366
367static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
368{
369 u16 buf[1];
370 struct spi_message msg;
371
372 struct spi_transfer xfer = {
373 .len = 2,
374 .tx_buf = buf,
375 };
376
377 buf[0] = (addr << 8) | data;
378
379 spi_message_init(&msg);
380 spi_message_add_tail(&xfer, &msg);
381
382 return spi_sync(lcd->spi, &msg);
383}
384
385static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
386 unsigned char command)
387{
388 int ret = 0;
389
390 if (address != DATA_ONLY)
391 ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
392 if (command != COMMAND_ONLY)
393 ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
394
395 return ret;
396}
397
398static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
399 const unsigned short *wbuf)
400{
401 int ret = 0, i = 0;
402
403 while ((wbuf[i] & DEFMASK) != ENDDEF) {
404 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
405 ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
406 if (ret)
407 break;
408 } else
409 udelay(wbuf[i+1]*1000);
410 i += 2;
411 }
412
413 return ret;
414}
415
416static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
417{
418 unsigned int i = 0;
419 int ret = 0;
420
421 /* disable gamma table updating. */
422 ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
423 if (ret) {
424 dev_err(lcd->dev, "failed to disable gamma table updating.\n");
425 goto gamma_err;
426 }
427
428 for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
429 ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
430 if (ret) {
431 dev_err(lcd->dev, "failed to set gamma table.\n");
432 goto gamma_err;
433 }
434 }
435
436 /* update gamma table. */
437 ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
438 if (ret)
439 dev_err(lcd->dev, "failed to update gamma table.\n");
440
441gamma_err:
442 return ret;
443}
444
445static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
446{
447 int ret = 0;
448
449 ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
450
451 return ret;
452}
453
454
455static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
456{
457 int ret, i;
458 const unsigned short *init_seq[] = {
459 SEQ_PANEL_CONDITION_SET,
460 SEQ_DISPLAY_CONDITION_SET,
461 SEQ_GAMMA_SETTING,
462 SEQ_ETC_CONDITION_SET,
463 SEQ_ACL_ON,
464 SEQ_ELVSS_ON,
465 };
466
467 for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
468 ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
469 if (ret)
470 break;
471 }
472
473 return ret;
474}
475
476static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
477{
478 int ret = 0, i;
479 const unsigned short *enable_seq[] = {
480 SEQ_STAND_BY_OFF,
481 SEQ_DISPLAY_ON,
482 };
483
484 for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
485 ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
486 if (ret)
487 break;
488 }
489
490 return ret;
491}
492
493static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
494{
495 int ret;
496
497 ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON);
498
499 return ret;
500}
501
502static int s6e63m0_power_on(struct s6e63m0 *lcd)
503{
504 int ret = 0;
505 struct lcd_platform_data *pd = NULL;
506 struct backlight_device *bd = NULL;
507
508 pd = lcd->lcd_pd;
509 if (!pd) {
510 dev_err(lcd->dev, "platform data is NULL.\n");
511 return -EFAULT;
512 }
513
514 bd = lcd->bd;
515 if (!bd) {
516 dev_err(lcd->dev, "backlight device is NULL.\n");
517 return -EFAULT;
518 }
519
520 if (!pd->power_on) {
521 dev_err(lcd->dev, "power_on is NULL.\n");
522 return -EFAULT;
523 } else {
524 pd->power_on(lcd->ld, 1);
525 mdelay(pd->power_on_delay);
526 }
527
528 if (!pd->reset) {
529 dev_err(lcd->dev, "reset is NULL.\n");
530 return -EFAULT;
531 } else {
532 pd->reset(lcd->ld);
533 mdelay(pd->reset_delay);
534 }
535
536 ret = s6e63m0_ldi_init(lcd);
537 if (ret) {
538 dev_err(lcd->dev, "failed to initialize ldi.\n");
539 return ret;
540 }
541
542 ret = s6e63m0_ldi_enable(lcd);
543 if (ret) {
544 dev_err(lcd->dev, "failed to enable ldi.\n");
545 return ret;
546 }
547
548 /* set brightness to current value after power on or resume. */
549 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
550 if (ret) {
551 dev_err(lcd->dev, "lcd gamma setting failed.\n");
552 return ret;
553 }
554
555 return 0;
556}
557
558static int s6e63m0_power_off(struct s6e63m0 *lcd)
559{
560 int ret = 0;
561 struct lcd_platform_data *pd = NULL;
562
563 pd = lcd->lcd_pd;
564 if (!pd) {
565 dev_err(lcd->dev, "platform data is NULL.\n");
566 return -EFAULT;
567 }
568
569 ret = s6e63m0_ldi_disable(lcd);
570 if (ret) {
571 dev_err(lcd->dev, "lcd setting failed.\n");
572 return -EIO;
573 }
574
575 mdelay(pd->power_off_delay);
576
577 if (!pd->power_on) {
578 dev_err(lcd->dev, "power_on is NULL.\n");
579 return -EFAULT;
580 } else
581 pd->power_on(lcd->ld, 0);
582
583 return 0;
584}
585
586static int s6e63m0_power(struct s6e63m0 *lcd, int power)
587{
588 int ret = 0;
589
590 if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
591 ret = s6e63m0_power_on(lcd);
592 else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
593 ret = s6e63m0_power_off(lcd);
594
595 if (!ret)
596 lcd->power = power;
597
598 return ret;
599}
600
601static int s6e63m0_set_power(struct lcd_device *ld, int power)
602{
603 struct s6e63m0 *lcd = lcd_get_data(ld);
604
605 if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
606 power != FB_BLANK_NORMAL) {
607 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
608 return -EINVAL;
609 }
610
611 return s6e63m0_power(lcd, power);
612}
613
614static int s6e63m0_get_power(struct lcd_device *ld)
615{
616 struct s6e63m0 *lcd = lcd_get_data(ld);
617
618 return lcd->power;
619}
620
621static int s6e63m0_get_brightness(struct backlight_device *bd)
622{
623 return bd->props.brightness;
624}
625
626static int s6e63m0_set_brightness(struct backlight_device *bd)
627{
628 int ret = 0, brightness = bd->props.brightness;
629 struct s6e63m0 *lcd = bl_get_data(bd);
630
631 if (brightness < MIN_BRIGHTNESS ||
632 brightness > bd->props.max_brightness) {
633 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
634 MIN_BRIGHTNESS, MAX_BRIGHTNESS);
635 return -EINVAL;
636 }
637
638 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
639 if (ret) {
640 dev_err(&bd->dev, "lcd brightness setting failed.\n");
641 return -EIO;
642 }
643
644 return ret;
645}
646
647static struct lcd_ops s6e63m0_lcd_ops = {
648 .set_power = s6e63m0_set_power,
649 .get_power = s6e63m0_get_power,
650};
651
652static const struct backlight_ops s6e63m0_backlight_ops = {
653 .get_brightness = s6e63m0_get_brightness,
654 .update_status = s6e63m0_set_brightness,
655};
656
657static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
658 struct device_attribute *attr, char *buf)
659{
660 struct s6e63m0 *lcd = dev_get_drvdata(dev);
661 char temp[10];
662
663 switch (lcd->gamma_mode) {
664 case 0:
665 sprintf(temp, "2.2 mode\n");
666 strcat(buf, temp);
667 break;
668 case 1:
669 sprintf(temp, "1.9 mode\n");
670 strcat(buf, temp);
671 break;
672 case 2:
673 sprintf(temp, "1.7 mode\n");
674 strcat(buf, temp);
675 break;
676 default:
677 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
678 break;
679 }
680
681 return strlen(buf);
682}
683
684static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
685 struct device_attribute *attr,
686 const char *buf, size_t len)
687{
688 struct s6e63m0 *lcd = dev_get_drvdata(dev);
689 struct backlight_device *bd = NULL;
690 int brightness, rc;
691
692 rc = strict_strtoul(buf, 0, (unsigned long *)&lcd->gamma_mode);
693 if (rc < 0)
694 return rc;
695
696 bd = lcd->bd;
697
698 brightness = bd->props.brightness;
699
700 switch (lcd->gamma_mode) {
701 case 0:
702 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
703 break;
704 case 1:
705 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
706 break;
707 case 2:
708 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
709 break;
710 default:
711 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
712 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
713 break;
714 }
715 return len;
716}
717
718static DEVICE_ATTR(gamma_mode, 0644,
719 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
720
721static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
722 struct device_attribute *attr, char *buf)
723{
724 struct s6e63m0 *lcd = dev_get_drvdata(dev);
725 char temp[3];
726
727 sprintf(temp, "%d\n", lcd->gamma_table_count);
728 strcpy(buf, temp);
729
730 return strlen(buf);
731}
732static DEVICE_ATTR(gamma_table, 0644,
733 s6e63m0_sysfs_show_gamma_table, NULL);
734
735static int __init s6e63m0_probe(struct spi_device *spi)
736{
737 int ret = 0;
738 struct s6e63m0 *lcd = NULL;
739 struct lcd_device *ld = NULL;
740 struct backlight_device *bd = NULL;
741
742 lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
743 if (!lcd)
744 return -ENOMEM;
745
746 /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
747 spi->bits_per_word = 9;
748
749 ret = spi_setup(spi);
750 if (ret < 0) {
751 dev_err(&spi->dev, "spi setup failed.\n");
752 goto out_free_lcd;
753 }
754
755 lcd->spi = spi;
756 lcd->dev = &spi->dev;
757
758 lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
759 if (!lcd->lcd_pd) {
760 dev_err(&spi->dev, "platform data is NULL.\n");
761 goto out_free_lcd;
762 }
763
764 ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
765 if (IS_ERR(ld)) {
766 ret = PTR_ERR(ld);
767 goto out_free_lcd;
768 }
769
770 lcd->ld = ld;
771
772 bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
773 &s6e63m0_backlight_ops, NULL);
774 if (IS_ERR(bd)) {
775 ret = PTR_ERR(bd);
776 goto out_lcd_unregister;
777 }
778
779 bd->props.max_brightness = MAX_BRIGHTNESS;
780 bd->props.brightness = MAX_BRIGHTNESS;
781 lcd->bd = bd;
782
783 /*
784 * it gets gamma table count available so it gets user
785 * know that.
786 */
787 lcd->gamma_table_count =
788 sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
789
790 ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
791 if (ret < 0)
792 dev_err(&(spi->dev), "failed to add sysfs entries\n");
793
794 ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
795 if (ret < 0)
796 dev_err(&(spi->dev), "failed to add sysfs entries\n");
797
798 /*
799 * if lcd panel was on from bootloader like u-boot then
800 * do not lcd on.
801 */
802 if (!lcd->lcd_pd->lcd_enabled) {
803 /*
804 * if lcd panel was off from bootloader then
805 * current lcd status is powerdown and then
806 * it enables lcd panel.
807 */
808 lcd->power = FB_BLANK_POWERDOWN;
809
810 s6e63m0_power(lcd, FB_BLANK_UNBLANK);
811 } else
812 lcd->power = FB_BLANK_UNBLANK;
813
814 dev_set_drvdata(&spi->dev, lcd);
815
816 dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
817
818 return 0;
819
820out_lcd_unregister:
821 lcd_device_unregister(ld);
822out_free_lcd:
823 kfree(lcd);
824 return ret;
825}
826
827static int __devexit s6e63m0_remove(struct spi_device *spi)
828{
829 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
830
831 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
832 lcd_device_unregister(lcd->ld);
833 kfree(lcd);
834
835 return 0;
836}
837
838#if defined(CONFIG_PM)
839unsigned int before_power;
840
841static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
842{
843 int ret = 0;
844 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
845
846 dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
847
848 before_power = lcd->power;
849
850 /*
851 * when lcd panel is suspend, lcd panel becomes off
852 * regardless of status.
853 */
854 ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
855
856 return ret;
857}
858
859static int s6e63m0_resume(struct spi_device *spi)
860{
861 int ret = 0;
862 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
863
864 /*
865 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
866 * (at that time, before_power is FB_BLANK_UNBLANK) then
867 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
868 */
869 if (before_power == FB_BLANK_UNBLANK)
870 lcd->power = FB_BLANK_POWERDOWN;
871
872 dev_dbg(&spi->dev, "before_power = %d\n", before_power);
873
874 ret = s6e63m0_power(lcd, before_power);
875
876 return ret;
877}
878#else
879#define s6e63m0_suspend NULL
880#define s6e63m0_resume NULL
881#endif
882
883/* Power down all displays on reboot, poweroff or halt. */
884static void s6e63m0_shutdown(struct spi_device *spi)
885{
886 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
887
888 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
889}
890
891static struct spi_driver s6e63m0_driver = {
892 .driver = {
893 .name = "s6e63m0",
894 .bus = &spi_bus_type,
895 .owner = THIS_MODULE,
896 },
897 .probe = s6e63m0_probe,
898 .remove = __devexit_p(s6e63m0_remove),
899 .shutdown = s6e63m0_shutdown,
900 .suspend = s6e63m0_suspend,
901 .resume = s6e63m0_resume,
902};
903
904static int __init s6e63m0_init(void)
905{
906 return spi_register_driver(&s6e63m0_driver);
907}
908
909static void __exit s6e63m0_exit(void)
910{
911 spi_unregister_driver(&s6e63m0_driver);
912}
913
914module_init(s6e63m0_init);
915module_exit(s6e63m0_exit);
916
917MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
918MODULE_DESCRIPTION("S6E63M0 LCD Driver");
919MODULE_LICENSE("GPL");
920