Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
s6e63m0.c
Go to the documentation of this file.
1 /*
2  * S6E63M0 AMOLED LCD panel driver.
3  *
4  * Author: InKi Dae <[email protected]>
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;
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",
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 = devm_kzalloc(&spi->dev, 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  return ret;
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  return -EFAULT;
764  }
765 
766  ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
767  if (IS_ERR(ld))
768  return PTR_ERR(ld);
769 
770  lcd->ld = ld;
771 
772  memset(&props, 0, sizeof(struct backlight_properties));
773  props.type = BACKLIGHT_RAW;
774  props.max_brightness = MAX_BRIGHTNESS;
775 
776  bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
777  &s6e63m0_backlight_ops, &props);
778  if (IS_ERR(bd)) {
779  ret = PTR_ERR(bd);
780  goto out_lcd_unregister;
781  }
782 
783  bd->props.brightness = MAX_BRIGHTNESS;
784  lcd->bd = bd;
785 
786  /*
787  * it gets gamma table count available so it gets user
788  * know that.
789  */
790  lcd->gamma_table_count =
791  sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
792 
793  ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
794  if (ret < 0)
795  dev_err(&(spi->dev), "failed to add sysfs entries\n");
796 
797  ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
798  if (ret < 0)
799  dev_err(&(spi->dev), "failed to add sysfs entries\n");
800 
801  /*
802  * if lcd panel was on from bootloader like u-boot then
803  * do not lcd on.
804  */
805  if (!lcd->lcd_pd->lcd_enabled) {
806  /*
807  * if lcd panel was off from bootloader then
808  * current lcd status is powerdown and then
809  * it enables lcd panel.
810  */
811  lcd->power = FB_BLANK_POWERDOWN;
812 
813  s6e63m0_power(lcd, FB_BLANK_UNBLANK);
814  } else
815  lcd->power = FB_BLANK_UNBLANK;
816 
817  dev_set_drvdata(&spi->dev, lcd);
818 
819  dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
820 
821  return 0;
822 
823 out_lcd_unregister:
825  return ret;
826 }
827 
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);
837 
838  return 0;
839 }
840 
841 #if defined(CONFIG_PM)
842 static unsigned int before_power;
843 
844 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
845 {
846  int ret = 0;
847  struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
848 
849  dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
850 
851  before_power = lcd->power;
852 
853  /*
854  * when lcd panel is suspend, lcd panel becomes off
855  * regardless of status.
856  */
857  ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
858 
859  return ret;
860 }
861 
862 static int s6e63m0_resume(struct spi_device *spi)
863 {
864  int ret = 0;
865  struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
866 
867  /*
868  * after suspended, if lcd panel status is FB_BLANK_UNBLANK
869  * (at that time, before_power is FB_BLANK_UNBLANK) then
870  * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
871  */
872  if (before_power == FB_BLANK_UNBLANK)
873  lcd->power = FB_BLANK_POWERDOWN;
874 
875  dev_dbg(&spi->dev, "before_power = %d\n", before_power);
876 
877  ret = s6e63m0_power(lcd, before_power);
878 
879  return ret;
880 }
881 #else
882 #define s6e63m0_suspend NULL
883 #define s6e63m0_resume NULL
884 #endif
885 
886 /* Power down all displays on reboot, poweroff or halt. */
887 static void s6e63m0_shutdown(struct spi_device *spi)
888 {
889  struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
890 
891  s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
892 }
893 
894 static struct spi_driver s6e63m0_driver = {
895  .driver = {
896  .name = "s6e63m0",
897  .owner = THIS_MODULE,
898  },
899  .probe = s6e63m0_probe,
900  .remove = __devexit_p(s6e63m0_remove),
901  .shutdown = s6e63m0_shutdown,
902  .suspend = s6e63m0_suspend,
903  .resume = s6e63m0_resume,
904 };
905 
906 module_spi_driver(s6e63m0_driver);
907 
908 MODULE_AUTHOR("InKi Dae <[email protected]>");
909 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
910 MODULE_LICENSE("GPL");
911