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 
48 struct 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 
60 static 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 
79 static 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 
93 static 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 
122 static 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 
321 static const unsigned short SEQ_ACL_ON[] = {
322 	/* ACL on */
323 	0xc0, 0x01,
324 
325 	ENDDEF, 0x0000
326 };
327 
328 static const unsigned short SEQ_ACL_OFF[] = {
329 	/* ACL off */
330 	0xc0, 0x00,
331 
332 	ENDDEF, 0x0000
333 };
334 
335 static const unsigned short SEQ_ELVSS_ON[] = {
336 	/* ELVSS on */
337 	0xb1, 0x0b,
338 
339 	ENDDEF, 0x0000
340 };
341 
342 static const unsigned short SEQ_ELVSS_OFF[] = {
343 	/* ELVSS off */
344 	0xb1, 0x0a,
345 
346 	ENDDEF, 0x0000
347 };
348 
349 static const unsigned short SEQ_STAND_BY_OFF[] = {
350 	0x11, COMMAND_ONLY,
351 
352 	ENDDEF, 0x0000
353 };
354 
355 static const unsigned short SEQ_STAND_BY_ON[] = {
356 	0x10, COMMAND_ONLY,
357 
358 	ENDDEF, 0x0000
359 };
360 
361 static const unsigned short SEQ_DISPLAY_ON[] = {
362 	0x29, COMMAND_ONLY,
363 
364 	ENDDEF, 0x0000
365 };
366 
367 
s6e63m0_spi_write_byte(struct s6e63m0 * lcd,int addr,int data)368 static 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 
s6e63m0_spi_write(struct s6e63m0 * lcd,unsigned char address,unsigned char command)386 static 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 
s6e63m0_panel_send_sequence(struct s6e63m0 * lcd,const unsigned short * wbuf)399 static 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 			udelay(wbuf[i+1]*1000);
411 		i += 2;
412 	}
413 
414 	return ret;
415 }
416 
_s6e63m0_gamma_ctl(struct s6e63m0 * lcd,const unsigned int * gamma)417 static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
418 {
419 	unsigned int i = 0;
420 	int ret = 0;
421 
422 	/* disable gamma table updating. */
423 	ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
424 	if (ret) {
425 		dev_err(lcd->dev, "failed to disable gamma table updating.\n");
426 		goto gamma_err;
427 	}
428 
429 	for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
430 		ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
431 		if (ret) {
432 			dev_err(lcd->dev, "failed to set gamma table.\n");
433 			goto gamma_err;
434 		}
435 	}
436 
437 	/* update gamma table. */
438 	ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
439 	if (ret)
440 		dev_err(lcd->dev, "failed to update gamma table.\n");
441 
442 gamma_err:
443 	return ret;
444 }
445 
s6e63m0_gamma_ctl(struct s6e63m0 * lcd,int gamma)446 static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
447 {
448 	int ret = 0;
449 
450 	ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
451 
452 	return ret;
453 }
454 
455 
s6e63m0_ldi_init(struct s6e63m0 * lcd)456 static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
457 {
458 	int ret, i;
459 	const unsigned short *init_seq[] = {
460 		SEQ_PANEL_CONDITION_SET,
461 		SEQ_DISPLAY_CONDITION_SET,
462 		SEQ_GAMMA_SETTING,
463 		SEQ_ETC_CONDITION_SET,
464 		SEQ_ACL_ON,
465 		SEQ_ELVSS_ON,
466 	};
467 
468 	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
469 		ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
470 		if (ret)
471 			break;
472 	}
473 
474 	return ret;
475 }
476 
s6e63m0_ldi_enable(struct s6e63m0 * lcd)477 static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
478 {
479 	int ret = 0, i;
480 	const unsigned short *enable_seq[] = {
481 		SEQ_STAND_BY_OFF,
482 		SEQ_DISPLAY_ON,
483 	};
484 
485 	for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
486 		ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
487 		if (ret)
488 			break;
489 	}
490 
491 	return ret;
492 }
493 
s6e63m0_ldi_disable(struct s6e63m0 * lcd)494 static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
495 {
496 	int ret;
497 
498 	ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON);
499 
500 	return ret;
501 }
502 
s6e63m0_power_on(struct s6e63m0 * lcd)503 static int s6e63m0_power_on(struct s6e63m0 *lcd)
504 {
505 	int ret = 0;
506 	struct lcd_platform_data *pd = NULL;
507 	struct backlight_device *bd = NULL;
508 
509 	pd = lcd->lcd_pd;
510 	if (!pd) {
511 		dev_err(lcd->dev, "platform data is NULL.\n");
512 		return -EFAULT;
513 	}
514 
515 	bd = lcd->bd;
516 	if (!bd) {
517 		dev_err(lcd->dev, "backlight device is NULL.\n");
518 		return -EFAULT;
519 	}
520 
521 	if (!pd->power_on) {
522 		dev_err(lcd->dev, "power_on is NULL.\n");
523 		return -EFAULT;
524 	} else {
525 		pd->power_on(lcd->ld, 1);
526 		mdelay(pd->power_on_delay);
527 	}
528 
529 	if (!pd->reset) {
530 		dev_err(lcd->dev, "reset is NULL.\n");
531 		return -EFAULT;
532 	} else {
533 		pd->reset(lcd->ld);
534 		mdelay(pd->reset_delay);
535 	}
536 
537 	ret = s6e63m0_ldi_init(lcd);
538 	if (ret) {
539 		dev_err(lcd->dev, "failed to initialize ldi.\n");
540 		return ret;
541 	}
542 
543 	ret = s6e63m0_ldi_enable(lcd);
544 	if (ret) {
545 		dev_err(lcd->dev, "failed to enable ldi.\n");
546 		return ret;
547 	}
548 
549 	/* set brightness to current value after power on or resume. */
550 	ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
551 	if (ret) {
552 		dev_err(lcd->dev, "lcd gamma setting failed.\n");
553 		return ret;
554 	}
555 
556 	return 0;
557 }
558 
s6e63m0_power_off(struct s6e63m0 * lcd)559 static int s6e63m0_power_off(struct s6e63m0 *lcd)
560 {
561 	int ret = 0;
562 	struct lcd_platform_data *pd = NULL;
563 
564 	pd = lcd->lcd_pd;
565 	if (!pd) {
566 		dev_err(lcd->dev, "platform data is NULL.\n");
567 		return -EFAULT;
568 	}
569 
570 	ret = s6e63m0_ldi_disable(lcd);
571 	if (ret) {
572 		dev_err(lcd->dev, "lcd setting failed.\n");
573 		return -EIO;
574 	}
575 
576 	mdelay(pd->power_off_delay);
577 
578 	if (!pd->power_on) {
579 		dev_err(lcd->dev, "power_on is NULL.\n");
580 		return -EFAULT;
581 	} else
582 		pd->power_on(lcd->ld, 0);
583 
584 	return 0;
585 }
586 
s6e63m0_power(struct s6e63m0 * lcd,int power)587 static int s6e63m0_power(struct s6e63m0 *lcd, int power)
588 {
589 	int ret = 0;
590 
591 	if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
592 		ret = s6e63m0_power_on(lcd);
593 	else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
594 		ret = s6e63m0_power_off(lcd);
595 
596 	if (!ret)
597 		lcd->power = power;
598 
599 	return ret;
600 }
601 
s6e63m0_set_power(struct lcd_device * ld,int power)602 static int s6e63m0_set_power(struct lcd_device *ld, int power)
603 {
604 	struct s6e63m0 *lcd = lcd_get_data(ld);
605 
606 	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
607 		power != FB_BLANK_NORMAL) {
608 		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
609 		return -EINVAL;
610 	}
611 
612 	return s6e63m0_power(lcd, power);
613 }
614 
s6e63m0_get_power(struct lcd_device * ld)615 static int s6e63m0_get_power(struct lcd_device *ld)
616 {
617 	struct s6e63m0 *lcd = lcd_get_data(ld);
618 
619 	return lcd->power;
620 }
621 
s6e63m0_get_brightness(struct backlight_device * bd)622 static int s6e63m0_get_brightness(struct backlight_device *bd)
623 {
624 	return bd->props.brightness;
625 }
626 
s6e63m0_set_brightness(struct backlight_device * bd)627 static int s6e63m0_set_brightness(struct backlight_device *bd)
628 {
629 	int ret = 0, brightness = bd->props.brightness;
630 	struct s6e63m0 *lcd = bl_get_data(bd);
631 
632 	if (brightness < MIN_BRIGHTNESS ||
633 		brightness > bd->props.max_brightness) {
634 		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
635 			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
636 		return -EINVAL;
637 	}
638 
639 	ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
640 	if (ret) {
641 		dev_err(&bd->dev, "lcd brightness setting failed.\n");
642 		return -EIO;
643 	}
644 
645 	return ret;
646 }
647 
648 static struct lcd_ops s6e63m0_lcd_ops = {
649 	.set_power = s6e63m0_set_power,
650 	.get_power = s6e63m0_get_power,
651 };
652 
653 static const struct backlight_ops s6e63m0_backlight_ops  = {
654 	.get_brightness = s6e63m0_get_brightness,
655 	.update_status = s6e63m0_set_brightness,
656 };
657 
s6e63m0_sysfs_show_gamma_mode(struct device * dev,struct device_attribute * attr,char * buf)658 static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
659 				      struct device_attribute *attr, char *buf)
660 {
661 	struct s6e63m0 *lcd = dev_get_drvdata(dev);
662 	char temp[10];
663 
664 	switch (lcd->gamma_mode) {
665 	case 0:
666 		sprintf(temp, "2.2 mode\n");
667 		strcat(buf, temp);
668 		break;
669 	case 1:
670 		sprintf(temp, "1.9 mode\n");
671 		strcat(buf, temp);
672 		break;
673 	case 2:
674 		sprintf(temp, "1.7 mode\n");
675 		strcat(buf, temp);
676 		break;
677 	default:
678 		dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
679 		break;
680 	}
681 
682 	return strlen(buf);
683 }
684 
s6e63m0_sysfs_store_gamma_mode(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)685 static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
686 				       struct device_attribute *attr,
687 				       const char *buf, size_t len)
688 {
689 	struct s6e63m0 *lcd = dev_get_drvdata(dev);
690 	struct backlight_device *bd = NULL;
691 	int brightness, rc;
692 
693 	rc = kstrtouint(buf, 0, &lcd->gamma_mode);
694 	if (rc < 0)
695 		return rc;
696 
697 	bd = lcd->bd;
698 
699 	brightness = bd->props.brightness;
700 
701 	switch (lcd->gamma_mode) {
702 	case 0:
703 		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
704 		break;
705 	case 1:
706 		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
707 		break;
708 	case 2:
709 		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
710 		break;
711 	default:
712 		dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
713 		_s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
714 		break;
715 	}
716 	return len;
717 }
718 
719 static DEVICE_ATTR(gamma_mode, 0644,
720 		s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
721 
s6e63m0_sysfs_show_gamma_table(struct device * dev,struct device_attribute * attr,char * buf)722 static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
723 				      struct device_attribute *attr, char *buf)
724 {
725 	struct s6e63m0 *lcd = dev_get_drvdata(dev);
726 	char temp[3];
727 
728 	sprintf(temp, "%d\n", lcd->gamma_table_count);
729 	strcpy(buf, temp);
730 
731 	return strlen(buf);
732 }
733 static DEVICE_ATTR(gamma_table, 0444,
734 		s6e63m0_sysfs_show_gamma_table, NULL);
735 
s6e63m0_probe(struct spi_device * spi)736 static int __devinit s6e63m0_probe(struct spi_device *spi)
737 {
738 	int ret = 0;
739 	struct s6e63m0 *lcd = NULL;
740 	struct lcd_device *ld = NULL;
741 	struct backlight_device *bd = NULL;
742 	struct backlight_properties props;
743 
744 	lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
745 	if (!lcd)
746 		return -ENOMEM;
747 
748 	/* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
749 	spi->bits_per_word = 9;
750 
751 	ret = spi_setup(spi);
752 	if (ret < 0) {
753 		dev_err(&spi->dev, "spi setup failed.\n");
754 		goto out_free_lcd;
755 	}
756 
757 	lcd->spi = spi;
758 	lcd->dev = &spi->dev;
759 
760 	lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
761 	if (!lcd->lcd_pd) {
762 		dev_err(&spi->dev, "platform data is NULL.\n");
763 		goto out_free_lcd;
764 	}
765 
766 	ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
767 	if (IS_ERR(ld)) {
768 		ret = PTR_ERR(ld);
769 		goto out_free_lcd;
770 	}
771 
772 	lcd->ld = ld;
773 
774 	memset(&props, 0, sizeof(struct backlight_properties));
775 	props.type = BACKLIGHT_RAW;
776 	props.max_brightness = MAX_BRIGHTNESS;
777 
778 	bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
779 		&s6e63m0_backlight_ops, &props);
780 	if (IS_ERR(bd)) {
781 		ret =  PTR_ERR(bd);
782 		goto out_lcd_unregister;
783 	}
784 
785 	bd->props.brightness = MAX_BRIGHTNESS;
786 	lcd->bd = bd;
787 
788 	/*
789 	 * it gets gamma table count available so it gets user
790 	 * know that.
791 	 */
792 	lcd->gamma_table_count =
793 	    sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
794 
795 	ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
796 	if (ret < 0)
797 		dev_err(&(spi->dev), "failed to add sysfs entries\n");
798 
799 	ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
800 	if (ret < 0)
801 		dev_err(&(spi->dev), "failed to add sysfs entries\n");
802 
803 	/*
804 	 * if lcd panel was on from bootloader like u-boot then
805 	 * do not lcd on.
806 	 */
807 	if (!lcd->lcd_pd->lcd_enabled) {
808 		/*
809 		 * if lcd panel was off from bootloader then
810 		 * current lcd status is powerdown and then
811 		 * it enables lcd panel.
812 		 */
813 		lcd->power = FB_BLANK_POWERDOWN;
814 
815 		s6e63m0_power(lcd, FB_BLANK_UNBLANK);
816 	} else
817 		lcd->power = FB_BLANK_UNBLANK;
818 
819 	dev_set_drvdata(&spi->dev, lcd);
820 
821 	dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
822 
823 	return 0;
824 
825 out_lcd_unregister:
826 	lcd_device_unregister(ld);
827 out_free_lcd:
828 	kfree(lcd);
829 	return ret;
830 }
831 
s6e63m0_remove(struct spi_device * spi)832 static int __devexit s6e63m0_remove(struct spi_device *spi)
833 {
834 	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
835 
836 	s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
837 	device_remove_file(&spi->dev, &dev_attr_gamma_table);
838 	device_remove_file(&spi->dev, &dev_attr_gamma_mode);
839 	backlight_device_unregister(lcd->bd);
840 	lcd_device_unregister(lcd->ld);
841 	kfree(lcd);
842 
843 	return 0;
844 }
845 
846 #if defined(CONFIG_PM)
847 static unsigned int before_power;
848 
s6e63m0_suspend(struct spi_device * spi,pm_message_t mesg)849 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
850 {
851 	int ret = 0;
852 	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
853 
854 	dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
855 
856 	before_power = lcd->power;
857 
858 	/*
859 	 * when lcd panel is suspend, lcd panel becomes off
860 	 * regardless of status.
861 	 */
862 	ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
863 
864 	return ret;
865 }
866 
s6e63m0_resume(struct spi_device * spi)867 static int s6e63m0_resume(struct spi_device *spi)
868 {
869 	int ret = 0;
870 	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
871 
872 	/*
873 	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
874 	 * (at that time, before_power is FB_BLANK_UNBLANK) then
875 	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
876 	 */
877 	if (before_power == FB_BLANK_UNBLANK)
878 		lcd->power = FB_BLANK_POWERDOWN;
879 
880 	dev_dbg(&spi->dev, "before_power = %d\n", before_power);
881 
882 	ret = s6e63m0_power(lcd, before_power);
883 
884 	return ret;
885 }
886 #else
887 #define s6e63m0_suspend		NULL
888 #define s6e63m0_resume		NULL
889 #endif
890 
891 /* Power down all displays on reboot, poweroff or halt. */
s6e63m0_shutdown(struct spi_device * spi)892 static void s6e63m0_shutdown(struct spi_device *spi)
893 {
894 	struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
895 
896 	s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
897 }
898 
899 static struct spi_driver s6e63m0_driver = {
900 	.driver = {
901 		.name	= "s6e63m0",
902 		.bus	= &spi_bus_type,
903 		.owner	= THIS_MODULE,
904 	},
905 	.probe		= s6e63m0_probe,
906 	.remove		= __devexit_p(s6e63m0_remove),
907 	.shutdown	= s6e63m0_shutdown,
908 	.suspend	= s6e63m0_suspend,
909 	.resume		= s6e63m0_resume,
910 };
911 
s6e63m0_init(void)912 static int __init s6e63m0_init(void)
913 {
914 	return spi_register_driver(&s6e63m0_driver);
915 }
916 
s6e63m0_exit(void)917 static void __exit s6e63m0_exit(void)
918 {
919 	spi_unregister_driver(&s6e63m0_driver);
920 }
921 
922 module_init(s6e63m0_init);
923 module_exit(s6e63m0_exit);
924 
925 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
926 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
927 MODULE_LICENSE("GPL");
928 
929