1/* linux/drivers/video/exynos/s6e8ax0.c
2 *
3 * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
4 *
5 * Inki Dae, <inki.dae@samsung.com>
6 * Donghwa Lee, <dh09.lee@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11*/
12
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/mutex.h>
17#include <linux/wait.h>
18#include <linux/ctype.h>
19#include <linux/io.h>
20#include <linux/delay.h>
21#include <linux/irq.h>
22#include <linux/interrupt.h>
23#include <linux/lcd.h>
24#include <linux/fb.h>
25#include <linux/backlight.h>
26#include <linux/regulator/consumer.h>
27
28#include <video/mipi_display.h>
29#include <video/exynos_mipi_dsim.h>
30
31#define LDI_MTP_LENGTH		24
32#define DSIM_PM_STABLE_TIME	10
33#define MIN_BRIGHTNESS		0
34#define MAX_BRIGHTNESS		24
35#define GAMMA_TABLE_COUNT	26
36
37#define POWER_IS_ON(pwr)	((pwr) == FB_BLANK_UNBLANK)
38#define POWER_IS_OFF(pwr)	((pwr) == FB_BLANK_POWERDOWN)
39#define POWER_IS_NRM(pwr)	((pwr) == FB_BLANK_NORMAL)
40
41#define lcd_to_master(a)	(a->dsim_dev->master)
42#define lcd_to_master_ops(a)	((lcd_to_master(a))->master_ops)
43
44enum {
45	DSIM_NONE_STATE = 0,
46	DSIM_RESUME_COMPLETE = 1,
47	DSIM_FRAME_DONE = 2,
48};
49
50struct s6e8ax0 {
51	struct device	*dev;
52	unsigned int			power;
53	unsigned int			id;
54	unsigned int			gamma;
55	unsigned int			acl_enable;
56	unsigned int			cur_acl;
57
58	struct lcd_device	*ld;
59	struct backlight_device	*bd;
60
61	struct mipi_dsim_lcd_device	*dsim_dev;
62	struct lcd_platform_data	*ddi_pd;
63	struct mutex			lock;
64	bool  enabled;
65};
66
67
68static struct regulator_bulk_data supplies[] = {
69	{ .supply = "vdd3", },
70	{ .supply = "vci", },
71};
72
73static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd)
74{
75	int ret = 0;
76	struct lcd_platform_data *pd = NULL;
77
78	pd = lcd->ddi_pd;
79	mutex_lock(&lcd->lock);
80	if (!lcd->enabled) {
81		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
82		if (ret)
83			goto out;
84
85		lcd->enabled = true;
86	}
87	msleep(pd->power_on_delay);
88out:
89	mutex_unlock(&lcd->lock);
90}
91
92static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd)
93{
94	int ret = 0;
95
96	mutex_lock(&lcd->lock);
97	if (lcd->enabled) {
98		ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
99		if (ret)
100			goto out;
101
102		lcd->enabled = false;
103	}
104out:
105	mutex_unlock(&lcd->lock);
106}
107
108static const unsigned char s6e8ax0_22_gamma_30[] = {
109	0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
110	0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
111	0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
112};
113
114static const unsigned char s6e8ax0_22_gamma_50[] = {
115	0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
116	0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
117	0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
118};
119
120static const unsigned char s6e8ax0_22_gamma_60[] = {
121	0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
122	0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
123	0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
124};
125
126static const unsigned char s6e8ax0_22_gamma_70[] = {
127	0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
128	0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
129	0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
130};
131
132static const unsigned char s6e8ax0_22_gamma_80[] = {
133	0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
134	0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
135	0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
136};
137
138static const unsigned char s6e8ax0_22_gamma_90[] = {
139	0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
140	0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
141	0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
142};
143
144static const unsigned char s6e8ax0_22_gamma_100[] = {
145	0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
146	0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
147	0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
148};
149
150static const unsigned char s6e8ax0_22_gamma_120[] = {
151	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
152	0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
153	0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
154};
155
156static const unsigned char s6e8ax0_22_gamma_130[] = {
157	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
158	0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
159	0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
160};
161
162static const unsigned char s6e8ax0_22_gamma_140[] = {
163	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
164	0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
165	0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
166};
167
168static const unsigned char s6e8ax0_22_gamma_150[] = {
169	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
170	0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
171	0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
172};
173
174static const unsigned char s6e8ax0_22_gamma_160[] = {
175	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
176	0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
177	0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
178};
179
180static const unsigned char s6e8ax0_22_gamma_170[] = {
181	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
182	0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
183	0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
184};
185
186static const unsigned char s6e8ax0_22_gamma_180[] = {
187	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
188	0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
189	0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
190};
191
192static const unsigned char s6e8ax0_22_gamma_190[] = {
193	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
194	0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
195	0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
196};
197
198static const unsigned char s6e8ax0_22_gamma_200[] = {
199	0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
200	0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
201	0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
202};
203
204static const unsigned char s6e8ax0_22_gamma_210[] = {
205	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
206	0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
207	0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
208};
209
210static const unsigned char s6e8ax0_22_gamma_220[] = {
211	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
212	0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
213	0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
214};
215
216static const unsigned char s6e8ax0_22_gamma_230[] = {
217	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
218	0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
219	0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
220};
221
222static const unsigned char s6e8ax0_22_gamma_240[] = {
223	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
224	0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
225	0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
226};
227
228static const unsigned char s6e8ax0_22_gamma_250[] = {
229	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
230	0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
231	0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
232};
233
234static const unsigned char s6e8ax0_22_gamma_260[] = {
235	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
236	0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
237	0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
238};
239
240static const unsigned char s6e8ax0_22_gamma_270[] = {
241	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
242	0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
243	0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
244};
245
246static const unsigned char s6e8ax0_22_gamma_280[] = {
247	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
248	0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
249	0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
250};
251
252static const unsigned char s6e8ax0_22_gamma_300[] = {
253	0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
254	0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
255	0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
256};
257
258static const unsigned char *s6e8ax0_22_gamma_table[] = {
259	s6e8ax0_22_gamma_30,
260	s6e8ax0_22_gamma_50,
261	s6e8ax0_22_gamma_60,
262	s6e8ax0_22_gamma_70,
263	s6e8ax0_22_gamma_80,
264	s6e8ax0_22_gamma_90,
265	s6e8ax0_22_gamma_100,
266	s6e8ax0_22_gamma_120,
267	s6e8ax0_22_gamma_130,
268	s6e8ax0_22_gamma_140,
269	s6e8ax0_22_gamma_150,
270	s6e8ax0_22_gamma_160,
271	s6e8ax0_22_gamma_170,
272	s6e8ax0_22_gamma_180,
273	s6e8ax0_22_gamma_190,
274	s6e8ax0_22_gamma_200,
275	s6e8ax0_22_gamma_210,
276	s6e8ax0_22_gamma_220,
277	s6e8ax0_22_gamma_230,
278	s6e8ax0_22_gamma_240,
279	s6e8ax0_22_gamma_250,
280	s6e8ax0_22_gamma_260,
281	s6e8ax0_22_gamma_270,
282	s6e8ax0_22_gamma_280,
283	s6e8ax0_22_gamma_300,
284};
285
286static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
287{
288	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
289
290	static const unsigned char data_to_send[] = {
291		0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
292		0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
293		0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
294		0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
295	};
296	static const unsigned char data_to_send_panel_reverse[] = {
297		0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
298		0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
299		0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
300		0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
301	};
302
303	if (lcd->dsim_dev->panel_reverse)
304		ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
305				data_to_send_panel_reverse,
306				ARRAY_SIZE(data_to_send_panel_reverse));
307	else
308		ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
309				data_to_send, ARRAY_SIZE(data_to_send));
310}
311
312static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
313{
314	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
315	static const unsigned char data_to_send[] = {
316		0xf2, 0x80, 0x03, 0x0d
317	};
318
319	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
320		data_to_send, ARRAY_SIZE(data_to_send));
321}
322
323/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
324static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
325{
326	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
327	unsigned int gamma = lcd->bd->props.brightness;
328
329	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
330			s6e8ax0_22_gamma_table[gamma],
331			GAMMA_TABLE_COUNT);
332}
333
334static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
335{
336	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
337	static const unsigned char data_to_send[] = {
338		0xf7, 0x03
339	};
340
341	ops->cmd_write(lcd_to_master(lcd),
342		MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send,
343		ARRAY_SIZE(data_to_send));
344}
345
346static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
347{
348	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
349	static const unsigned char data_to_send[] = {
350		0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
351		0x0d, 0x00, 0x00
352	};
353
354	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
355		data_to_send, ARRAY_SIZE(data_to_send));
356}
357
358static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
359{
360	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
361	static const unsigned char data_to_send[] = {
362		0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
363		0x00
364	};
365
366	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
367		data_to_send, ARRAY_SIZE(data_to_send));
368}
369
370static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
371{
372	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
373	static const unsigned char data_to_send[] = {
374		0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
375	};
376
377	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
378		data_to_send, ARRAY_SIZE(data_to_send));
379}
380
381static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
382{
383	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
384	static const unsigned char data_to_send[] = {
385		0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
386	};
387
388	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
389		data_to_send, ARRAY_SIZE(data_to_send));
390}
391
392static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
393{
394	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
395	static const unsigned char data_to_send[] = {
396		0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
397	};
398
399	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
400		data_to_send, ARRAY_SIZE(data_to_send));
401}
402static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
403{
404	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
405	static const unsigned char data_to_send[] = {
406		0xe3, 0x40
407	};
408
409	ops->cmd_write(lcd_to_master(lcd),
410		MIPI_DSI_DCS_SHORT_WRITE_PARAM,
411		data_to_send, ARRAY_SIZE(data_to_send));
412}
413
414static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
415{
416	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
417	static const unsigned char data_to_send[] = {
418		0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
419	};
420
421	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
422		data_to_send, ARRAY_SIZE(data_to_send));
423}
424
425static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
426{
427	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
428	static const unsigned char data_to_send[] = {
429		0xb1, 0x04, 0x00
430	};
431
432	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
433		data_to_send, ARRAY_SIZE(data_to_send));
434}
435
436static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
437{
438	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
439	static const unsigned char data_to_send[] = {
440		0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
441		0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
442		0x64, 0xaf
443	};
444
445	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
446		data_to_send, ARRAY_SIZE(data_to_send));
447}
448
449static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
450{
451	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
452	static const unsigned char data_to_send[] = {
453		0x10, 0x00
454	};
455
456	ops->cmd_write(lcd_to_master(lcd),
457		MIPI_DSI_DCS_SHORT_WRITE,
458		data_to_send, ARRAY_SIZE(data_to_send));
459}
460
461static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
462{
463	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
464	static const unsigned char data_to_send[] = {
465		0x11, 0x00
466	};
467
468	ops->cmd_write(lcd_to_master(lcd),
469		MIPI_DSI_DCS_SHORT_WRITE,
470		data_to_send, ARRAY_SIZE(data_to_send));
471}
472
473static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
474{
475	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
476	static const unsigned char data_to_send[] = {
477		0x29, 0x00
478	};
479
480	ops->cmd_write(lcd_to_master(lcd),
481		MIPI_DSI_DCS_SHORT_WRITE,
482		data_to_send, ARRAY_SIZE(data_to_send));
483}
484
485static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
486{
487	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
488	static const unsigned char data_to_send[] = {
489		0x28, 0x00
490	};
491
492	ops->cmd_write(lcd_to_master(lcd),
493		MIPI_DSI_DCS_SHORT_WRITE,
494		data_to_send, ARRAY_SIZE(data_to_send));
495}
496
497static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
498{
499	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
500	static const unsigned char data_to_send[] = {
501		0xf0, 0x5a, 0x5a
502	};
503
504	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
505		data_to_send, ARRAY_SIZE(data_to_send));
506}
507
508static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
509{
510	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
511	static const unsigned char data_to_send[] = {
512		0xc0, 0x01
513	};
514
515	ops->cmd_write(lcd_to_master(lcd),
516		MIPI_DSI_DCS_SHORT_WRITE,
517		data_to_send, ARRAY_SIZE(data_to_send));
518}
519
520static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
521{
522	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
523	static const unsigned char data_to_send[] = {
524		0xc0, 0x00
525	};
526
527	ops->cmd_write(lcd_to_master(lcd),
528		MIPI_DSI_DCS_SHORT_WRITE,
529		data_to_send, ARRAY_SIZE(data_to_send));
530}
531
532/* Full white 50% reducing setting */
533static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
534{
535	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
536	/* Full white 50% reducing setting */
537	static const unsigned char cutoff_50[] = {
538		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
539		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
540		0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
541		0x3f, 0x46
542	};
543	/* Full white 45% reducing setting */
544	static const unsigned char cutoff_45[] = {
545		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
546		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
547		0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
548		0x37, 0x3d
549	};
550	/* Full white 40% reducing setting */
551	static const unsigned char cutoff_40[] = {
552		0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
553		0x00, 0x00, 0x04, 0xff,	0x00, 0x00, 0x00, 0x00, 0x00,
554		0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
555		0x31, 0x36
556	};
557
558	if (lcd->acl_enable) {
559		if (lcd->cur_acl == 0) {
560			if (lcd->gamma == 0 || lcd->gamma == 1) {
561				s6e8ax0_acl_off(lcd);
562				dev_dbg(&lcd->ld->dev,
563					"cur_acl=%d\n", lcd->cur_acl);
564			} else
565				s6e8ax0_acl_on(lcd);
566		}
567		switch (lcd->gamma) {
568		case 0: /* 30cd */
569			s6e8ax0_acl_off(lcd);
570			lcd->cur_acl = 0;
571			break;
572		case 1 ... 3: /* 50cd ~ 90cd */
573			ops->cmd_write(lcd_to_master(lcd),
574				MIPI_DSI_DCS_LONG_WRITE,
575				cutoff_40,
576				ARRAY_SIZE(cutoff_40));
577			lcd->cur_acl = 40;
578			break;
579		case 4 ... 7: /* 120cd ~ 210cd */
580			ops->cmd_write(lcd_to_master(lcd),
581				MIPI_DSI_DCS_LONG_WRITE,
582				cutoff_45,
583				ARRAY_SIZE(cutoff_45));
584			lcd->cur_acl = 45;
585			break;
586		case 8 ... 10: /* 220cd ~ 300cd */
587			ops->cmd_write(lcd_to_master(lcd),
588				MIPI_DSI_DCS_LONG_WRITE,
589				cutoff_50,
590				ARRAY_SIZE(cutoff_50));
591			lcd->cur_acl = 50;
592			break;
593		default:
594			break;
595		}
596	} else {
597		s6e8ax0_acl_off(lcd);
598		lcd->cur_acl = 0;
599		dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl);
600	}
601}
602
603static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
604{
605	unsigned int ret;
606	unsigned int addr = 0xd1;	/* MTP ID */
607	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
608
609	ret = ops->cmd_read(lcd_to_master(lcd),
610			MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM,
611			addr, 3, mtp_id);
612}
613
614static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
615{
616	s6e8ax0_apply_level2_key(lcd);
617	s6e8ax0_sleep_out(lcd);
618	msleep(1);
619	s6e8ax0_panel_cond(lcd);
620	s6e8ax0_display_cond(lcd);
621	s6e8ax0_gamma_cond(lcd);
622	s6e8ax0_gamma_update(lcd);
623
624	s6e8ax0_etc_cond1(lcd);
625	s6e8ax0_etc_cond2(lcd);
626	s6e8ax0_etc_cond3(lcd);
627	s6e8ax0_etc_cond4(lcd);
628	s6e8ax0_etc_cond5(lcd);
629	s6e8ax0_etc_cond6(lcd);
630	s6e8ax0_etc_cond7(lcd);
631
632	s6e8ax0_elvss_nvm_set(lcd);
633	s6e8ax0_elvss_set(lcd);
634
635	s6e8ax0_acl_ctrl_set(lcd);
636	s6e8ax0_acl_on(lcd);
637
638	/* if ID3 value is not 33h, branch private elvss mode */
639	msleep(lcd->ddi_pd->power_on_delay);
640
641	return 0;
642}
643
644static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
645{
646	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
647
648	ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
649			s6e8ax0_22_gamma_table[brightness],
650			ARRAY_SIZE(s6e8ax0_22_gamma_table));
651
652	/* update gamma table. */
653	s6e8ax0_gamma_update(lcd);
654	lcd->gamma = brightness;
655
656	return 0;
657}
658
659static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
660{
661	s6e8ax0_update_gamma_ctrl(lcd, gamma);
662
663	return 0;
664}
665
666static int s6e8ax0_set_power(struct lcd_device *ld, int power)
667{
668	struct s6e8ax0 *lcd = lcd_get_data(ld);
669	struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
670	int ret = 0;
671
672	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
673			power != FB_BLANK_NORMAL) {
674		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
675		return -EINVAL;
676	}
677
678	if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) {
679		/* LCD power on */
680		if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power))
681			|| (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) {
682			ret = ops->set_blank_mode(lcd_to_master(lcd), power);
683			if (!ret && lcd->power != power)
684				lcd->power = power;
685		}
686	} else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) {
687		/* LCD power off */
688		if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) ||
689		(POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) {
690			ret = ops->set_early_blank_mode(lcd_to_master(lcd),
691							power);
692			if (!ret && lcd->power != power)
693				lcd->power = power;
694		}
695	}
696
697	return ret;
698}
699
700static int s6e8ax0_get_power(struct lcd_device *ld)
701{
702	struct s6e8ax0 *lcd = lcd_get_data(ld);
703
704	return lcd->power;
705}
706
707static int s6e8ax0_set_brightness(struct backlight_device *bd)
708{
709	int ret = 0, brightness = bd->props.brightness;
710	struct s6e8ax0 *lcd = bl_get_data(bd);
711
712	if (brightness < MIN_BRIGHTNESS ||
713		brightness > bd->props.max_brightness) {
714		dev_err(lcd->dev, "lcd brightness should be %d to %d.\n",
715			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
716		return -EINVAL;
717	}
718
719	ret = s6e8ax0_gamma_ctrl(lcd, brightness);
720	if (ret) {
721		dev_err(&bd->dev, "lcd brightness setting failed.\n");
722		return -EIO;
723	}
724
725	return ret;
726}
727
728static struct lcd_ops s6e8ax0_lcd_ops = {
729	.set_power = s6e8ax0_set_power,
730	.get_power = s6e8ax0_get_power,
731};
732
733static const struct backlight_ops s6e8ax0_backlight_ops = {
734	.update_status = s6e8ax0_set_brightness,
735};
736
737static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power)
738{
739	struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
740
741	msleep(lcd->ddi_pd->power_on_delay);
742
743	/* lcd power on */
744	if (power)
745		s6e8ax0_regulator_enable(lcd);
746	else
747		s6e8ax0_regulator_disable(lcd);
748
749	msleep(lcd->ddi_pd->reset_delay);
750
751	/* lcd reset */
752	if (lcd->ddi_pd->reset)
753		lcd->ddi_pd->reset(lcd->ld);
754	msleep(5);
755}
756
757static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev)
758{
759	struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
760
761	s6e8ax0_panel_init(lcd);
762	s6e8ax0_display_on(lcd);
763
764	lcd->power = FB_BLANK_UNBLANK;
765}
766
767static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
768{
769	struct s6e8ax0 *lcd;
770	int ret;
771	u8 mtp_id[3] = {0, };
772
773	lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL);
774	if (!lcd) {
775		dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
776		return -ENOMEM;
777	}
778
779	lcd->dsim_dev = dsim_dev;
780	lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data;
781	lcd->dev = &dsim_dev->dev;
782
783	mutex_init(&lcd->lock);
784
785	ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
786	if (ret) {
787		dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
788		return ret;
789	}
790
791	lcd->ld = devm_lcd_device_register(lcd->dev, "s6e8ax0", lcd->dev, lcd,
792			&s6e8ax0_lcd_ops);
793	if (IS_ERR(lcd->ld)) {
794		dev_err(lcd->dev, "failed to register lcd ops.\n");
795		return PTR_ERR(lcd->ld);
796	}
797
798	lcd->bd = devm_backlight_device_register(lcd->dev, "s6e8ax0-bl",
799				lcd->dev, lcd, &s6e8ax0_backlight_ops, NULL);
800	if (IS_ERR(lcd->bd)) {
801		dev_err(lcd->dev, "failed to register backlight ops.\n");
802		return PTR_ERR(lcd->bd);
803	}
804
805	lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
806	lcd->bd->props.brightness = MAX_BRIGHTNESS;
807
808	s6e8ax0_read_id(lcd, mtp_id);
809	if (mtp_id[0] == 0x00)
810		dev_err(lcd->dev, "read id failed\n");
811
812	dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
813			mtp_id[0], mtp_id[1], mtp_id[2]);
814
815	if (mtp_id[2] == 0x33)
816		dev_info(lcd->dev,
817			"ID-3 is 0xff does not support dynamic elvss\n");
818	else
819		dev_info(lcd->dev,
820			"ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
821
822	lcd->acl_enable = 1;
823	lcd->cur_acl = 0;
824
825	dev_set_drvdata(&dsim_dev->dev, lcd);
826
827	dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
828
829	return 0;
830}
831
832#ifdef CONFIG_PM
833static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev)
834{
835	struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
836
837	s6e8ax0_sleep_in(lcd);
838	msleep(lcd->ddi_pd->power_off_delay);
839	s6e8ax0_display_off(lcd);
840
841	s6e8ax0_regulator_disable(lcd);
842
843	return 0;
844}
845
846static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev)
847{
848	struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
849
850	s6e8ax0_sleep_out(lcd);
851	msleep(lcd->ddi_pd->power_on_delay);
852
853	s6e8ax0_regulator_enable(lcd);
854	s6e8ax0_set_sequence(dsim_dev);
855
856	return 0;
857}
858#else
859#define s6e8ax0_suspend		NULL
860#define s6e8ax0_resume		NULL
861#endif
862
863static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
864	.name = "s6e8ax0",
865	.id = -1,
866
867	.power_on = s6e8ax0_power_on,
868	.set_sequence = s6e8ax0_set_sequence,
869	.probe = s6e8ax0_probe,
870	.suspend = s6e8ax0_suspend,
871	.resume = s6e8ax0_resume,
872};
873
874static int s6e8ax0_init(void)
875{
876	exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
877
878	return 0;
879}
880
881static void s6e8ax0_exit(void)
882{
883	return;
884}
885
886module_init(s6e8ax0_init);
887module_exit(s6e8ax0_exit);
888
889MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
890MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
891MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
892MODULE_LICENSE("GPL");
893