]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/video/exynos/s6e8ax0.c
Merge tag 'stable/for-linus-3.4-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[mirror_ubuntu-bionic-kernel.git] / drivers / video / exynos / s6e8ax0.c
CommitLineData
9befe40f
DL
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
297 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
298 data_to_send, ARRAY_SIZE(data_to_send));
299}
300
301static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
302{
303 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
304 static const unsigned char data_to_send[] = {
305 0xf2, 0x80, 0x03, 0x0d
306 };
307
308 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
309 data_to_send, ARRAY_SIZE(data_to_send));
310}
311
312/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
313static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
314{
315 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
316 unsigned int gamma = lcd->bd->props.brightness;
317
318 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
319 s6e8ax0_22_gamma_table[gamma],
320 GAMMA_TABLE_COUNT);
321}
322
323static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
324{
325 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
326 static const unsigned char data_to_send[] = {
327 0xf7, 0x03
328 };
329
330 ops->cmd_write(lcd_to_master(lcd),
331 MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send,
332 ARRAY_SIZE(data_to_send));
333}
334
335static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
336{
337 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
338 static const unsigned char data_to_send[] = {
339 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
340 0x0d, 0x00, 0x00
341 };
342
343 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
344 data_to_send, ARRAY_SIZE(data_to_send));
345}
346
347static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
348{
349 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
350 static const unsigned char data_to_send[] = {
351 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
352 0x00
353 };
354
355 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
356 data_to_send, ARRAY_SIZE(data_to_send));
357}
358
359static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
360{
361 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
362 static const unsigned char data_to_send[] = {
363 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
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_cond4(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 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
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_cond5(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 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
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}
391static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
392{
393 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
394 static const unsigned char data_to_send[] = {
395 0xe3, 0x40
396 };
397
398 ops->cmd_write(lcd_to_master(lcd),
399 MIPI_DSI_DCS_SHORT_WRITE_PARAM,
400 data_to_send, ARRAY_SIZE(data_to_send));
401}
402
403static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
404{
405 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
406 static const unsigned char data_to_send[] = {
407 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
408 };
409
410 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
411 data_to_send, ARRAY_SIZE(data_to_send));
412}
413
414static void s6e8ax0_elvss_set(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 0xb1, 0x04, 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_nvm_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 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
430 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
431 0x64, 0xaf
432 };
433
434 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
435 data_to_send, ARRAY_SIZE(data_to_send));
436}
437
438static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
439{
440 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
441 static const unsigned char data_to_send[] = {
442 0x10, 0x00
443 };
444
445 ops->cmd_write(lcd_to_master(lcd),
446 MIPI_DSI_DCS_SHORT_WRITE,
447 data_to_send, ARRAY_SIZE(data_to_send));
448}
449
450static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
451{
452 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
453 static const unsigned char data_to_send[] = {
454 0x11, 0x00
455 };
456
457 ops->cmd_write(lcd_to_master(lcd),
458 MIPI_DSI_DCS_SHORT_WRITE,
459 data_to_send, ARRAY_SIZE(data_to_send));
460}
461
462static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
463{
464 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
465 static const unsigned char data_to_send[] = {
466 0x29, 0x00
467 };
468
469 ops->cmd_write(lcd_to_master(lcd),
470 MIPI_DSI_DCS_SHORT_WRITE,
471 data_to_send, ARRAY_SIZE(data_to_send));
472}
473
474static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
475{
476 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
477 static const unsigned char data_to_send[] = {
478 0x28, 0x00
479 };
480
481 ops->cmd_write(lcd_to_master(lcd),
482 MIPI_DSI_DCS_SHORT_WRITE,
483 data_to_send, ARRAY_SIZE(data_to_send));
484}
485
486static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
487{
488 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
489 static const unsigned char data_to_send[] = {
490 0xf0, 0x5a, 0x5a
491 };
492
493 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
494 data_to_send, ARRAY_SIZE(data_to_send));
495}
496
497static void s6e8ax0_acl_on(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 0xc0, 0x01
502 };
503
504 ops->cmd_write(lcd_to_master(lcd),
505 MIPI_DSI_DCS_SHORT_WRITE,
506 data_to_send, ARRAY_SIZE(data_to_send));
507}
508
509static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
510{
511 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
512 static const unsigned char data_to_send[] = {
513 0xc0, 0x00
514 };
515
516 ops->cmd_write(lcd_to_master(lcd),
517 MIPI_DSI_DCS_SHORT_WRITE,
518 data_to_send, ARRAY_SIZE(data_to_send));
519}
520
521/* Full white 50% reducing setting */
522static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
523{
524 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
525 /* Full white 50% reducing setting */
526 static const unsigned char cutoff_50[] = {
527 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
528 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
529 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
530 0x3f, 0x46
531 };
532 /* Full white 45% reducing setting */
533 static const unsigned char cutoff_45[] = {
534 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
535 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
536 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
537 0x37, 0x3d
538 };
539 /* Full white 40% reducing setting */
540 static const unsigned char cutoff_40[] = {
541 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
542 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
543 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
544 0x31, 0x36
545 };
546
547 if (lcd->acl_enable) {
548 if (lcd->cur_acl == 0) {
549 if (lcd->gamma == 0 || lcd->gamma == 1) {
550 s6e8ax0_acl_off(lcd);
551 dev_dbg(&lcd->ld->dev,
552 "cur_acl=%d\n", lcd->cur_acl);
553 } else
554 s6e8ax0_acl_on(lcd);
555 }
556 switch (lcd->gamma) {
557 case 0: /* 30cd */
558 s6e8ax0_acl_off(lcd);
559 lcd->cur_acl = 0;
560 break;
561 case 1 ... 3: /* 50cd ~ 90cd */
562 ops->cmd_write(lcd_to_master(lcd),
563 MIPI_DSI_DCS_LONG_WRITE,
564 cutoff_40,
565 ARRAY_SIZE(cutoff_40));
566 lcd->cur_acl = 40;
567 break;
568 case 4 ... 7: /* 120cd ~ 210cd */
569 ops->cmd_write(lcd_to_master(lcd),
570 MIPI_DSI_DCS_LONG_WRITE,
571 cutoff_45,
572 ARRAY_SIZE(cutoff_45));
573 lcd->cur_acl = 45;
574 break;
575 case 8 ... 10: /* 220cd ~ 300cd */
576 ops->cmd_write(lcd_to_master(lcd),
577 MIPI_DSI_DCS_LONG_WRITE,
578 cutoff_50,
579 ARRAY_SIZE(cutoff_50));
580 lcd->cur_acl = 50;
581 break;
582 default:
583 break;
584 }
585 } else {
586 s6e8ax0_acl_off(lcd);
587 lcd->cur_acl = 0;
588 dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl);
589 }
590}
591
592static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
593{
594 unsigned int ret;
595 unsigned int addr = 0xd1; /* MTP ID */
596 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
597
598 ret = ops->cmd_read(lcd_to_master(lcd),
599 MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM,
600 addr, 3, mtp_id);
601}
602
603static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
604{
605 s6e8ax0_apply_level2_key(lcd);
606 s6e8ax0_sleep_out(lcd);
607 msleep(1);
608 s6e8ax0_panel_cond(lcd);
609 s6e8ax0_display_cond(lcd);
610 s6e8ax0_gamma_cond(lcd);
611 s6e8ax0_gamma_update(lcd);
612
613 s6e8ax0_etc_cond1(lcd);
614 s6e8ax0_etc_cond2(lcd);
615 s6e8ax0_etc_cond3(lcd);
616 s6e8ax0_etc_cond4(lcd);
617 s6e8ax0_etc_cond5(lcd);
618 s6e8ax0_etc_cond6(lcd);
619 s6e8ax0_etc_cond7(lcd);
620
621 s6e8ax0_elvss_nvm_set(lcd);
622 s6e8ax0_elvss_set(lcd);
623
624 s6e8ax0_acl_ctrl_set(lcd);
625 s6e8ax0_acl_on(lcd);
626
627 /* if ID3 value is not 33h, branch private elvss mode */
628 msleep(lcd->ddi_pd->power_on_delay);
629
630 return 0;
631}
632
633static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
634{
635 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
636
637 ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
638 s6e8ax0_22_gamma_table[brightness],
639 ARRAY_SIZE(s6e8ax0_22_gamma_table));
640
641 /* update gamma table. */
642 s6e8ax0_gamma_update(lcd);
643 lcd->gamma = brightness;
644
645 return 0;
646}
647
648static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
649{
650 s6e8ax0_update_gamma_ctrl(lcd, gamma);
651
652 return 0;
653}
654
655static int s6e8ax0_set_power(struct lcd_device *ld, int power)
656{
657 struct s6e8ax0 *lcd = lcd_get_data(ld);
658 struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
659 int ret = 0;
660
661 if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
662 power != FB_BLANK_NORMAL) {
663 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
664 return -EINVAL;
665 }
666
667 if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) {
668 /* LCD power on */
669 if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power))
670 || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) {
671 ret = ops->set_blank_mode(lcd_to_master(lcd), power);
672 if (!ret && lcd->power != power)
673 lcd->power = power;
674 }
675 } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) {
676 /* LCD power off */
677 if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) ||
678 (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) {
679 ret = ops->set_early_blank_mode(lcd_to_master(lcd),
680 power);
681 if (!ret && lcd->power != power)
682 lcd->power = power;
683 }
684 }
685
686 return ret;
687}
688
689static int s6e8ax0_get_power(struct lcd_device *ld)
690{
691 struct s6e8ax0 *lcd = lcd_get_data(ld);
692
693 return lcd->power;
694}
695
696static int s6e8ax0_get_brightness(struct backlight_device *bd)
697{
698 return bd->props.brightness;
699}
700
701static int s6e8ax0_set_brightness(struct backlight_device *bd)
702{
703 int ret = 0, brightness = bd->props.brightness;
704 struct s6e8ax0 *lcd = bl_get_data(bd);
705
706 if (brightness < MIN_BRIGHTNESS ||
707 brightness > bd->props.max_brightness) {
708 dev_err(lcd->dev, "lcd brightness should be %d to %d.\n",
709 MIN_BRIGHTNESS, MAX_BRIGHTNESS);
710 return -EINVAL;
711 }
712
713 ret = s6e8ax0_gamma_ctrl(lcd, brightness);
714 if (ret) {
715 dev_err(&bd->dev, "lcd brightness setting failed.\n");
716 return -EIO;
717 }
718
719 return ret;
720}
721
722static struct lcd_ops s6e8ax0_lcd_ops = {
723 .set_power = s6e8ax0_set_power,
724 .get_power = s6e8ax0_get_power,
725};
726
727static const struct backlight_ops s6e8ax0_backlight_ops = {
728 .get_brightness = s6e8ax0_get_brightness,
729 .update_status = s6e8ax0_set_brightness,
730};
731
732static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power)
733{
734 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
735
736 msleep(lcd->ddi_pd->power_on_delay);
737
738 /* lcd power on */
739 if (power)
740 s6e8ax0_regulator_enable(lcd);
741 else
742 s6e8ax0_regulator_disable(lcd);
743
744 msleep(lcd->ddi_pd->reset_delay);
745
746 /* lcd reset */
747 if (lcd->ddi_pd->reset)
748 lcd->ddi_pd->reset(lcd->ld);
749 msleep(5);
750}
751
752static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev)
753{
754 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
755
756 s6e8ax0_panel_init(lcd);
757 s6e8ax0_display_on(lcd);
758
759 lcd->power = FB_BLANK_UNBLANK;
760}
761
762static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
763{
764 struct s6e8ax0 *lcd;
765 int ret;
766 u8 mtp_id[3] = {0, };
767
768 lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL);
769 if (!lcd) {
770 dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
771 return -ENOMEM;
772 }
773
774 lcd->dsim_dev = dsim_dev;
775 lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data;
776 lcd->dev = &dsim_dev->dev;
777
778 mutex_init(&lcd->lock);
779
780 ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
781 if (ret) {
782 dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
783 goto err_lcd_register;
784 }
785
786 lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd,
787 &s6e8ax0_lcd_ops);
788 if (IS_ERR(lcd->ld)) {
789 dev_err(lcd->dev, "failed to register lcd ops.\n");
790 ret = PTR_ERR(lcd->ld);
791 goto err_lcd_register;
792 }
793
794 lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd,
795 &s6e8ax0_backlight_ops, NULL);
796 if (IS_ERR(lcd->bd)) {
797 dev_err(lcd->dev, "failed to register backlight ops.\n");
798 ret = PTR_ERR(lcd->bd);
799 goto err_backlight_register;
800 }
801
802 lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
803 lcd->bd->props.brightness = MAX_BRIGHTNESS;
804
805 s6e8ax0_read_id(lcd, mtp_id);
806 if (mtp_id[0] == 0x00)
807 dev_err(lcd->dev, "read id failed\n");
808
809 dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
810 mtp_id[0], mtp_id[1], mtp_id[2]);
811
812 if (mtp_id[2] == 0x33)
813 dev_info(lcd->dev,
814 "ID-3 is 0xff does not support dynamic elvss\n");
815 else
816 dev_info(lcd->dev,
817 "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
818
819 lcd->acl_enable = 1;
820 lcd->cur_acl = 0;
821
822 dev_set_drvdata(&dsim_dev->dev, lcd);
823
824 dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
825
826 return 0;
827
828err_backlight_register:
829 lcd_device_unregister(lcd->ld);
830
831err_lcd_register:
832 regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
833 kfree(lcd);
834
835 return ret;
836}
837
838#ifdef CONFIG_PM
839static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev)
840{
841 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
842
843 s6e8ax0_sleep_in(lcd);
844 msleep(lcd->ddi_pd->power_off_delay);
845 s6e8ax0_display_off(lcd);
846
847 s6e8ax0_regulator_disable(lcd);
848
849 return 0;
850}
851
852static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev)
853{
854 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
855
856 s6e8ax0_sleep_out(lcd);
857 msleep(lcd->ddi_pd->power_on_delay);
858
859 s6e8ax0_regulator_enable(lcd);
860 s6e8ax0_set_sequence(dsim_dev);
861
862 return 0;
863}
864#else
865#define s6e8ax0_suspend NULL
866#define s6e8ax0_resume NULL
867#endif
868
869static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
870 .name = "s6e8ax0",
871 .id = -1,
872
873 .power_on = s6e8ax0_power_on,
874 .set_sequence = s6e8ax0_set_sequence,
875 .probe = s6e8ax0_probe,
876 .suspend = s6e8ax0_suspend,
877 .resume = s6e8ax0_resume,
878};
879
880static int s6e8ax0_init(void)
881{
882 exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
883
884 return 0;
885}
886
887static void s6e8ax0_exit(void)
888{
889 return;
890}
891
892module_init(s6e8ax0_init);
893module_exit(s6e8ax0_exit);
894
895MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
896MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
897MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
898MODULE_LICENSE("GPL");