s6e63m0.c revision 97a4d9d53a2c63706cb5bf222c37bf273939a7eb
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			msleep(wbuf[i+1]);
411		}
412		i += 2;
413	}
414
415	return ret;
416}
417
418static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
419{
420	unsigned int i = 0;
421	int ret = 0;
422
423	/* disable gamma table updating. */
424	ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
425	if (ret) {
426		dev_err(lcd->dev, "failed to disable gamma table updating.\n");
427		goto gamma_err;
428	}
429
430	for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
431		ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
432		if (ret) {
433			dev_err(lcd->dev, "failed to set gamma table.\n");
434			goto gamma_err;
435		}
436	}
437
438	/* update gamma table. */
439	ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
440	if (ret)
441		dev_err(lcd->dev, "failed to update gamma table.\n");
442
443gamma_err:
444	return ret;
445}
446
447static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
448{
449	int ret = 0;
450
451	ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
452
453	return ret;
454}
455
456
457static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
458{
459	int ret, i;
460	const unsigned short *init_seq[] = {
461		seq_panel_condition_set,
462		seq_display_condition_set,
463		seq_gamma_setting,
464		seq_etc_condition_set,
465		seq_acl_on,
466		seq_elvss_on,
467	};
468
469	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
470		ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
471		if (ret)
472			break;
473	}
474
475	return ret;
476}
477
478static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
479{
480	int ret = 0, i;
481	const unsigned short *enable_seq[] = {
482		seq_stand_by_off,
483		seq_display_on,
484	};
485
486	for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
487		ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
488		if (ret)
489			break;
490	}
491
492	return ret;
493}
494
495static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
496{
497	int ret;
498
499	ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on);
500
501	return ret;
502}
503
504static int s6e63m0_power_on(struct s6e63m0 *lcd)
505{
506	int ret = 0;
507	struct lcd_platform_data *pd = NULL;
508	struct backlight_device *bd = NULL;
509
510	pd = lcd->lcd_pd;
511	if (!pd) {
512		dev_err(lcd->dev, "platform data is NULL.\n");
513		return -EFAULT;
514	}
515
516	bd = lcd->bd;
517	if (!bd) {
518		dev_err(lcd->dev, "backlight device is NULL.\n");
519		return -EFAULT;
520	}
521
522	if (!pd->power_on) {
523		dev_err(lcd->dev, "power_on is NULL.\n");
524		return -EFAULT;
525	} else {
526		pd->power_on(lcd->ld, 1);
527		msleep(pd->power_on_delay);
528	}
529
530	if (!pd->reset) {
531		dev_err(lcd->dev, "reset is NULL.\n");
532		return -EFAULT;
533	} else {
534		pd->reset(lcd->ld);
535		msleep(pd->reset_delay);
536	}
537
538	ret = s6e63m0_ldi_init(lcd);
539	if (ret) {
540		dev_err(lcd->dev, "failed to initialize ldi.\n");
541		return ret;
542	}
543
544	ret = s6e63m0_ldi_enable(lcd);
545	if (ret) {
546		dev_err(lcd->dev, "failed to enable ldi.\n");
547		return ret;
548	}
549
550	/* set brightness to current value after power on or resume. */
551	ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
552	if (ret) {
553		dev_err(lcd->dev, "lcd gamma setting failed.\n");
554		return ret;
555	}
556
557	return 0;
558}
559
560static int s6e63m0_power_off(struct s6e63m0 *lcd)
561{
562	int ret = 0;
563	struct lcd_platform_data *pd = NULL;
564
565	pd = lcd->lcd_pd;
566	if (!pd) {
567		dev_err(lcd->dev, "platform data is NULL.\n");
568		return -EFAULT;
569	}
570
571	ret = s6e63m0_ldi_disable(lcd);
572	if (ret) {
573		dev_err(lcd->dev, "lcd setting failed.\n");
574		return -EIO;
575	}
576
577	msleep(pd->power_off_delay);
578
579	if (!pd->power_on) {
580		dev_err(lcd->dev, "power_on is NULL.\n");
581		return -EFAULT;
582	} else
583		pd->power_on(lcd->ld, 0);
584
585	return 0;
586}
587
588static int s6e63m0_power(struct s6e63m0 *lcd, int power)
589{
590	int ret = 0;
591
592	if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
593		ret = s6e63m0_power_on(lcd);
594	else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
595		ret = s6e63m0_power_off(lcd);
596
597	if (!ret)
598		lcd->power = power;
599
600	return ret;
601}
602
603static int s6e63m0_set_power(struct lcd_device *ld, int power)
604{
605	struct s6e63m0 *lcd = lcd_get_data(ld);
606
607	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
608		power != FB_BLANK_NORMAL) {
609		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
610		return -EINVAL;
611	}
612
613	return s6e63m0_power(lcd, power);
614}
615
616static int s6e63m0_get_power(struct lcd_device *ld)
617{
618	struct s6e63m0 *lcd = lcd_get_data(ld);
619
620	return lcd->power;
621}
622
623static int s6e63m0_get_brightness(struct backlight_device *bd)
624{
625	return bd->props.brightness;
626}
627
628static int s6e63m0_set_brightness(struct backlight_device *bd)
629{
630	int ret = 0, brightness = bd->props.brightness;
631	struct s6e63m0 *lcd = bl_get_data(bd);
632
633	if (brightness < MIN_BRIGHTNESS ||
634		brightness > bd->props.max_brightness) {
635		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
636			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
637		return -EINVAL;
638	}
639
640	ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
641	if (ret) {
642		dev_err(&bd->dev, "lcd brightness setting failed.\n");
643		return -EIO;
644	}
645
646	return ret;
647}
648
649static struct lcd_ops s6e63m0_lcd_ops = {
650	.set_power = s6e63m0_set_power,
651	.get_power = s6e63m0_get_power,
652};
653
654static const struct backlight_ops s6e63m0_backlight_ops  = {
655	.get_brightness = s6e63m0_get_brightness,
656	.update_status = s6e63m0_set_brightness,
657};
658
659static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
660				      struct device_attribute *attr, char *buf)
661{
662	struct s6e63m0 *lcd = dev_get_drvdata(dev);
663	char temp[10];
664
665	switch (lcd->gamma_mode) {
666	case 0:
667		sprintf(temp, "2.2 mode\n");
668		strcat(buf, temp);
669		break;
670	case 1:
671		sprintf(temp, "1.9 mode\n");
672		strcat(buf, temp);
673		break;
674	case 2:
675		sprintf(temp, "1.7 mode\n");
676		strcat(buf, temp);
677		break;
678	default:
679		dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
680		break;
681	}
682
683	return strlen(buf);
684}
685
686static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
687				       struct device_attribute *attr,
688				       const char *buf, size_t len)
689{
690	struct s6e63m0 *lcd = dev_get_drvdata(dev);
691	struct backlight_device *bd = NULL;
692	int brightness, rc;
693
694	rc = kstrtouint(buf, 0, &lcd->gamma_mode);
695	if (rc < 0)
696		return rc;
697
698	bd = lcd->bd;
699
700	brightness = bd->props.brightness;
701
702	switch (lcd->gamma_mode) {
703	case 0:
704		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
705		break;
706	case 1:
707		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
708		break;
709	case 2:
710		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
711		break;
712	default:
713		dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
714		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
715		break;
716	}
717	return len;
718}
719
720static DEVICE_ATTR(gamma_mode, 0644,
721		s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
722
723static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
724				      struct device_attribute *attr, char *buf)
725{
726	struct s6e63m0 *lcd = dev_get_drvdata(dev);
727	char temp[3];
728
729	sprintf(temp, "%d\n", lcd->gamma_table_count);
730	strcpy(buf, temp);
731
732	return strlen(buf);
733}
734static DEVICE_ATTR(gamma_table, 0444,
735		s6e63m0_sysfs_show_gamma_table, NULL);
736
737static int s6e63m0_probe(struct spi_device *spi)
738{
739	int ret = 0;
740	struct s6e63m0 *lcd = NULL;
741	struct lcd_device *ld = NULL;
742	struct backlight_device *bd = NULL;
743	struct backlight_properties props;
744
745	lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
746	if (!lcd)
747		return -ENOMEM;
748
749	/* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
750	spi->bits_per_word = 9;
751
752	ret = spi_setup(spi);
753	if (ret < 0) {
754		dev_err(&spi->dev, "spi setup failed.\n");
755		return ret;
756	}
757
758	lcd->spi = spi;
759	lcd->dev = &spi->dev;
760
761	lcd->lcd_pd = spi->dev.platform_data;
762	if (!lcd->lcd_pd) {
763		dev_err(&spi->dev, "platform data is NULL.\n");
764		return -EFAULT;
765	}
766
767	ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
768	if (IS_ERR(ld))
769		return PTR_ERR(ld);
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);
826	return ret;
827}
828
829static int s6e63m0_remove(struct spi_device *spi)
830{
831	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
832
833	s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
834	device_remove_file(&spi->dev, &dev_attr_gamma_table);
835	device_remove_file(&spi->dev, &dev_attr_gamma_mode);
836	backlight_device_unregister(lcd->bd);
837	lcd_device_unregister(lcd->ld);
838
839	return 0;
840}
841
842#if defined(CONFIG_PM)
843static unsigned 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		.owner	= THIS_MODULE,
899	},
900	.probe		= s6e63m0_probe,
901	.remove		= s6e63m0_remove,
902	.shutdown	= s6e63m0_shutdown,
903	.suspend	= s6e63m0_suspend,
904	.resume		= s6e63m0_resume,
905};
906
907module_spi_driver(s6e63m0_driver);
908
909MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
910MODULE_DESCRIPTION("S6E63M0 LCD Driver");
911MODULE_LICENSE("GPL");
912
913