s6e63m0.c revision bb7ca747f8d6243b3943c5b133048652020f4a50
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
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, 0444,
733		s6e63m0_sysfs_show_gamma_table, NULL);
734
735static 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
821out_lcd_unregister:
822	lcd_device_unregister(ld);
823out_free_lcd:
824	kfree(lcd);
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	kfree(lcd);
838
839	return 0;
840}
841
842#if defined(CONFIG_PM)
843unsigned int before_power;
844
845static 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
863static 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. */
888static 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
895static 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
908static int __init s6e63m0_init(void)
909{
910	return spi_register_driver(&s6e63m0_driver);
911}
912
913static void __exit s6e63m0_exit(void)
914{
915	spi_unregister_driver(&s6e63m0_driver);
916}
917
918module_init(s6e63m0_init);
919module_exit(s6e63m0_exit);
920
921MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
922MODULE_DESCRIPTION("S6E63M0 LCD Driver");
923MODULE_LICENSE("GPL");
924
925