blob: 92e53e20b2eaa7c8b2fb025cd2d2625499bc1cad [file] [log] [blame]
InKi Daeee378a52010-05-24 12:21:36 -07001/*
2 * S6E63M0 AMOLED LCD panel driver.
3 *
4 * Author: InKi Dae <inki.dae@samsung.com>
5 *
6 * Derived from drivers/video/omap/lcd-apollon.c
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23#include <linux/wait.h>
24#include <linux/fb.h>
25#include <linux/delay.h>
26#include <linux/gpio.h>
27#include <linux/spi/spi.h>
28#include <linux/irq.h>
29#include <linux/interrupt.h>
30#include <linux/kernel.h>
31#include <linux/lcd.h>
32#include <linux/backlight.h>
Paul Gortmaker355b2002011-07-03 16:17:28 -040033#include <linux/module.h>
InKi Daeee378a52010-05-24 12:21:36 -070034
35#include "s6e63m0_gamma.h"
36
37#define SLEEPMSEC 0x1000
38#define ENDDEF 0x2000
39#define DEFMASK 0xFF00
40#define COMMAND_ONLY 0xFE
41#define DATA_ONLY 0xFF
42
43#define MIN_BRIGHTNESS 0
44#define MAX_BRIGHTNESS 10
45
InKi Daeee378a52010-05-24 12:21:36 -070046struct s6e63m0 {
47 struct device *dev;
48 struct spi_device *spi;
49 unsigned int power;
50 unsigned int current_brightness;
51 unsigned int gamma_mode;
52 unsigned int gamma_table_count;
53 struct lcd_device *ld;
54 struct backlight_device *bd;
55 struct lcd_platform_data *lcd_pd;
56};
57
Jingoo Han4a959e52013-02-21 16:43:20 -080058static const unsigned short seq_panel_condition_set[] = {
InKi Daeee378a52010-05-24 12:21:36 -070059 0xF8, 0x01,
60 DATA_ONLY, 0x27,
61 DATA_ONLY, 0x27,
62 DATA_ONLY, 0x07,
63 DATA_ONLY, 0x07,
64 DATA_ONLY, 0x54,
65 DATA_ONLY, 0x9f,
66 DATA_ONLY, 0x63,
67 DATA_ONLY, 0x86,
68 DATA_ONLY, 0x1a,
69 DATA_ONLY, 0x33,
70 DATA_ONLY, 0x0d,
71 DATA_ONLY, 0x00,
72 DATA_ONLY, 0x00,
73
74 ENDDEF, 0x0000
75};
76
Jingoo Han4a959e52013-02-21 16:43:20 -080077static const unsigned short seq_display_condition_set[] = {
InKi Daeee378a52010-05-24 12:21:36 -070078 0xf2, 0x02,
79 DATA_ONLY, 0x03,
80 DATA_ONLY, 0x1c,
81 DATA_ONLY, 0x10,
82 DATA_ONLY, 0x10,
83
84 0xf7, 0x03,
85 DATA_ONLY, 0x00,
86 DATA_ONLY, 0x00,
87
88 ENDDEF, 0x0000
89};
90
Jingoo Han4a959e52013-02-21 16:43:20 -080091static const unsigned short seq_gamma_setting[] = {
InKi Daeee378a52010-05-24 12:21:36 -070092 0xfa, 0x00,
93 DATA_ONLY, 0x18,
94 DATA_ONLY, 0x08,
95 DATA_ONLY, 0x24,
96 DATA_ONLY, 0x64,
97 DATA_ONLY, 0x56,
98 DATA_ONLY, 0x33,
99 DATA_ONLY, 0xb6,
100 DATA_ONLY, 0xba,
101 DATA_ONLY, 0xa8,
102 DATA_ONLY, 0xac,
103 DATA_ONLY, 0xb1,
104 DATA_ONLY, 0x9d,
105 DATA_ONLY, 0xc1,
106 DATA_ONLY, 0xc1,
107 DATA_ONLY, 0xb7,
108 DATA_ONLY, 0x00,
109 DATA_ONLY, 0x9c,
110 DATA_ONLY, 0x00,
111 DATA_ONLY, 0x9f,
112 DATA_ONLY, 0x00,
113 DATA_ONLY, 0xd6,
114
115 0xfa, 0x01,
116
117 ENDDEF, 0x0000
118};
119
Jingoo Han4a959e52013-02-21 16:43:20 -0800120static const unsigned short seq_etc_condition_set[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700121 0xf6, 0x00,
122 DATA_ONLY, 0x8c,
123 DATA_ONLY, 0x07,
124
125 0xb3, 0xc,
126
127 0xb5, 0x2c,
128 DATA_ONLY, 0x12,
129 DATA_ONLY, 0x0c,
130 DATA_ONLY, 0x0a,
131 DATA_ONLY, 0x10,
132 DATA_ONLY, 0x0e,
133 DATA_ONLY, 0x17,
134 DATA_ONLY, 0x13,
135 DATA_ONLY, 0x1f,
136 DATA_ONLY, 0x1a,
137 DATA_ONLY, 0x2a,
138 DATA_ONLY, 0x24,
139 DATA_ONLY, 0x1f,
140 DATA_ONLY, 0x1b,
141 DATA_ONLY, 0x1a,
142 DATA_ONLY, 0x17,
143
144 DATA_ONLY, 0x2b,
145 DATA_ONLY, 0x26,
146 DATA_ONLY, 0x22,
147 DATA_ONLY, 0x20,
148 DATA_ONLY, 0x3a,
149 DATA_ONLY, 0x34,
150 DATA_ONLY, 0x30,
151 DATA_ONLY, 0x2c,
152 DATA_ONLY, 0x29,
153 DATA_ONLY, 0x26,
154 DATA_ONLY, 0x25,
155 DATA_ONLY, 0x23,
156 DATA_ONLY, 0x21,
157 DATA_ONLY, 0x20,
158 DATA_ONLY, 0x1e,
159 DATA_ONLY, 0x1e,
160
161 0xb6, 0x00,
162 DATA_ONLY, 0x00,
163 DATA_ONLY, 0x11,
164 DATA_ONLY, 0x22,
165 DATA_ONLY, 0x33,
166 DATA_ONLY, 0x44,
167 DATA_ONLY, 0x44,
168 DATA_ONLY, 0x44,
169
170 DATA_ONLY, 0x55,
171 DATA_ONLY, 0x55,
172 DATA_ONLY, 0x66,
173 DATA_ONLY, 0x66,
174 DATA_ONLY, 0x66,
175 DATA_ONLY, 0x66,
176 DATA_ONLY, 0x66,
177 DATA_ONLY, 0x66,
178
179 0xb7, 0x2c,
180 DATA_ONLY, 0x12,
181 DATA_ONLY, 0x0c,
182 DATA_ONLY, 0x0a,
183 DATA_ONLY, 0x10,
184 DATA_ONLY, 0x0e,
185 DATA_ONLY, 0x17,
186 DATA_ONLY, 0x13,
187 DATA_ONLY, 0x1f,
188 DATA_ONLY, 0x1a,
189 DATA_ONLY, 0x2a,
190 DATA_ONLY, 0x24,
191 DATA_ONLY, 0x1f,
192 DATA_ONLY, 0x1b,
193 DATA_ONLY, 0x1a,
194 DATA_ONLY, 0x17,
195
196 DATA_ONLY, 0x2b,
197 DATA_ONLY, 0x26,
198 DATA_ONLY, 0x22,
199 DATA_ONLY, 0x20,
200 DATA_ONLY, 0x3a,
201 DATA_ONLY, 0x34,
202 DATA_ONLY, 0x30,
203 DATA_ONLY, 0x2c,
204 DATA_ONLY, 0x29,
205 DATA_ONLY, 0x26,
206 DATA_ONLY, 0x25,
207 DATA_ONLY, 0x23,
208 DATA_ONLY, 0x21,
209 DATA_ONLY, 0x20,
210 DATA_ONLY, 0x1e,
211 DATA_ONLY, 0x1e,
212
213 0xb8, 0x00,
214 DATA_ONLY, 0x00,
215 DATA_ONLY, 0x11,
216 DATA_ONLY, 0x22,
217 DATA_ONLY, 0x33,
218 DATA_ONLY, 0x44,
219 DATA_ONLY, 0x44,
220 DATA_ONLY, 0x44,
221
222 DATA_ONLY, 0x55,
223 DATA_ONLY, 0x55,
224 DATA_ONLY, 0x66,
225 DATA_ONLY, 0x66,
226 DATA_ONLY, 0x66,
227 DATA_ONLY, 0x66,
228 DATA_ONLY, 0x66,
229 DATA_ONLY, 0x66,
230
231 0xb9, 0x2c,
232 DATA_ONLY, 0x12,
233 DATA_ONLY, 0x0c,
234 DATA_ONLY, 0x0a,
235 DATA_ONLY, 0x10,
236 DATA_ONLY, 0x0e,
237 DATA_ONLY, 0x17,
238 DATA_ONLY, 0x13,
239 DATA_ONLY, 0x1f,
240 DATA_ONLY, 0x1a,
241 DATA_ONLY, 0x2a,
242 DATA_ONLY, 0x24,
243 DATA_ONLY, 0x1f,
244 DATA_ONLY, 0x1b,
245 DATA_ONLY, 0x1a,
246 DATA_ONLY, 0x17,
247
248 DATA_ONLY, 0x2b,
249 DATA_ONLY, 0x26,
250 DATA_ONLY, 0x22,
251 DATA_ONLY, 0x20,
252 DATA_ONLY, 0x3a,
253 DATA_ONLY, 0x34,
254 DATA_ONLY, 0x30,
255 DATA_ONLY, 0x2c,
256 DATA_ONLY, 0x29,
257 DATA_ONLY, 0x26,
258 DATA_ONLY, 0x25,
259 DATA_ONLY, 0x23,
260 DATA_ONLY, 0x21,
261 DATA_ONLY, 0x20,
262 DATA_ONLY, 0x1e,
263 DATA_ONLY, 0x1e,
264
265 0xba, 0x00,
266 DATA_ONLY, 0x00,
267 DATA_ONLY, 0x11,
268 DATA_ONLY, 0x22,
269 DATA_ONLY, 0x33,
270 DATA_ONLY, 0x44,
271 DATA_ONLY, 0x44,
272 DATA_ONLY, 0x44,
273
274 DATA_ONLY, 0x55,
275 DATA_ONLY, 0x55,
276 DATA_ONLY, 0x66,
277 DATA_ONLY, 0x66,
278 DATA_ONLY, 0x66,
279 DATA_ONLY, 0x66,
280 DATA_ONLY, 0x66,
281 DATA_ONLY, 0x66,
282
283 0xc1, 0x4d,
284 DATA_ONLY, 0x96,
285 DATA_ONLY, 0x1d,
286 DATA_ONLY, 0x00,
287 DATA_ONLY, 0x00,
288 DATA_ONLY, 0x01,
289 DATA_ONLY, 0xdf,
290 DATA_ONLY, 0x00,
291 DATA_ONLY, 0x00,
292 DATA_ONLY, 0x03,
293 DATA_ONLY, 0x1f,
294 DATA_ONLY, 0x00,
295 DATA_ONLY, 0x00,
296 DATA_ONLY, 0x00,
297 DATA_ONLY, 0x00,
298 DATA_ONLY, 0x00,
299 DATA_ONLY, 0x00,
300 DATA_ONLY, 0x00,
301 DATA_ONLY, 0x00,
302 DATA_ONLY, 0x03,
303 DATA_ONLY, 0x06,
304 DATA_ONLY, 0x09,
305 DATA_ONLY, 0x0d,
306 DATA_ONLY, 0x0f,
307 DATA_ONLY, 0x12,
308 DATA_ONLY, 0x15,
309 DATA_ONLY, 0x18,
310
311 0xb2, 0x10,
312 DATA_ONLY, 0x10,
313 DATA_ONLY, 0x0b,
314 DATA_ONLY, 0x05,
315
316 ENDDEF, 0x0000
317};
318
Jingoo Han4a959e52013-02-21 16:43:20 -0800319static const unsigned short seq_acl_on[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700320 /* ACL on */
321 0xc0, 0x01,
322
323 ENDDEF, 0x0000
324};
325
Jingoo Han4a959e52013-02-21 16:43:20 -0800326static const unsigned short seq_acl_off[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700327 /* ACL off */
328 0xc0, 0x00,
329
330 ENDDEF, 0x0000
331};
332
Jingoo Han4a959e52013-02-21 16:43:20 -0800333static const unsigned short seq_elvss_on[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700334 /* ELVSS on */
335 0xb1, 0x0b,
336
337 ENDDEF, 0x0000
338};
339
Jingoo Han4a959e52013-02-21 16:43:20 -0800340static const unsigned short seq_elvss_off[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700341 /* ELVSS off */
342 0xb1, 0x0a,
343
344 ENDDEF, 0x0000
345};
346
Jingoo Han4a959e52013-02-21 16:43:20 -0800347static const unsigned short seq_stand_by_off[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700348 0x11, COMMAND_ONLY,
349
350 ENDDEF, 0x0000
351};
352
Jingoo Han4a959e52013-02-21 16:43:20 -0800353static const unsigned short seq_stand_by_on[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700354 0x10, COMMAND_ONLY,
355
356 ENDDEF, 0x0000
357};
358
Jingoo Han4a959e52013-02-21 16:43:20 -0800359static const unsigned short seq_display_on[] = {
InKi Daeee378a52010-05-24 12:21:36 -0700360 0x29, COMMAND_ONLY,
361
362 ENDDEF, 0x0000
363};
364
365
366static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
367{
368 u16 buf[1];
369 struct spi_message msg;
370
371 struct spi_transfer xfer = {
372 .len = 2,
373 .tx_buf = buf,
374 };
375
376 buf[0] = (addr << 8) | data;
377
378 spi_message_init(&msg);
379 spi_message_add_tail(&xfer, &msg);
380
381 return spi_sync(lcd->spi, &msg);
382}
383
384static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
385 unsigned char command)
386{
387 int ret = 0;
388
389 if (address != DATA_ONLY)
390 ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
391 if (command != COMMAND_ONLY)
392 ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
393
394 return ret;
395}
396
397static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
398 const unsigned short *wbuf)
399{
400 int ret = 0, i = 0;
401
402 while ((wbuf[i] & DEFMASK) != ENDDEF) {
403 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
404 ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
405 if (ret)
406 break;
Jingoo Han97a4d9d2013-02-21 16:43:21 -0800407 } else {
408 msleep(wbuf[i+1]);
409 }
InKi Daeee378a52010-05-24 12:21:36 -0700410 i += 2;
411 }
412
413 return ret;
414}
415
416static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
417{
418 unsigned int i = 0;
419 int ret = 0;
420
421 /* disable gamma table updating. */
422 ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
423 if (ret) {
424 dev_err(lcd->dev, "failed to disable gamma table updating.\n");
425 goto gamma_err;
426 }
427
428 for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
429 ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
430 if (ret) {
431 dev_err(lcd->dev, "failed to set gamma table.\n");
432 goto gamma_err;
433 }
434 }
435
436 /* update gamma table. */
437 ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
438 if (ret)
439 dev_err(lcd->dev, "failed to update gamma table.\n");
440
441gamma_err:
442 return ret;
443}
444
445static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
446{
447 int ret = 0;
448
449 ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
450
451 return ret;
452}
453
454
455static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
456{
457 int ret, i;
458 const unsigned short *init_seq[] = {
Jingoo Han4a959e52013-02-21 16:43:20 -0800459 seq_panel_condition_set,
460 seq_display_condition_set,
461 seq_gamma_setting,
462 seq_etc_condition_set,
463 seq_acl_on,
464 seq_elvss_on,
InKi Daeee378a52010-05-24 12:21:36 -0700465 };
466
467 for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
468 ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
469 if (ret)
470 break;
471 }
472
473 return ret;
474}
475
476static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
477{
478 int ret = 0, i;
479 const unsigned short *enable_seq[] = {
Jingoo Han4a959e52013-02-21 16:43:20 -0800480 seq_stand_by_off,
481 seq_display_on,
InKi Daeee378a52010-05-24 12:21:36 -0700482 };
483
484 for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
485 ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
486 if (ret)
487 break;
488 }
489
490 return ret;
491}
492
493static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
494{
495 int ret;
496
Jingoo Han4a959e52013-02-21 16:43:20 -0800497 ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on);
InKi Daeee378a52010-05-24 12:21:36 -0700498
499 return ret;
500}
501
Jingoo Han892dc2e2013-02-21 16:43:23 -0800502static int s6e63m0_power_is_on(int power)
503{
504 return power <= FB_BLANK_NORMAL;
505}
506
InKi Daeee378a52010-05-24 12:21:36 -0700507static int s6e63m0_power_on(struct s6e63m0 *lcd)
508{
509 int ret = 0;
Jingoo Han892dc2e2013-02-21 16:43:23 -0800510 struct lcd_platform_data *pd;
511 struct backlight_device *bd;
InKi Daeee378a52010-05-24 12:21:36 -0700512
513 pd = lcd->lcd_pd;
InKi Daeee378a52010-05-24 12:21:36 -0700514 bd = lcd->bd;
InKi Daeee378a52010-05-24 12:21:36 -0700515
516 if (!pd->power_on) {
517 dev_err(lcd->dev, "power_on is NULL.\n");
Jingoo Han81c650e2013-02-21 16:43:24 -0800518 return -EINVAL;
InKi Daeee378a52010-05-24 12:21:36 -0700519 } else {
520 pd->power_on(lcd->ld, 1);
Jingoo Han97a4d9d2013-02-21 16:43:21 -0800521 msleep(pd->power_on_delay);
InKi Daeee378a52010-05-24 12:21:36 -0700522 }
523
524 if (!pd->reset) {
525 dev_err(lcd->dev, "reset is NULL.\n");
Jingoo Han81c650e2013-02-21 16:43:24 -0800526 return -EINVAL;
InKi Daeee378a52010-05-24 12:21:36 -0700527 } else {
528 pd->reset(lcd->ld);
Jingoo Han97a4d9d2013-02-21 16:43:21 -0800529 msleep(pd->reset_delay);
InKi Daeee378a52010-05-24 12:21:36 -0700530 }
531
532 ret = s6e63m0_ldi_init(lcd);
533 if (ret) {
534 dev_err(lcd->dev, "failed to initialize ldi.\n");
535 return ret;
536 }
537
538 ret = s6e63m0_ldi_enable(lcd);
539 if (ret) {
540 dev_err(lcd->dev, "failed to enable ldi.\n");
541 return ret;
542 }
543
544 /* set brightness to current value after power on or resume. */
545 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
546 if (ret) {
547 dev_err(lcd->dev, "lcd gamma setting failed.\n");
548 return ret;
549 }
550
551 return 0;
552}
553
554static int s6e63m0_power_off(struct s6e63m0 *lcd)
555{
Jingoo Han892dc2e2013-02-21 16:43:23 -0800556 int ret;
557 struct lcd_platform_data *pd;
InKi Daeee378a52010-05-24 12:21:36 -0700558
559 pd = lcd->lcd_pd;
InKi Daeee378a52010-05-24 12:21:36 -0700560
561 ret = s6e63m0_ldi_disable(lcd);
562 if (ret) {
563 dev_err(lcd->dev, "lcd setting failed.\n");
564 return -EIO;
565 }
566
Jingoo Han97a4d9d2013-02-21 16:43:21 -0800567 msleep(pd->power_off_delay);
InKi Daeee378a52010-05-24 12:21:36 -0700568
Jingoo Han892dc2e2013-02-21 16:43:23 -0800569 pd->power_on(lcd->ld, 0);
InKi Daeee378a52010-05-24 12:21:36 -0700570
571 return 0;
572}
573
574static int s6e63m0_power(struct s6e63m0 *lcd, int power)
575{
576 int ret = 0;
577
Jingoo Han892dc2e2013-02-21 16:43:23 -0800578 if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power))
InKi Daeee378a52010-05-24 12:21:36 -0700579 ret = s6e63m0_power_on(lcd);
Jingoo Han892dc2e2013-02-21 16:43:23 -0800580 else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power))
InKi Daeee378a52010-05-24 12:21:36 -0700581 ret = s6e63m0_power_off(lcd);
582
583 if (!ret)
584 lcd->power = power;
585
586 return ret;
587}
588
589static int s6e63m0_set_power(struct lcd_device *ld, int power)
590{
591 struct s6e63m0 *lcd = lcd_get_data(ld);
592
593 if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
594 power != FB_BLANK_NORMAL) {
595 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
596 return -EINVAL;
597 }
598
599 return s6e63m0_power(lcd, power);
600}
601
602static int s6e63m0_get_power(struct lcd_device *ld)
603{
604 struct s6e63m0 *lcd = lcd_get_data(ld);
605
606 return lcd->power;
607}
608
609static int s6e63m0_get_brightness(struct backlight_device *bd)
610{
611 return bd->props.brightness;
612}
613
614static int s6e63m0_set_brightness(struct backlight_device *bd)
615{
616 int ret = 0, brightness = bd->props.brightness;
617 struct s6e63m0 *lcd = bl_get_data(bd);
618
619 if (brightness < MIN_BRIGHTNESS ||
620 brightness > bd->props.max_brightness) {
621 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
622 MIN_BRIGHTNESS, MAX_BRIGHTNESS);
623 return -EINVAL;
624 }
625
626 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
627 if (ret) {
628 dev_err(&bd->dev, "lcd brightness setting failed.\n");
629 return -EIO;
630 }
631
632 return ret;
633}
634
635static struct lcd_ops s6e63m0_lcd_ops = {
636 .set_power = s6e63m0_set_power,
637 .get_power = s6e63m0_get_power,
638};
639
640static const struct backlight_ops s6e63m0_backlight_ops = {
641 .get_brightness = s6e63m0_get_brightness,
642 .update_status = s6e63m0_set_brightness,
643};
644
645static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
646 struct device_attribute *attr, char *buf)
647{
648 struct s6e63m0 *lcd = dev_get_drvdata(dev);
649 char temp[10];
650
651 switch (lcd->gamma_mode) {
652 case 0:
653 sprintf(temp, "2.2 mode\n");
654 strcat(buf, temp);
655 break;
656 case 1:
657 sprintf(temp, "1.9 mode\n");
658 strcat(buf, temp);
659 break;
660 case 2:
661 sprintf(temp, "1.7 mode\n");
662 strcat(buf, temp);
663 break;
664 default:
665 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
666 break;
667 }
668
669 return strlen(buf);
670}
671
672static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
673 struct device_attribute *attr,
674 const char *buf, size_t len)
675{
676 struct s6e63m0 *lcd = dev_get_drvdata(dev);
677 struct backlight_device *bd = NULL;
678 int brightness, rc;
679
Dan Carpentercf2b94d2012-03-15 15:17:12 -0700680 rc = kstrtouint(buf, 0, &lcd->gamma_mode);
InKi Daeee378a52010-05-24 12:21:36 -0700681 if (rc < 0)
682 return rc;
683
684 bd = lcd->bd;
685
686 brightness = bd->props.brightness;
687
688 switch (lcd->gamma_mode) {
689 case 0:
690 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
691 break;
692 case 1:
693 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
694 break;
695 case 2:
696 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
697 break;
698 default:
699 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
700 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
701 break;
702 }
703 return len;
704}
705
706static DEVICE_ATTR(gamma_mode, 0644,
707 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
708
709static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
710 struct device_attribute *attr, char *buf)
711{
712 struct s6e63m0 *lcd = dev_get_drvdata(dev);
713 char temp[3];
714
715 sprintf(temp, "%d\n", lcd->gamma_table_count);
716 strcpy(buf, temp);
717
718 return strlen(buf);
719}
Axel Lined3a6782010-11-11 14:05:25 -0800720static DEVICE_ATTR(gamma_table, 0444,
InKi Daeee378a52010-05-24 12:21:36 -0700721 s6e63m0_sysfs_show_gamma_table, NULL);
722
Bill Pemberton1b9e4502012-11-19 13:21:46 -0500723static int s6e63m0_probe(struct spi_device *spi)
InKi Daeee378a52010-05-24 12:21:36 -0700724{
725 int ret = 0;
726 struct s6e63m0 *lcd = NULL;
727 struct lcd_device *ld = NULL;
728 struct backlight_device *bd = NULL;
Axel Linef22f6a2011-07-25 17:12:01 -0700729 struct backlight_properties props;
InKi Daeee378a52010-05-24 12:21:36 -0700730
Jingoo Han541f9362012-05-29 15:07:24 -0700731 lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
InKi Daeee378a52010-05-24 12:21:36 -0700732 if (!lcd)
733 return -ENOMEM;
734
735 /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
736 spi->bits_per_word = 9;
737
738 ret = spi_setup(spi);
739 if (ret < 0) {
740 dev_err(&spi->dev, "spi setup failed.\n");
Jingoo Han541f9362012-05-29 15:07:24 -0700741 return ret;
InKi Daeee378a52010-05-24 12:21:36 -0700742 }
743
744 lcd->spi = spi;
745 lcd->dev = &spi->dev;
746
Jingoo Han3c48d1f2012-12-17 16:00:55 -0800747 lcd->lcd_pd = spi->dev.platform_data;
InKi Daeee378a52010-05-24 12:21:36 -0700748 if (!lcd->lcd_pd) {
749 dev_err(&spi->dev, "platform data is NULL.\n");
Jingoo Han81c650e2013-02-21 16:43:24 -0800750 return -EINVAL;
InKi Daeee378a52010-05-24 12:21:36 -0700751 }
752
753 ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
Jingoo Han541f9362012-05-29 15:07:24 -0700754 if (IS_ERR(ld))
755 return PTR_ERR(ld);
InKi Daeee378a52010-05-24 12:21:36 -0700756
757 lcd->ld = ld;
758
Axel Linef22f6a2011-07-25 17:12:01 -0700759 memset(&props, 0, sizeof(struct backlight_properties));
760 props.type = BACKLIGHT_RAW;
761 props.max_brightness = MAX_BRIGHTNESS;
762
InKi Daeee378a52010-05-24 12:21:36 -0700763 bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
Axel Linef22f6a2011-07-25 17:12:01 -0700764 &s6e63m0_backlight_ops, &props);
InKi Daeee378a52010-05-24 12:21:36 -0700765 if (IS_ERR(bd)) {
766 ret = PTR_ERR(bd);
767 goto out_lcd_unregister;
768 }
769
InKi Daeee378a52010-05-24 12:21:36 -0700770 bd->props.brightness = MAX_BRIGHTNESS;
771 lcd->bd = bd;
772
773 /*
774 * it gets gamma table count available so it gets user
775 * know that.
776 */
777 lcd->gamma_table_count =
778 sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
779
780 ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
781 if (ret < 0)
782 dev_err(&(spi->dev), "failed to add sysfs entries\n");
783
784 ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
785 if (ret < 0)
786 dev_err(&(spi->dev), "failed to add sysfs entries\n");
787
788 /*
789 * if lcd panel was on from bootloader like u-boot then
790 * do not lcd on.
791 */
792 if (!lcd->lcd_pd->lcd_enabled) {
793 /*
794 * if lcd panel was off from bootloader then
795 * current lcd status is powerdown and then
796 * it enables lcd panel.
797 */
798 lcd->power = FB_BLANK_POWERDOWN;
799
800 s6e63m0_power(lcd, FB_BLANK_UNBLANK);
Jingoo Han892dc2e2013-02-21 16:43:23 -0800801 } else {
InKi Daeee378a52010-05-24 12:21:36 -0700802 lcd->power = FB_BLANK_UNBLANK;
Jingoo Han892dc2e2013-02-21 16:43:23 -0800803 }
InKi Daeee378a52010-05-24 12:21:36 -0700804
805 dev_set_drvdata(&spi->dev, lcd);
806
807 dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
808
809 return 0;
810
811out_lcd_unregister:
812 lcd_device_unregister(ld);
InKi Daeee378a52010-05-24 12:21:36 -0700813 return ret;
814}
815
Bill Pemberton7e4b9d02012-11-19 13:26:34 -0500816static int s6e63m0_remove(struct spi_device *spi)
InKi Daeee378a52010-05-24 12:21:36 -0700817{
818 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
819
820 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
Axel Lind974e002010-11-11 14:05:26 -0800821 device_remove_file(&spi->dev, &dev_attr_gamma_table);
822 device_remove_file(&spi->dev, &dev_attr_gamma_mode);
823 backlight_device_unregister(lcd->bd);
InKi Daeee378a52010-05-24 12:21:36 -0700824 lcd_device_unregister(lcd->ld);
InKi Daeee378a52010-05-24 12:21:36 -0700825
826 return 0;
827}
828
829#if defined(CONFIG_PM)
InKi Daeee378a52010-05-24 12:21:36 -0700830static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
831{
InKi Daeee378a52010-05-24 12:21:36 -0700832 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
833
834 dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
835
InKi Daeee378a52010-05-24 12:21:36 -0700836 /*
837 * when lcd panel is suspend, lcd panel becomes off
838 * regardless of status.
839 */
Jingoo Hanb6bef462013-02-21 16:43:25 -0800840 return s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
InKi Daeee378a52010-05-24 12:21:36 -0700841}
842
843static int s6e63m0_resume(struct spi_device *spi)
844{
InKi Daeee378a52010-05-24 12:21:36 -0700845 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
846
Jingoo Hanb6bef462013-02-21 16:43:25 -0800847 lcd->power = FB_BLANK_POWERDOWN;
InKi Daeee378a52010-05-24 12:21:36 -0700848
Jingoo Hanb6bef462013-02-21 16:43:25 -0800849 return s6e63m0_power(lcd, FB_BLANK_UNBLANK);
InKi Daeee378a52010-05-24 12:21:36 -0700850}
851#else
852#define s6e63m0_suspend NULL
853#define s6e63m0_resume NULL
854#endif
855
856/* Power down all displays on reboot, poweroff or halt. */
857static void s6e63m0_shutdown(struct spi_device *spi)
858{
859 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
860
861 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
862}
863
864static struct spi_driver s6e63m0_driver = {
865 .driver = {
866 .name = "s6e63m0",
InKi Daeee378a52010-05-24 12:21:36 -0700867 .owner = THIS_MODULE,
868 },
869 .probe = s6e63m0_probe,
Bill Pembertond1723fa2012-11-19 13:21:09 -0500870 .remove = s6e63m0_remove,
InKi Daeee378a52010-05-24 12:21:36 -0700871 .shutdown = s6e63m0_shutdown,
872 .suspend = s6e63m0_suspend,
873 .resume = s6e63m0_resume,
874};
875
Axel Lin462dd832012-03-23 15:01:59 -0700876module_spi_driver(s6e63m0_driver);
InKi Daeee378a52010-05-24 12:21:36 -0700877
878MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
879MODULE_DESCRIPTION("S6E63M0 LCD Driver");
880MODULE_LICENSE("GPL");
881