s6e63m0.c revision ef22f6a70c9186c8e25f757b0e8f7374b37f69bf
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	struct backlight_properties props;
742
743	lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
744	if (!lcd)
745		return -ENOMEM;
746
747	/* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
748	spi->bits_per_word = 9;
749
750	ret = spi_setup(spi);
751	if (ret < 0) {
752		dev_err(&spi->dev, "spi setup failed.\n");
753		goto out_free_lcd;
754	}
755
756	lcd->spi = spi;
757	lcd->dev = &spi->dev;
758
759	lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
760	if (!lcd->lcd_pd) {
761		dev_err(&spi->dev, "platform data is NULL.\n");
762		goto out_free_lcd;
763	}
764
765	ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
766	if (IS_ERR(ld)) {
767		ret = PTR_ERR(ld);
768		goto out_free_lcd;
769	}
770
771	lcd->ld = ld;
772
773	memset(&props, 0, sizeof(struct backlight_properties));
774	props.type = BACKLIGHT_RAW;
775	props.max_brightness = MAX_BRIGHTNESS;
776
777	bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
778		&s6e63m0_backlight_ops, &props);
779	if (IS_ERR(bd)) {
780		ret =  PTR_ERR(bd);
781		goto out_lcd_unregister;
782	}
783
784	bd->props.brightness = MAX_BRIGHTNESS;
785	lcd->bd = bd;
786
787	/*
788	 * it gets gamma table count available so it gets user
789	 * know that.
790	 */
791	lcd->gamma_table_count =
792	    sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
793
794	ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
795	if (ret < 0)
796		dev_err(&(spi->dev), "failed to add sysfs entries\n");
797
798	ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
799	if (ret < 0)
800		dev_err(&(spi->dev), "failed to add sysfs entries\n");
801
802	/*
803	 * if lcd panel was on from bootloader like u-boot then
804	 * do not lcd on.
805	 */
806	if (!lcd->lcd_pd->lcd_enabled) {
807		/*
808		 * if lcd panel was off from bootloader then
809		 * current lcd status is powerdown and then
810		 * it enables lcd panel.
811		 */
812		lcd->power = FB_BLANK_POWERDOWN;
813
814		s6e63m0_power(lcd, FB_BLANK_UNBLANK);
815	} else
816		lcd->power = FB_BLANK_UNBLANK;
817
818	dev_set_drvdata(&spi->dev, lcd);
819
820	dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
821
822	return 0;
823
824out_lcd_unregister:
825	lcd_device_unregister(ld);
826out_free_lcd:
827	kfree(lcd);
828	return ret;
829}
830
831static int __devexit s6e63m0_remove(struct spi_device *spi)
832{
833	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
834
835	s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
836	device_remove_file(&spi->dev, &dev_attr_gamma_table);
837	device_remove_file(&spi->dev, &dev_attr_gamma_mode);
838	backlight_device_unregister(lcd->bd);
839	lcd_device_unregister(lcd->ld);
840	kfree(lcd);
841
842	return 0;
843}
844
845#if defined(CONFIG_PM)
846static unsigned int before_power;
847
848static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
849{
850	int ret = 0;
851	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
852
853	dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
854
855	before_power = lcd->power;
856
857	/*
858	 * when lcd panel is suspend, lcd panel becomes off
859	 * regardless of status.
860	 */
861	ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
862
863	return ret;
864}
865
866static int s6e63m0_resume(struct spi_device *spi)
867{
868	int ret = 0;
869	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
870
871	/*
872	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
873	 * (at that time, before_power is FB_BLANK_UNBLANK) then
874	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
875	 */
876	if (before_power == FB_BLANK_UNBLANK)
877		lcd->power = FB_BLANK_POWERDOWN;
878
879	dev_dbg(&spi->dev, "before_power = %d\n", before_power);
880
881	ret = s6e63m0_power(lcd, before_power);
882
883	return ret;
884}
885#else
886#define s6e63m0_suspend		NULL
887#define s6e63m0_resume		NULL
888#endif
889
890/* Power down all displays on reboot, poweroff or halt. */
891static void s6e63m0_shutdown(struct spi_device *spi)
892{
893	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
894
895	s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
896}
897
898static struct spi_driver s6e63m0_driver = {
899	.driver = {
900		.name	= "s6e63m0",
901		.bus	= &spi_bus_type,
902		.owner	= THIS_MODULE,
903	},
904	.probe		= s6e63m0_probe,
905	.remove		= __devexit_p(s6e63m0_remove),
906	.shutdown	= s6e63m0_shutdown,
907	.suspend	= s6e63m0_suspend,
908	.resume		= s6e63m0_resume,
909};
910
911static int __init s6e63m0_init(void)
912{
913	return spi_register_driver(&s6e63m0_driver);
914}
915
916static void __exit s6e63m0_exit(void)
917{
918	spi_unregister_driver(&s6e63m0_driver);
919}
920
921module_init(s6e63m0_init);
922module_exit(s6e63m0_exit);
923
924MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
925MODULE_DESCRIPTION("S6E63M0 LCD Driver");
926MODULE_LICENSE("GPL");
927
928