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