blob: ca2602413aa43a0b0dcda6a47ce4e6e57fc574e7 [file] [log] [blame]
Donghwa Lee9befe402012-02-08 12:47:39 -08001/* 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 };
Donghwa Lee24bb7a62012-05-09 14:33:31 +0900296 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 };
Donghwa Lee9befe402012-02-08 12:47:39 -0800302
Donghwa Lee24bb7a62012-05-09 14:33:31 +0900303 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));
Donghwa Lee9befe402012-02-08 12:47:39 -0800310}
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_get_brightness(struct backlight_device *bd)
708{
709 return bd->props.brightness;
710}
711
712static int s6e8ax0_set_brightness(struct backlight_device *bd)
713{
714 int ret = 0, brightness = bd->props.brightness;
715 struct s6e8ax0 *lcd = bl_get_data(bd);
716
717 if (brightness < MIN_BRIGHTNESS ||
718 brightness > bd->props.max_brightness) {
719 dev_err(lcd->dev, "lcd brightness should be %d to %d.\n",
720 MIN_BRIGHTNESS, MAX_BRIGHTNESS);
721 return -EINVAL;
722 }
723
724 ret = s6e8ax0_gamma_ctrl(lcd, brightness);
725 if (ret) {
726 dev_err(&bd->dev, "lcd brightness setting failed.\n");
727 return -EIO;
728 }
729
730 return ret;
731}
732
733static struct lcd_ops s6e8ax0_lcd_ops = {
734 .set_power = s6e8ax0_set_power,
735 .get_power = s6e8ax0_get_power,
736};
737
738static const struct backlight_ops s6e8ax0_backlight_ops = {
739 .get_brightness = s6e8ax0_get_brightness,
740 .update_status = s6e8ax0_set_brightness,
741};
742
743static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power)
744{
745 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
746
747 msleep(lcd->ddi_pd->power_on_delay);
748
749 /* lcd power on */
750 if (power)
751 s6e8ax0_regulator_enable(lcd);
752 else
753 s6e8ax0_regulator_disable(lcd);
754
755 msleep(lcd->ddi_pd->reset_delay);
756
757 /* lcd reset */
758 if (lcd->ddi_pd->reset)
759 lcd->ddi_pd->reset(lcd->ld);
760 msleep(5);
761}
762
763static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev)
764{
765 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
766
767 s6e8ax0_panel_init(lcd);
768 s6e8ax0_display_on(lcd);
769
770 lcd->power = FB_BLANK_UNBLANK;
771}
772
773static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
774{
775 struct s6e8ax0 *lcd;
776 int ret;
777 u8 mtp_id[3] = {0, };
778
Sachin Kamata25fc872013-02-21 16:42:25 -0800779 lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL);
Donghwa Lee9befe402012-02-08 12:47:39 -0800780 if (!lcd) {
781 dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
782 return -ENOMEM;
783 }
784
785 lcd->dsim_dev = dsim_dev;
786 lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data;
787 lcd->dev = &dsim_dev->dev;
788
789 mutex_init(&lcd->lock);
790
Sachin Kamata25fc872013-02-21 16:42:25 -0800791 ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
Donghwa Lee9befe402012-02-08 12:47:39 -0800792 if (ret) {
793 dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
Sachin Kamata25fc872013-02-21 16:42:25 -0800794 return ret;
Donghwa Lee9befe402012-02-08 12:47:39 -0800795 }
796
797 lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd,
798 &s6e8ax0_lcd_ops);
799 if (IS_ERR(lcd->ld)) {
800 dev_err(lcd->dev, "failed to register lcd ops.\n");
Sachin Kamata25fc872013-02-21 16:42:25 -0800801 return PTR_ERR(lcd->ld);
Donghwa Lee9befe402012-02-08 12:47:39 -0800802 }
803
804 lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd,
805 &s6e8ax0_backlight_ops, NULL);
806 if (IS_ERR(lcd->bd)) {
807 dev_err(lcd->dev, "failed to register backlight ops.\n");
808 ret = PTR_ERR(lcd->bd);
809 goto err_backlight_register;
810 }
811
812 lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
813 lcd->bd->props.brightness = MAX_BRIGHTNESS;
814
815 s6e8ax0_read_id(lcd, mtp_id);
816 if (mtp_id[0] == 0x00)
817 dev_err(lcd->dev, "read id failed\n");
818
819 dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
820 mtp_id[0], mtp_id[1], mtp_id[2]);
821
822 if (mtp_id[2] == 0x33)
823 dev_info(lcd->dev,
824 "ID-3 is 0xff does not support dynamic elvss\n");
825 else
826 dev_info(lcd->dev,
827 "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
828
829 lcd->acl_enable = 1;
830 lcd->cur_acl = 0;
831
832 dev_set_drvdata(&dsim_dev->dev, lcd);
833
834 dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
835
836 return 0;
837
838err_backlight_register:
839 lcd_device_unregister(lcd->ld);
Donghwa Lee9befe402012-02-08 12:47:39 -0800840 return ret;
841}
842
843#ifdef CONFIG_PM
844static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev)
845{
846 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
847
848 s6e8ax0_sleep_in(lcd);
849 msleep(lcd->ddi_pd->power_off_delay);
850 s6e8ax0_display_off(lcd);
851
852 s6e8ax0_regulator_disable(lcd);
853
854 return 0;
855}
856
857static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev)
858{
859 struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
860
861 s6e8ax0_sleep_out(lcd);
862 msleep(lcd->ddi_pd->power_on_delay);
863
864 s6e8ax0_regulator_enable(lcd);
865 s6e8ax0_set_sequence(dsim_dev);
866
867 return 0;
868}
869#else
870#define s6e8ax0_suspend NULL
871#define s6e8ax0_resume NULL
872#endif
873
874static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
875 .name = "s6e8ax0",
876 .id = -1,
877
878 .power_on = s6e8ax0_power_on,
879 .set_sequence = s6e8ax0_set_sequence,
880 .probe = s6e8ax0_probe,
881 .suspend = s6e8ax0_suspend,
882 .resume = s6e8ax0_resume,
883};
884
885static int s6e8ax0_init(void)
886{
887 exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
888
889 return 0;
890}
891
892static void s6e8ax0_exit(void)
893{
894 return;
895}
896
897module_init(s6e8ax0_init);
898module_exit(s6e8ax0_exit);
899
900MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
901MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
902MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
903MODULE_LICENSE("GPL");