s6e63m0.c revision 541f936f5d3993c5bbed33bdb53acd6de2403b04
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
48struct 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
60static 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
79static 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
93static 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
122static 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
321static const unsigned short SEQ_ACL_ON[] = {
322	/* ACL on */
323	0xc0, 0x01,
324
325	ENDDEF, 0x0000
326};
327
328static const unsigned short SEQ_ACL_OFF[] = {
329	/* ACL off */
330	0xc0, 0x00,
331
332	ENDDEF, 0x0000
333};
334
335static const unsigned short SEQ_ELVSS_ON[] = {
336	/* ELVSS on */
337	0xb1, 0x0b,
338
339	ENDDEF, 0x0000
340};
341
342static const unsigned short SEQ_ELVSS_OFF[] = {
343	/* ELVSS off */
344	0xb1, 0x0a,
345
346	ENDDEF, 0x0000
347};
348
349static const unsigned short SEQ_STAND_BY_OFF[] = {
350	0x11, COMMAND_ONLY,
351
352	ENDDEF, 0x0000
353};
354
355static const unsigned short SEQ_STAND_BY_ON[] = {
356	0x10, COMMAND_ONLY,
357
358	ENDDEF, 0x0000
359};
360
361static const unsigned short SEQ_DISPLAY_ON[] = {
362	0x29, COMMAND_ONLY,
363
364	ENDDEF, 0x0000
365};
366
367
368static 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
386static 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
399static 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
417static 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
442gamma_err:
443	return ret;
444}
445
446static 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
456static 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
477static 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
494static 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
503static 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
559static 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
587static 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
602static 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
615static int s6e63m0_get_power(struct lcd_device *ld)
616{
617	struct s6e63m0 *lcd = lcd_get_data(ld);
618
619	return lcd->power;
620}
621
622static int s6e63m0_get_brightness(struct backlight_device *bd)
623{
624	return bd->props.brightness;
625}
626
627static 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
648static struct lcd_ops s6e63m0_lcd_ops = {
649	.set_power = s6e63m0_set_power,
650	.get_power = s6e63m0_get_power,
651};
652
653static const struct backlight_ops s6e63m0_backlight_ops  = {
654	.get_brightness = s6e63m0_get_brightness,
655	.update_status = s6e63m0_set_brightness,
656};
657
658static 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
685static 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
719static DEVICE_ATTR(gamma_mode, 0644,
720		s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
721
722static 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}
733static DEVICE_ATTR(gamma_table, 0444,
734		s6e63m0_sysfs_show_gamma_table, NULL);
735
736static 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
823out_lcd_unregister:
824	lcd_device_unregister(ld);
825	return ret;
826}
827
828static 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
838	return 0;
839}
840
841#if defined(CONFIG_PM)
842static unsigned int before_power;
843
844static 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
862static 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. */
887static 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
894static 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
906module_spi_driver(s6e63m0_driver);
907
908MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
909MODULE_DESCRIPTION("S6E63M0 LCD Driver");
910MODULE_LICENSE("GPL");
911
912