1 /*
2  * ams369fg06 AMOLED LCD panel driver.
3  *
4  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
5  * Author: Jingoo Han  <jg1.han@samsung.com>
6  *
7  * Derived from drivers/video/s6e63m0.c
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23 
24 #include <linux/wait.h>
25 #include <linux/module.h>
26 #include <linux/fb.h>
27 #include <linux/delay.h>
28 #include <linux/gpio.h>
29 #include <linux/spi/spi.h>
30 #include <linux/lcd.h>
31 #include <linux/backlight.h>
32 
33 #define SLEEPMSEC		0x1000
34 #define ENDDEF			0x2000
35 #define	DEFMASK			0xFF00
36 #define COMMAND_ONLY		0xFE
37 #define DATA_ONLY		0xFF
38 
39 #define MAX_GAMMA_LEVEL		5
40 #define GAMMA_TABLE_COUNT	21
41 
42 #define MIN_BRIGHTNESS		0
43 #define MAX_BRIGHTNESS		255
44 #define DEFAULT_BRIGHTNESS	150
45 
46 struct ams369fg06 {
47 	struct device			*dev;
48 	struct spi_device		*spi;
49 	unsigned int			power;
50 	struct lcd_device		*ld;
51 	struct backlight_device		*bd;
52 	struct lcd_platform_data	*lcd_pd;
53 };
54 
55 static const unsigned short seq_display_on[] = {
56 	0x14, 0x03,
57 	ENDDEF, 0x0000
58 };
59 
60 static const unsigned short seq_display_off[] = {
61 	0x14, 0x00,
62 	ENDDEF, 0x0000
63 };
64 
65 static const unsigned short seq_stand_by_on[] = {
66 	0x1D, 0xA1,
67 	SLEEPMSEC, 200,
68 	ENDDEF, 0x0000
69 };
70 
71 static const unsigned short seq_stand_by_off[] = {
72 	0x1D, 0xA0,
73 	SLEEPMSEC, 250,
74 	ENDDEF, 0x0000
75 };
76 
77 static const unsigned short seq_setting[] = {
78 	0x31, 0x08,
79 	0x32, 0x14,
80 	0x30, 0x02,
81 	0x27, 0x01,
82 	0x12, 0x08,
83 	0x13, 0x08,
84 	0x15, 0x00,
85 	0x16, 0x00,
86 
87 	0xef, 0xd0,
88 	DATA_ONLY, 0xe8,
89 
90 	0x39, 0x44,
91 	0x40, 0x00,
92 	0x41, 0x3f,
93 	0x42, 0x2a,
94 	0x43, 0x27,
95 	0x44, 0x27,
96 	0x45, 0x1f,
97 	0x46, 0x44,
98 	0x50, 0x00,
99 	0x51, 0x00,
100 	0x52, 0x17,
101 	0x53, 0x24,
102 	0x54, 0x26,
103 	0x55, 0x1f,
104 	0x56, 0x43,
105 	0x60, 0x00,
106 	0x61, 0x3f,
107 	0x62, 0x2a,
108 	0x63, 0x25,
109 	0x64, 0x24,
110 	0x65, 0x1b,
111 	0x66, 0x5c,
112 
113 	0x17, 0x22,
114 	0x18, 0x33,
115 	0x19, 0x03,
116 	0x1a, 0x01,
117 	0x22, 0xa4,
118 	0x23, 0x00,
119 	0x26, 0xa0,
120 
121 	0x1d, 0xa0,
122 	SLEEPMSEC, 300,
123 
124 	0x14, 0x03,
125 
126 	ENDDEF, 0x0000
127 };
128 
129 /* gamma value: 2.2 */
130 static const unsigned int ams369fg06_22_250[] = {
131 	0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
132 	0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
133 	0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
134 };
135 
136 static const unsigned int ams369fg06_22_200[] = {
137 	0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
138 	0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
139 	0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
140 };
141 
142 static const unsigned int ams369fg06_22_150[] = {
143 	0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
144 	0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
145 	0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
146 };
147 
148 static const unsigned int ams369fg06_22_100[] = {
149 	0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
150 	0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
151 	0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
152 };
153 
154 static const unsigned int ams369fg06_22_50[] = {
155 	0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
156 	0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
157 	0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
158 };
159 
160 struct ams369fg06_gamma {
161 	unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
162 };
163 
164 static struct ams369fg06_gamma gamma_table = {
165 	.gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
166 	.gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
167 	.gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
168 	.gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
169 	.gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
170 };
171 
172 static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data)
173 {
174 	u16 buf[1];
175 	struct spi_message msg;
176 
177 	struct spi_transfer xfer = {
178 		.len		= 2,
179 		.tx_buf		= buf,
180 	};
181 
182 	buf[0] = (addr << 8) | data;
183 
184 	spi_message_init(&msg);
185 	spi_message_add_tail(&xfer, &msg);
186 
187 	return spi_sync(lcd->spi, &msg);
188 }
189 
190 static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
191 	unsigned char command)
192 {
193 	int ret = 0;
194 
195 	if (address != DATA_ONLY)
196 		ret = ams369fg06_spi_write_byte(lcd, 0x70, address);
197 	if (command != COMMAND_ONLY)
198 		ret = ams369fg06_spi_write_byte(lcd, 0x72, command);
199 
200 	return ret;
201 }
202 
203 static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
204 	const unsigned short *wbuf)
205 {
206 	int ret = 0, i = 0;
207 
208 	while ((wbuf[i] & DEFMASK) != ENDDEF) {
209 		if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
210 			ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
211 			if (ret)
212 				break;
213 		} else
214 			mdelay(wbuf[i+1]);
215 		i += 2;
216 	}
217 
218 	return ret;
219 }
220 
221 static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
222 	const unsigned int *gamma)
223 {
224 	unsigned int i = 0;
225 	int ret = 0;
226 
227 	for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
228 		ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
229 		ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
230 		ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
231 		if (ret) {
232 			dev_err(lcd->dev, "failed to set gamma table.\n");
233 			goto gamma_err;
234 		}
235 	}
236 
237 gamma_err:
238 	return ret;
239 }
240 
241 static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
242 {
243 	int ret = 0;
244 	int gamma = 0;
245 
246 	if ((brightness >= 0) && (brightness <= 50))
247 		gamma = 0;
248 	else if ((brightness > 50) && (brightness <= 100))
249 		gamma = 1;
250 	else if ((brightness > 100) && (brightness <= 150))
251 		gamma = 2;
252 	else if ((brightness > 150) && (brightness <= 200))
253 		gamma = 3;
254 	else if ((brightness > 200) && (brightness <= 255))
255 		gamma = 4;
256 
257 	ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
258 
259 	return ret;
260 }
261 
262 static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
263 {
264 	int ret, i;
265 	static const unsigned short *init_seq[] = {
266 		seq_setting,
267 		seq_stand_by_off,
268 	};
269 
270 	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
271 		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
272 		if (ret)
273 			break;
274 	}
275 
276 	return ret;
277 }
278 
279 static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
280 {
281 	int ret, i;
282 	static const unsigned short *init_seq[] = {
283 		seq_stand_by_off,
284 		seq_display_on,
285 	};
286 
287 	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
288 		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
289 		if (ret)
290 			break;
291 	}
292 
293 	return ret;
294 }
295 
296 static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
297 {
298 	int ret, i;
299 
300 	static const unsigned short *init_seq[] = {
301 		seq_display_off,
302 		seq_stand_by_on,
303 	};
304 
305 	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
306 		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
307 		if (ret)
308 			break;
309 	}
310 
311 	return ret;
312 }
313 
314 static int ams369fg06_power_is_on(int power)
315 {
316 	return ((power) <= FB_BLANK_NORMAL);
317 }
318 
319 static int ams369fg06_power_on(struct ams369fg06 *lcd)
320 {
321 	int ret = 0;
322 	struct lcd_platform_data *pd = NULL;
323 	struct backlight_device *bd = NULL;
324 
325 	pd = lcd->lcd_pd;
326 	if (!pd) {
327 		dev_err(lcd->dev, "platform data is NULL.\n");
328 		return -EFAULT;
329 	}
330 
331 	bd = lcd->bd;
332 	if (!bd) {
333 		dev_err(lcd->dev, "backlight device is NULL.\n");
334 		return -EFAULT;
335 	}
336 
337 	if (!pd->power_on) {
338 		dev_err(lcd->dev, "power_on is NULL.\n");
339 		return -EFAULT;
340 	} else {
341 		pd->power_on(lcd->ld, 1);
342 		mdelay(pd->power_on_delay);
343 	}
344 
345 	if (!pd->reset) {
346 		dev_err(lcd->dev, "reset is NULL.\n");
347 		return -EFAULT;
348 	} else {
349 		pd->reset(lcd->ld);
350 		mdelay(pd->reset_delay);
351 	}
352 
353 	ret = ams369fg06_ldi_init(lcd);
354 	if (ret) {
355 		dev_err(lcd->dev, "failed to initialize ldi.\n");
356 		return ret;
357 	}
358 
359 	ret = ams369fg06_ldi_enable(lcd);
360 	if (ret) {
361 		dev_err(lcd->dev, "failed to enable ldi.\n");
362 		return ret;
363 	}
364 
365 	/* set brightness to current value after power on or resume. */
366 	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
367 	if (ret) {
368 		dev_err(lcd->dev, "lcd gamma setting failed.\n");
369 		return ret;
370 	}
371 
372 	return 0;
373 }
374 
375 static int ams369fg06_power_off(struct ams369fg06 *lcd)
376 {
377 	int ret = 0;
378 	struct lcd_platform_data *pd = NULL;
379 
380 	pd = lcd->lcd_pd;
381 	if (!pd) {
382 		dev_err(lcd->dev, "platform data is NULL\n");
383 		return -EFAULT;
384 	}
385 
386 	ret = ams369fg06_ldi_disable(lcd);
387 	if (ret) {
388 		dev_err(lcd->dev, "lcd setting failed.\n");
389 		return -EIO;
390 	}
391 
392 	mdelay(pd->power_off_delay);
393 
394 	if (!pd->power_on) {
395 		dev_err(lcd->dev, "power_on is NULL.\n");
396 		return -EFAULT;
397 	} else
398 		pd->power_on(lcd->ld, 0);
399 
400 	return 0;
401 }
402 
403 static int ams369fg06_power(struct ams369fg06 *lcd, int power)
404 {
405 	int ret = 0;
406 
407 	if (ams369fg06_power_is_on(power) &&
408 		!ams369fg06_power_is_on(lcd->power))
409 		ret = ams369fg06_power_on(lcd);
410 	else if (!ams369fg06_power_is_on(power) &&
411 		ams369fg06_power_is_on(lcd->power))
412 		ret = ams369fg06_power_off(lcd);
413 
414 	if (!ret)
415 		lcd->power = power;
416 
417 	return ret;
418 }
419 
420 static int ams369fg06_get_power(struct lcd_device *ld)
421 {
422 	struct ams369fg06 *lcd = lcd_get_data(ld);
423 
424 	return lcd->power;
425 }
426 
427 static int ams369fg06_set_power(struct lcd_device *ld, int power)
428 {
429 	struct ams369fg06 *lcd = lcd_get_data(ld);
430 
431 	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
432 		power != FB_BLANK_NORMAL) {
433 		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
434 		return -EINVAL;
435 	}
436 
437 	return ams369fg06_power(lcd, power);
438 }
439 
440 static int ams369fg06_get_brightness(struct backlight_device *bd)
441 {
442 	return bd->props.brightness;
443 }
444 
445 static int ams369fg06_set_brightness(struct backlight_device *bd)
446 {
447 	int ret = 0;
448 	int brightness = bd->props.brightness;
449 	struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev);
450 
451 	if (brightness < MIN_BRIGHTNESS ||
452 		brightness > bd->props.max_brightness) {
453 		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
454 			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
455 		return -EINVAL;
456 	}
457 
458 	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
459 	if (ret) {
460 		dev_err(&bd->dev, "lcd brightness setting failed.\n");
461 		return -EIO;
462 	}
463 
464 	return ret;
465 }
466 
467 static struct lcd_ops ams369fg06_lcd_ops = {
468 	.get_power = ams369fg06_get_power,
469 	.set_power = ams369fg06_set_power,
470 };
471 
472 static const struct backlight_ops ams369fg06_backlight_ops = {
473 	.get_brightness = ams369fg06_get_brightness,
474 	.update_status = ams369fg06_set_brightness,
475 };
476 
477 static int __devinit ams369fg06_probe(struct spi_device *spi)
478 {
479 	int ret = 0;
480 	struct ams369fg06 *lcd = NULL;
481 	struct lcd_device *ld = NULL;
482 	struct backlight_device *bd = NULL;
483 	struct backlight_properties props;
484 
485 	lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL);
486 	if (!lcd)
487 		return -ENOMEM;
488 
489 	/* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
490 	spi->bits_per_word = 16;
491 
492 	ret = spi_setup(spi);
493 	if (ret < 0) {
494 		dev_err(&spi->dev, "spi setup failed.\n");
495 		return ret;
496 	}
497 
498 	lcd->spi = spi;
499 	lcd->dev = &spi->dev;
500 
501 	lcd->lcd_pd = spi->dev.platform_data;
502 	if (!lcd->lcd_pd) {
503 		dev_err(&spi->dev, "platform data is NULL\n");
504 		return -EFAULT;
505 	}
506 
507 	ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
508 		&ams369fg06_lcd_ops);
509 	if (IS_ERR(ld))
510 		return PTR_ERR(ld);
511 
512 	lcd->ld = ld;
513 
514 	memset(&props, 0, sizeof(struct backlight_properties));
515 	props.type = BACKLIGHT_RAW;
516 	props.max_brightness = MAX_BRIGHTNESS;
517 
518 	bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd,
519 		&ams369fg06_backlight_ops, &props);
520 	if (IS_ERR(bd)) {
521 		ret =  PTR_ERR(bd);
522 		goto out_lcd_unregister;
523 	}
524 
525 	bd->props.brightness = DEFAULT_BRIGHTNESS;
526 	lcd->bd = bd;
527 
528 	if (!lcd->lcd_pd->lcd_enabled) {
529 		/*
530 		 * if lcd panel was off from bootloader then
531 		 * current lcd status is powerdown and then
532 		 * it enables lcd panel.
533 		 */
534 		lcd->power = FB_BLANK_POWERDOWN;
535 
536 		ams369fg06_power(lcd, FB_BLANK_UNBLANK);
537 	} else
538 		lcd->power = FB_BLANK_UNBLANK;
539 
540 	dev_set_drvdata(&spi->dev, lcd);
541 
542 	dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
543 
544 	return 0;
545 
546 out_lcd_unregister:
547 	lcd_device_unregister(ld);
548 	return ret;
549 }
550 
551 static int __devexit ams369fg06_remove(struct spi_device *spi)
552 {
553 	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
554 
555 	ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
556 	backlight_device_unregister(lcd->bd);
557 	lcd_device_unregister(lcd->ld);
558 
559 	return 0;
560 }
561 
562 #if defined(CONFIG_PM)
563 static unsigned int before_power;
564 
565 static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg)
566 {
567 	int ret = 0;
568 	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
569 
570 	dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
571 
572 	before_power = lcd->power;
573 
574 	/*
575 	 * when lcd panel is suspend, lcd panel becomes off
576 	 * regardless of status.
577 	 */
578 	ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
579 
580 	return ret;
581 }
582 
583 static int ams369fg06_resume(struct spi_device *spi)
584 {
585 	int ret = 0;
586 	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
587 
588 	/*
589 	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
590 	 * (at that time, before_power is FB_BLANK_UNBLANK) then
591 	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
592 	 */
593 	if (before_power == FB_BLANK_UNBLANK)
594 		lcd->power = FB_BLANK_POWERDOWN;
595 
596 	dev_dbg(&spi->dev, "before_power = %d\n", before_power);
597 
598 	ret = ams369fg06_power(lcd, before_power);
599 
600 	return ret;
601 }
602 #else
603 #define ams369fg06_suspend	NULL
604 #define ams369fg06_resume	NULL
605 #endif
606 
607 static void ams369fg06_shutdown(struct spi_device *spi)
608 {
609 	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
610 
611 	ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
612 }
613 
614 static struct spi_driver ams369fg06_driver = {
615 	.driver = {
616 		.name	= "ams369fg06",
617 		.owner	= THIS_MODULE,
618 	},
619 	.probe		= ams369fg06_probe,
620 	.remove		= __devexit_p(ams369fg06_remove),
621 	.shutdown	= ams369fg06_shutdown,
622 	.suspend	= ams369fg06_suspend,
623 	.resume		= ams369fg06_resume,
624 };
625 
626 module_spi_driver(ams369fg06_driver);
627 
628 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
629 MODULE_DESCRIPTION("ams369fg06 LCD Driver");
630 MODULE_LICENSE("GPL");
631