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