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
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
47 struct 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
59 static 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
78 static 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
92 static 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
121 static 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
320 static const unsigned short SEQ_ACL_ON[] = {
321 /* ACL on */
322 0xc0, 0x01,
323
324 ENDDEF, 0x0000
325 };
326
327 static const unsigned short SEQ_ACL_OFF[] = {
328 /* ACL off */
329 0xc0, 0x00,
330
331 ENDDEF, 0x0000
332 };
333
334 static const unsigned short SEQ_ELVSS_ON[] = {
335 /* ELVSS on */
336 0xb1, 0x0b,
337
338 ENDDEF, 0x0000
339 };
340
341 static const unsigned short SEQ_ELVSS_OFF[] = {
342 /* ELVSS off */
343 0xb1, 0x0a,
344
345 ENDDEF, 0x0000
346 };
347
348 static const unsigned short SEQ_STAND_BY_OFF[] = {
349 0x11, COMMAND_ONLY,
350
351 ENDDEF, 0x0000
352 };
353
354 static const unsigned short SEQ_STAND_BY_ON[] = {
355 0x10, COMMAND_ONLY,
356
357 ENDDEF, 0x0000
358 };
359
360 static const unsigned short SEQ_DISPLAY_ON[] = {
361 0x29, COMMAND_ONLY,
362
363 ENDDEF, 0x0000
364 };
365
366
s6e63m0_spi_write_byte(struct s6e63m0 * lcd,int addr,int data)367 static 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
s6e63m0_spi_write(struct s6e63m0 * lcd,unsigned char address,unsigned char command)385 static 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
s6e63m0_panel_send_sequence(struct s6e63m0 * lcd,const unsigned short * wbuf)398 static 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
_s6e63m0_gamma_ctl(struct s6e63m0 * lcd,const unsigned int * gamma)416 static 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
441 gamma_err:
442 return ret;
443 }
444
s6e63m0_gamma_ctl(struct s6e63m0 * lcd,int gamma)445 static 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
s6e63m0_ldi_init(struct s6e63m0 * lcd)455 static 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
s6e63m0_ldi_enable(struct s6e63m0 * lcd)476 static 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
s6e63m0_ldi_disable(struct s6e63m0 * lcd)493 static 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
s6e63m0_power_on(struct s6e63m0 * lcd)502 static 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
s6e63m0_power_off(struct s6e63m0 * lcd)558 static 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
s6e63m0_power(struct s6e63m0 * lcd,int power)586 static 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
s6e63m0_set_power(struct lcd_device * ld,int power)601 static 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
s6e63m0_get_power(struct lcd_device * ld)614 static int s6e63m0_get_power(struct lcd_device *ld)
615 {
616 struct s6e63m0 *lcd = lcd_get_data(ld);
617
618 return lcd->power;
619 }
620
s6e63m0_get_brightness(struct backlight_device * bd)621 static int s6e63m0_get_brightness(struct backlight_device *bd)
622 {
623 return bd->props.brightness;
624 }
625
s6e63m0_set_brightness(struct backlight_device * bd)626 static 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
647 static struct lcd_ops s6e63m0_lcd_ops = {
648 .set_power = s6e63m0_set_power,
649 .get_power = s6e63m0_get_power,
650 };
651
652 static const struct backlight_ops s6e63m0_backlight_ops = {
653 .get_brightness = s6e63m0_get_brightness,
654 .update_status = s6e63m0_set_brightness,
655 };
656
s6e63m0_sysfs_show_gamma_mode(struct device * dev,struct device_attribute * attr,char * buf)657 static 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
s6e63m0_sysfs_store_gamma_mode(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)684 static 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
718 static DEVICE_ATTR(gamma_mode, 0644,
719 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
720
s6e63m0_sysfs_show_gamma_table(struct device * dev,struct device_attribute * attr,char * buf)721 static 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 }
732 static DEVICE_ATTR(gamma_table, 0444,
733 s6e63m0_sysfs_show_gamma_table, NULL);
734
s6e63m0_probe(struct spi_device * spi)735 static int __devinit 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 bd->props.type = BACKLIGHT_RAW;
782 lcd->bd = bd;
783
784 /*
785 * it gets gamma table count available so it gets user
786 * know that.
787 */
788 lcd->gamma_table_count =
789 sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
790
791 ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
792 if (ret < 0)
793 dev_err(&(spi->dev), "failed to add sysfs entries\n");
794
795 ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
796 if (ret < 0)
797 dev_err(&(spi->dev), "failed to add sysfs entries\n");
798
799 /*
800 * if lcd panel was on from bootloader like u-boot then
801 * do not lcd on.
802 */
803 if (!lcd->lcd_pd->lcd_enabled) {
804 /*
805 * if lcd panel was off from bootloader then
806 * current lcd status is powerdown and then
807 * it enables lcd panel.
808 */
809 lcd->power = FB_BLANK_POWERDOWN;
810
811 s6e63m0_power(lcd, FB_BLANK_UNBLANK);
812 } else
813 lcd->power = FB_BLANK_UNBLANK;
814
815 dev_set_drvdata(&spi->dev, lcd);
816
817 dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
818
819 return 0;
820
821 out_lcd_unregister:
822 lcd_device_unregister(ld);
823 out_free_lcd:
824 kfree(lcd);
825 return ret;
826 }
827
s6e63m0_remove(struct spi_device * spi)828 static int __devexit s6e63m0_remove(struct spi_device *spi)
829 {
830 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
831
832 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
833 device_remove_file(&spi->dev, &dev_attr_gamma_table);
834 device_remove_file(&spi->dev, &dev_attr_gamma_mode);
835 backlight_device_unregister(lcd->bd);
836 lcd_device_unregister(lcd->ld);
837 kfree(lcd);
838
839 return 0;
840 }
841
842 #if defined(CONFIG_PM)
843 unsigned int before_power;
844
s6e63m0_suspend(struct spi_device * spi,pm_message_t mesg)845 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
846 {
847 int ret = 0;
848 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
849
850 dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
851
852 before_power = lcd->power;
853
854 /*
855 * when lcd panel is suspend, lcd panel becomes off
856 * regardless of status.
857 */
858 ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
859
860 return ret;
861 }
862
s6e63m0_resume(struct spi_device * spi)863 static int s6e63m0_resume(struct spi_device *spi)
864 {
865 int ret = 0;
866 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
867
868 /*
869 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
870 * (at that time, before_power is FB_BLANK_UNBLANK) then
871 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
872 */
873 if (before_power == FB_BLANK_UNBLANK)
874 lcd->power = FB_BLANK_POWERDOWN;
875
876 dev_dbg(&spi->dev, "before_power = %d\n", before_power);
877
878 ret = s6e63m0_power(lcd, before_power);
879
880 return ret;
881 }
882 #else
883 #define s6e63m0_suspend NULL
884 #define s6e63m0_resume NULL
885 #endif
886
887 /* Power down all displays on reboot, poweroff or halt. */
s6e63m0_shutdown(struct spi_device * spi)888 static void s6e63m0_shutdown(struct spi_device *spi)
889 {
890 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
891
892 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
893 }
894
895 static struct spi_driver s6e63m0_driver = {
896 .driver = {
897 .name = "s6e63m0",
898 .bus = &spi_bus_type,
899 .owner = THIS_MODULE,
900 },
901 .probe = s6e63m0_probe,
902 .remove = __devexit_p(s6e63m0_remove),
903 .shutdown = s6e63m0_shutdown,
904 .suspend = s6e63m0_suspend,
905 .resume = s6e63m0_resume,
906 };
907
s6e63m0_init(void)908 static int __init s6e63m0_init(void)
909 {
910 return spi_register_driver(&s6e63m0_driver);
911 }
912
s6e63m0_exit(void)913 static void __exit s6e63m0_exit(void)
914 {
915 spi_unregister_driver(&s6e63m0_driver);
916 }
917
918 module_init(s6e63m0_init);
919 module_exit(s6e63m0_exit);
920
921 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
922 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
923 MODULE_LICENSE("GPL");
924
925