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 
47 struct 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 
59 static 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 
78 static 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 
92 static 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 
121 static 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 
320 static const unsigned short SEQ_ACL_ON[] = {
321 	/* ACL on */
322 	0xc0, 0x01,
323 
324 	ENDDEF, 0x0000
325 };
326 
327 static const unsigned short SEQ_ACL_OFF[] = {
328 	/* ACL off */
329 	0xc0, 0x00,
330 
331 	ENDDEF, 0x0000
332 };
333 
334 static const unsigned short SEQ_ELVSS_ON[] = {
335 	/* ELVSS on */
336 	0xb1, 0x0b,
337 
338 	ENDDEF, 0x0000
339 };
340 
341 static const unsigned short SEQ_ELVSS_OFF[] = {
342 	/* ELVSS off */
343 	0xb1, 0x0a,
344 
345 	ENDDEF, 0x0000
346 };
347 
348 static const unsigned short SEQ_STAND_BY_OFF[] = {
349 	0x11, COMMAND_ONLY,
350 
351 	ENDDEF, 0x0000
352 };
353 
354 static const unsigned short SEQ_STAND_BY_ON[] = {
355 	0x10, COMMAND_ONLY,
356 
357 	ENDDEF, 0x0000
358 };
359 
360 static const unsigned short SEQ_DISPLAY_ON[] = {
361 	0x29, COMMAND_ONLY,
362 
363 	ENDDEF, 0x0000
364 };
365 
366 
s6e63m0_spi_write_byte(struct s6e63m0 * lcd,int addr,int data)367 static 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 
s6e63m0_spi_write(struct s6e63m0 * lcd,unsigned char address,unsigned char command)385 static 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 
s6e63m0_panel_send_sequence(struct s6e63m0 * lcd,const unsigned short * wbuf)398 static 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 
_s6e63m0_gamma_ctl(struct s6e63m0 * lcd,const unsigned int * gamma)416 static 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 
441 gamma_err:
442 	return ret;
443 }
444 
s6e63m0_gamma_ctl(struct s6e63m0 * lcd,int gamma)445 static 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 
s6e63m0_ldi_init(struct s6e63m0 * lcd)455 static 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 
s6e63m0_ldi_enable(struct s6e63m0 * lcd)476 static 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 
s6e63m0_ldi_disable(struct s6e63m0 * lcd)493 static 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 
s6e63m0_power_on(struct s6e63m0 * lcd)502 static 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 
s6e63m0_power_off(struct s6e63m0 * lcd)558 static 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 
s6e63m0_power(struct s6e63m0 * lcd,int power)586 static 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 
s6e63m0_set_power(struct lcd_device * ld,int power)601 static 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 
s6e63m0_get_power(struct lcd_device * ld)614 static int s6e63m0_get_power(struct lcd_device *ld)
615 {
616 	struct s6e63m0 *lcd = lcd_get_data(ld);
617 
618 	return lcd->power;
619 }
620 
s6e63m0_get_brightness(struct backlight_device * bd)621 static int s6e63m0_get_brightness(struct backlight_device *bd)
622 {
623 	return bd->props.brightness;
624 }
625 
s6e63m0_set_brightness(struct backlight_device * bd)626 static 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 
647 static struct lcd_ops s6e63m0_lcd_ops = {
648 	.set_power = s6e63m0_set_power,
649 	.get_power = s6e63m0_get_power,
650 };
651 
652 static const struct backlight_ops s6e63m0_backlight_ops  = {
653 	.get_brightness = s6e63m0_get_brightness,
654 	.update_status = s6e63m0_set_brightness,
655 };
656 
s6e63m0_sysfs_show_gamma_mode(struct device * dev,struct device_attribute * attr,char * buf)657 static 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 
s6e63m0_sysfs_store_gamma_mode(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)684 static 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 
718 static DEVICE_ATTR(gamma_mode, 0644,
719 		s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
720 
s6e63m0_sysfs_show_gamma_table(struct device * dev,struct device_attribute * attr,char * buf)721 static 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 }
732 static DEVICE_ATTR(gamma_table, 0444,
733 		s6e63m0_sysfs_show_gamma_table, NULL);
734 
s6e63m0_probe(struct spi_device * spi)735 static 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 
821 out_lcd_unregister:
822 	lcd_device_unregister(ld);
823 out_free_lcd:
824 	kfree(lcd);
825 	return ret;
826 }
827 
s6e63m0_remove(struct spi_device * spi)828 static 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)
843 unsigned int before_power;
844 
s6e63m0_suspend(struct spi_device * spi,pm_message_t mesg)845 static 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 
s6e63m0_resume(struct spi_device * spi)863 static 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. */
s6e63m0_shutdown(struct spi_device * spi)888 static 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 
895 static 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 
s6e63m0_init(void)908 static int __init s6e63m0_init(void)
909 {
910 	return spi_register_driver(&s6e63m0_driver);
911 }
912 
s6e63m0_exit(void)913 static void __exit s6e63m0_exit(void)
914 {
915 	spi_unregister_driver(&s6e63m0_driver);
916 }
917 
918 module_init(s6e63m0_init);
919 module_exit(s6e63m0_exit);
920 
921 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
922 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
923 MODULE_LICENSE("GPL");
924 
925