s6e63m0.c revision 892dc2e6603034188f51b30e702498a51d8c7365
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
46struct s6e63m0 {
47	struct device			*dev;
48	struct spi_device		*spi;
49	unsigned int			power;
50	unsigned int			current_brightness;
51	unsigned int			gamma_mode;
52	unsigned int			gamma_table_count;
53	struct lcd_device		*ld;
54	struct backlight_device		*bd;
55	struct lcd_platform_data	*lcd_pd;
56};
57
58static const unsigned short seq_panel_condition_set[] = {
59	0xF8, 0x01,
60	DATA_ONLY, 0x27,
61	DATA_ONLY, 0x27,
62	DATA_ONLY, 0x07,
63	DATA_ONLY, 0x07,
64	DATA_ONLY, 0x54,
65	DATA_ONLY, 0x9f,
66	DATA_ONLY, 0x63,
67	DATA_ONLY, 0x86,
68	DATA_ONLY, 0x1a,
69	DATA_ONLY, 0x33,
70	DATA_ONLY, 0x0d,
71	DATA_ONLY, 0x00,
72	DATA_ONLY, 0x00,
73
74	ENDDEF, 0x0000
75};
76
77static const unsigned short seq_display_condition_set[] = {
78	0xf2, 0x02,
79	DATA_ONLY, 0x03,
80	DATA_ONLY, 0x1c,
81	DATA_ONLY, 0x10,
82	DATA_ONLY, 0x10,
83
84	0xf7, 0x03,
85	DATA_ONLY, 0x00,
86	DATA_ONLY, 0x00,
87
88	ENDDEF, 0x0000
89};
90
91static const unsigned short seq_gamma_setting[] = {
92	0xfa, 0x00,
93	DATA_ONLY, 0x18,
94	DATA_ONLY, 0x08,
95	DATA_ONLY, 0x24,
96	DATA_ONLY, 0x64,
97	DATA_ONLY, 0x56,
98	DATA_ONLY, 0x33,
99	DATA_ONLY, 0xb6,
100	DATA_ONLY, 0xba,
101	DATA_ONLY, 0xa8,
102	DATA_ONLY, 0xac,
103	DATA_ONLY, 0xb1,
104	DATA_ONLY, 0x9d,
105	DATA_ONLY, 0xc1,
106	DATA_ONLY, 0xc1,
107	DATA_ONLY, 0xb7,
108	DATA_ONLY, 0x00,
109	DATA_ONLY, 0x9c,
110	DATA_ONLY, 0x00,
111	DATA_ONLY, 0x9f,
112	DATA_ONLY, 0x00,
113	DATA_ONLY, 0xd6,
114
115	0xfa, 0x01,
116
117	ENDDEF, 0x0000
118};
119
120static const unsigned short seq_etc_condition_set[] = {
121	0xf6, 0x00,
122	DATA_ONLY, 0x8c,
123	DATA_ONLY, 0x07,
124
125	0xb3, 0xc,
126
127	0xb5, 0x2c,
128	DATA_ONLY, 0x12,
129	DATA_ONLY, 0x0c,
130	DATA_ONLY, 0x0a,
131	DATA_ONLY, 0x10,
132	DATA_ONLY, 0x0e,
133	DATA_ONLY, 0x17,
134	DATA_ONLY, 0x13,
135	DATA_ONLY, 0x1f,
136	DATA_ONLY, 0x1a,
137	DATA_ONLY, 0x2a,
138	DATA_ONLY, 0x24,
139	DATA_ONLY, 0x1f,
140	DATA_ONLY, 0x1b,
141	DATA_ONLY, 0x1a,
142	DATA_ONLY, 0x17,
143
144	DATA_ONLY, 0x2b,
145	DATA_ONLY, 0x26,
146	DATA_ONLY, 0x22,
147	DATA_ONLY, 0x20,
148	DATA_ONLY, 0x3a,
149	DATA_ONLY, 0x34,
150	DATA_ONLY, 0x30,
151	DATA_ONLY, 0x2c,
152	DATA_ONLY, 0x29,
153	DATA_ONLY, 0x26,
154	DATA_ONLY, 0x25,
155	DATA_ONLY, 0x23,
156	DATA_ONLY, 0x21,
157	DATA_ONLY, 0x20,
158	DATA_ONLY, 0x1e,
159	DATA_ONLY, 0x1e,
160
161	0xb6, 0x00,
162	DATA_ONLY, 0x00,
163	DATA_ONLY, 0x11,
164	DATA_ONLY, 0x22,
165	DATA_ONLY, 0x33,
166	DATA_ONLY, 0x44,
167	DATA_ONLY, 0x44,
168	DATA_ONLY, 0x44,
169
170	DATA_ONLY, 0x55,
171	DATA_ONLY, 0x55,
172	DATA_ONLY, 0x66,
173	DATA_ONLY, 0x66,
174	DATA_ONLY, 0x66,
175	DATA_ONLY, 0x66,
176	DATA_ONLY, 0x66,
177	DATA_ONLY, 0x66,
178
179	0xb7, 0x2c,
180	DATA_ONLY, 0x12,
181	DATA_ONLY, 0x0c,
182	DATA_ONLY, 0x0a,
183	DATA_ONLY, 0x10,
184	DATA_ONLY, 0x0e,
185	DATA_ONLY, 0x17,
186	DATA_ONLY, 0x13,
187	DATA_ONLY, 0x1f,
188	DATA_ONLY, 0x1a,
189	DATA_ONLY, 0x2a,
190	DATA_ONLY, 0x24,
191	DATA_ONLY, 0x1f,
192	DATA_ONLY, 0x1b,
193	DATA_ONLY, 0x1a,
194	DATA_ONLY, 0x17,
195
196	DATA_ONLY, 0x2b,
197	DATA_ONLY, 0x26,
198	DATA_ONLY, 0x22,
199	DATA_ONLY, 0x20,
200	DATA_ONLY, 0x3a,
201	DATA_ONLY, 0x34,
202	DATA_ONLY, 0x30,
203	DATA_ONLY, 0x2c,
204	DATA_ONLY, 0x29,
205	DATA_ONLY, 0x26,
206	DATA_ONLY, 0x25,
207	DATA_ONLY, 0x23,
208	DATA_ONLY, 0x21,
209	DATA_ONLY, 0x20,
210	DATA_ONLY, 0x1e,
211	DATA_ONLY, 0x1e,
212
213	0xb8, 0x00,
214	DATA_ONLY, 0x00,
215	DATA_ONLY, 0x11,
216	DATA_ONLY, 0x22,
217	DATA_ONLY, 0x33,
218	DATA_ONLY, 0x44,
219	DATA_ONLY, 0x44,
220	DATA_ONLY, 0x44,
221
222	DATA_ONLY, 0x55,
223	DATA_ONLY, 0x55,
224	DATA_ONLY, 0x66,
225	DATA_ONLY, 0x66,
226	DATA_ONLY, 0x66,
227	DATA_ONLY, 0x66,
228	DATA_ONLY, 0x66,
229	DATA_ONLY, 0x66,
230
231	0xb9, 0x2c,
232	DATA_ONLY, 0x12,
233	DATA_ONLY, 0x0c,
234	DATA_ONLY, 0x0a,
235	DATA_ONLY, 0x10,
236	DATA_ONLY, 0x0e,
237	DATA_ONLY, 0x17,
238	DATA_ONLY, 0x13,
239	DATA_ONLY, 0x1f,
240	DATA_ONLY, 0x1a,
241	DATA_ONLY, 0x2a,
242	DATA_ONLY, 0x24,
243	DATA_ONLY, 0x1f,
244	DATA_ONLY, 0x1b,
245	DATA_ONLY, 0x1a,
246	DATA_ONLY, 0x17,
247
248	DATA_ONLY, 0x2b,
249	DATA_ONLY, 0x26,
250	DATA_ONLY, 0x22,
251	DATA_ONLY, 0x20,
252	DATA_ONLY, 0x3a,
253	DATA_ONLY, 0x34,
254	DATA_ONLY, 0x30,
255	DATA_ONLY, 0x2c,
256	DATA_ONLY, 0x29,
257	DATA_ONLY, 0x26,
258	DATA_ONLY, 0x25,
259	DATA_ONLY, 0x23,
260	DATA_ONLY, 0x21,
261	DATA_ONLY, 0x20,
262	DATA_ONLY, 0x1e,
263	DATA_ONLY, 0x1e,
264
265	0xba, 0x00,
266	DATA_ONLY, 0x00,
267	DATA_ONLY, 0x11,
268	DATA_ONLY, 0x22,
269	DATA_ONLY, 0x33,
270	DATA_ONLY, 0x44,
271	DATA_ONLY, 0x44,
272	DATA_ONLY, 0x44,
273
274	DATA_ONLY, 0x55,
275	DATA_ONLY, 0x55,
276	DATA_ONLY, 0x66,
277	DATA_ONLY, 0x66,
278	DATA_ONLY, 0x66,
279	DATA_ONLY, 0x66,
280	DATA_ONLY, 0x66,
281	DATA_ONLY, 0x66,
282
283	0xc1, 0x4d,
284	DATA_ONLY, 0x96,
285	DATA_ONLY, 0x1d,
286	DATA_ONLY, 0x00,
287	DATA_ONLY, 0x00,
288	DATA_ONLY, 0x01,
289	DATA_ONLY, 0xdf,
290	DATA_ONLY, 0x00,
291	DATA_ONLY, 0x00,
292	DATA_ONLY, 0x03,
293	DATA_ONLY, 0x1f,
294	DATA_ONLY, 0x00,
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, 0x03,
303	DATA_ONLY, 0x06,
304	DATA_ONLY, 0x09,
305	DATA_ONLY, 0x0d,
306	DATA_ONLY, 0x0f,
307	DATA_ONLY, 0x12,
308	DATA_ONLY, 0x15,
309	DATA_ONLY, 0x18,
310
311	0xb2, 0x10,
312	DATA_ONLY, 0x10,
313	DATA_ONLY, 0x0b,
314	DATA_ONLY, 0x05,
315
316	ENDDEF, 0x0000
317};
318
319static const unsigned short seq_acl_on[] = {
320	/* ACL on */
321	0xc0, 0x01,
322
323	ENDDEF, 0x0000
324};
325
326static const unsigned short seq_acl_off[] = {
327	/* ACL off */
328	0xc0, 0x00,
329
330	ENDDEF, 0x0000
331};
332
333static const unsigned short seq_elvss_on[] = {
334	/* ELVSS on */
335	0xb1, 0x0b,
336
337	ENDDEF, 0x0000
338};
339
340static const unsigned short seq_elvss_off[] = {
341	/* ELVSS off */
342	0xb1, 0x0a,
343
344	ENDDEF, 0x0000
345};
346
347static const unsigned short seq_stand_by_off[] = {
348	0x11, COMMAND_ONLY,
349
350	ENDDEF, 0x0000
351};
352
353static const unsigned short seq_stand_by_on[] = {
354	0x10, COMMAND_ONLY,
355
356	ENDDEF, 0x0000
357};
358
359static const unsigned short seq_display_on[] = {
360	0x29, COMMAND_ONLY,
361
362	ENDDEF, 0x0000
363};
364
365
366static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
367{
368	u16 buf[1];
369	struct spi_message msg;
370
371	struct spi_transfer xfer = {
372		.len		= 2,
373		.tx_buf		= buf,
374	};
375
376	buf[0] = (addr << 8) | data;
377
378	spi_message_init(&msg);
379	spi_message_add_tail(&xfer, &msg);
380
381	return spi_sync(lcd->spi, &msg);
382}
383
384static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
385	unsigned char command)
386{
387	int ret = 0;
388
389	if (address != DATA_ONLY)
390		ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
391	if (command != COMMAND_ONLY)
392		ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
393
394	return ret;
395}
396
397static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
398	const unsigned short *wbuf)
399{
400	int ret = 0, i = 0;
401
402	while ((wbuf[i] & DEFMASK) != ENDDEF) {
403		if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
404			ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
405			if (ret)
406				break;
407		} else {
408			msleep(wbuf[i+1]);
409		}
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_is_on(int power)
503{
504	return power <= FB_BLANK_NORMAL;
505}
506
507static int s6e63m0_power_on(struct s6e63m0 *lcd)
508{
509	int ret = 0;
510	struct lcd_platform_data *pd;
511	struct backlight_device *bd;
512
513	pd = lcd->lcd_pd;
514	bd = lcd->bd;
515
516	if (!pd->power_on) {
517		dev_err(lcd->dev, "power_on is NULL.\n");
518		return -EFAULT;
519	} else {
520		pd->power_on(lcd->ld, 1);
521		msleep(pd->power_on_delay);
522	}
523
524	if (!pd->reset) {
525		dev_err(lcd->dev, "reset is NULL.\n");
526		return -EFAULT;
527	} else {
528		pd->reset(lcd->ld);
529		msleep(pd->reset_delay);
530	}
531
532	ret = s6e63m0_ldi_init(lcd);
533	if (ret) {
534		dev_err(lcd->dev, "failed to initialize ldi.\n");
535		return ret;
536	}
537
538	ret = s6e63m0_ldi_enable(lcd);
539	if (ret) {
540		dev_err(lcd->dev, "failed to enable ldi.\n");
541		return ret;
542	}
543
544	/* set brightness to current value after power on or resume. */
545	ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
546	if (ret) {
547		dev_err(lcd->dev, "lcd gamma setting failed.\n");
548		return ret;
549	}
550
551	return 0;
552}
553
554static int s6e63m0_power_off(struct s6e63m0 *lcd)
555{
556	int ret;
557	struct lcd_platform_data *pd;
558
559	pd = lcd->lcd_pd;
560
561	ret = s6e63m0_ldi_disable(lcd);
562	if (ret) {
563		dev_err(lcd->dev, "lcd setting failed.\n");
564		return -EIO;
565	}
566
567	msleep(pd->power_off_delay);
568
569	pd->power_on(lcd->ld, 0);
570
571	return 0;
572}
573
574static int s6e63m0_power(struct s6e63m0 *lcd, int power)
575{
576	int ret = 0;
577
578	if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power))
579		ret = s6e63m0_power_on(lcd);
580	else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power))
581		ret = s6e63m0_power_off(lcd);
582
583	if (!ret)
584		lcd->power = power;
585
586	return ret;
587}
588
589static int s6e63m0_set_power(struct lcd_device *ld, int power)
590{
591	struct s6e63m0 *lcd = lcd_get_data(ld);
592
593	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
594		power != FB_BLANK_NORMAL) {
595		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
596		return -EINVAL;
597	}
598
599	return s6e63m0_power(lcd, power);
600}
601
602static int s6e63m0_get_power(struct lcd_device *ld)
603{
604	struct s6e63m0 *lcd = lcd_get_data(ld);
605
606	return lcd->power;
607}
608
609static int s6e63m0_get_brightness(struct backlight_device *bd)
610{
611	return bd->props.brightness;
612}
613
614static int s6e63m0_set_brightness(struct backlight_device *bd)
615{
616	int ret = 0, brightness = bd->props.brightness;
617	struct s6e63m0 *lcd = bl_get_data(bd);
618
619	if (brightness < MIN_BRIGHTNESS ||
620		brightness > bd->props.max_brightness) {
621		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
622			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
623		return -EINVAL;
624	}
625
626	ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
627	if (ret) {
628		dev_err(&bd->dev, "lcd brightness setting failed.\n");
629		return -EIO;
630	}
631
632	return ret;
633}
634
635static struct lcd_ops s6e63m0_lcd_ops = {
636	.set_power = s6e63m0_set_power,
637	.get_power = s6e63m0_get_power,
638};
639
640static const struct backlight_ops s6e63m0_backlight_ops  = {
641	.get_brightness = s6e63m0_get_brightness,
642	.update_status = s6e63m0_set_brightness,
643};
644
645static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
646				      struct device_attribute *attr, char *buf)
647{
648	struct s6e63m0 *lcd = dev_get_drvdata(dev);
649	char temp[10];
650
651	switch (lcd->gamma_mode) {
652	case 0:
653		sprintf(temp, "2.2 mode\n");
654		strcat(buf, temp);
655		break;
656	case 1:
657		sprintf(temp, "1.9 mode\n");
658		strcat(buf, temp);
659		break;
660	case 2:
661		sprintf(temp, "1.7 mode\n");
662		strcat(buf, temp);
663		break;
664	default:
665		dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
666		break;
667	}
668
669	return strlen(buf);
670}
671
672static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
673				       struct device_attribute *attr,
674				       const char *buf, size_t len)
675{
676	struct s6e63m0 *lcd = dev_get_drvdata(dev);
677	struct backlight_device *bd = NULL;
678	int brightness, rc;
679
680	rc = kstrtouint(buf, 0, &lcd->gamma_mode);
681	if (rc < 0)
682		return rc;
683
684	bd = lcd->bd;
685
686	brightness = bd->props.brightness;
687
688	switch (lcd->gamma_mode) {
689	case 0:
690		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
691		break;
692	case 1:
693		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
694		break;
695	case 2:
696		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
697		break;
698	default:
699		dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
700		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
701		break;
702	}
703	return len;
704}
705
706static DEVICE_ATTR(gamma_mode, 0644,
707		s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
708
709static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
710				      struct device_attribute *attr, char *buf)
711{
712	struct s6e63m0 *lcd = dev_get_drvdata(dev);
713	char temp[3];
714
715	sprintf(temp, "%d\n", lcd->gamma_table_count);
716	strcpy(buf, temp);
717
718	return strlen(buf);
719}
720static DEVICE_ATTR(gamma_table, 0444,
721		s6e63m0_sysfs_show_gamma_table, NULL);
722
723static int s6e63m0_probe(struct spi_device *spi)
724{
725	int ret = 0;
726	struct s6e63m0 *lcd = NULL;
727	struct lcd_device *ld = NULL;
728	struct backlight_device *bd = NULL;
729	struct backlight_properties props;
730
731	lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
732	if (!lcd)
733		return -ENOMEM;
734
735	/* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
736	spi->bits_per_word = 9;
737
738	ret = spi_setup(spi);
739	if (ret < 0) {
740		dev_err(&spi->dev, "spi setup failed.\n");
741		return ret;
742	}
743
744	lcd->spi = spi;
745	lcd->dev = &spi->dev;
746
747	lcd->lcd_pd = spi->dev.platform_data;
748	if (!lcd->lcd_pd) {
749		dev_err(&spi->dev, "platform data is NULL.\n");
750		return -EFAULT;
751	}
752
753	ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
754	if (IS_ERR(ld))
755		return PTR_ERR(ld);
756
757	lcd->ld = ld;
758
759	memset(&props, 0, sizeof(struct backlight_properties));
760	props.type = BACKLIGHT_RAW;
761	props.max_brightness = MAX_BRIGHTNESS;
762
763	bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
764		&s6e63m0_backlight_ops, &props);
765	if (IS_ERR(bd)) {
766		ret =  PTR_ERR(bd);
767		goto out_lcd_unregister;
768	}
769
770	bd->props.brightness = MAX_BRIGHTNESS;
771	lcd->bd = bd;
772
773	/*
774	 * it gets gamma table count available so it gets user
775	 * know that.
776	 */
777	lcd->gamma_table_count =
778	    sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
779
780	ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
781	if (ret < 0)
782		dev_err(&(spi->dev), "failed to add sysfs entries\n");
783
784	ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
785	if (ret < 0)
786		dev_err(&(spi->dev), "failed to add sysfs entries\n");
787
788	/*
789	 * if lcd panel was on from bootloader like u-boot then
790	 * do not lcd on.
791	 */
792	if (!lcd->lcd_pd->lcd_enabled) {
793		/*
794		 * if lcd panel was off from bootloader then
795		 * current lcd status is powerdown and then
796		 * it enables lcd panel.
797		 */
798		lcd->power = FB_BLANK_POWERDOWN;
799
800		s6e63m0_power(lcd, FB_BLANK_UNBLANK);
801	} else {
802		lcd->power = FB_BLANK_UNBLANK;
803	}
804
805	dev_set_drvdata(&spi->dev, lcd);
806
807	dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
808
809	return 0;
810
811out_lcd_unregister:
812	lcd_device_unregister(ld);
813	return ret;
814}
815
816static int s6e63m0_remove(struct spi_device *spi)
817{
818	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
819
820	s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
821	device_remove_file(&spi->dev, &dev_attr_gamma_table);
822	device_remove_file(&spi->dev, &dev_attr_gamma_mode);
823	backlight_device_unregister(lcd->bd);
824	lcd_device_unregister(lcd->ld);
825
826	return 0;
827}
828
829#if defined(CONFIG_PM)
830static unsigned int before_power;
831
832static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
833{
834	int ret = 0;
835	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
836
837	dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
838
839	before_power = lcd->power;
840
841	/*
842	 * when lcd panel is suspend, lcd panel becomes off
843	 * regardless of status.
844	 */
845	ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
846
847	return ret;
848}
849
850static int s6e63m0_resume(struct spi_device *spi)
851{
852	int ret = 0;
853	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
854
855	/*
856	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
857	 * (at that time, before_power is FB_BLANK_UNBLANK) then
858	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
859	 */
860	if (before_power == FB_BLANK_UNBLANK)
861		lcd->power = FB_BLANK_POWERDOWN;
862
863	dev_dbg(&spi->dev, "before_power = %d\n", before_power);
864
865	ret = s6e63m0_power(lcd, before_power);
866
867	return ret;
868}
869#else
870#define s6e63m0_suspend		NULL
871#define s6e63m0_resume		NULL
872#endif
873
874/* Power down all displays on reboot, poweroff or halt. */
875static void s6e63m0_shutdown(struct spi_device *spi)
876{
877	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
878
879	s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
880}
881
882static struct spi_driver s6e63m0_driver = {
883	.driver = {
884		.name	= "s6e63m0",
885		.owner	= THIS_MODULE,
886	},
887	.probe		= s6e63m0_probe,
888	.remove		= s6e63m0_remove,
889	.shutdown	= s6e63m0_shutdown,
890	.suspend	= s6e63m0_suspend,
891	.resume		= s6e63m0_resume,
892};
893
894module_spi_driver(s6e63m0_driver);
895
896MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
897MODULE_DESCRIPTION("S6E63M0 LCD Driver");
898MODULE_LICENSE("GPL");
899
900