blob: 767b802cf6aae7510704012943f2007d353eefd2 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include "msm_fb.h"
15
16#include <linux/memory.h>
17#include <linux/kernel.h>
18#include <linux/sched.h>
19#include <linux/time.h>
20#include <linux/init.h>
21#include <linux/interrupt.h>
22#include "linux/proc_fs.h"
23
24#include <linux/delay.h>
25
26#include <mach/hardware.h>
27#include <linux/io.h>
28
29#include <asm/system.h>
30#include <asm/mach-types.h>
31
32/* The following are for MSM5100 on Gator
33*/
34#ifdef FEATURE_PM1000
35#include "pm1000.h"
36#endif /* FEATURE_PM1000 */
37/* The following are for MSM6050 on Bambi
38*/
39#ifdef FEATURE_PMIC_LCDKBD_LED_DRIVER
40#include "pm.h"
41#endif /* FEATURE_PMIC_LCDKBD_LED_DRIVER */
42
43#ifdef DISP_DEVICE_18BPP
44#undef DISP_DEVICE_18BPP
45#define DISP_DEVICE_16BPP
46#endif
47
48#define QCIF_WIDTH 176
49#define QCIF_HEIGHT 220
50
51static void *DISP_CMD_PORT;
52static void *DISP_DATA_PORT;
53
54#define DISP_CMD_DISON 0xaf
55#define DISP_CMD_DISOFF 0xae
56#define DISP_CMD_DISNOR 0xa6
57#define DISP_CMD_DISINV 0xa7
58#define DISP_CMD_DISCTL 0xca
59#define DISP_CMD_GCP64 0xcb
60#define DISP_CMD_GCP16 0xcc
61#define DISP_CMD_GSSET 0xcd
62#define DISP_GS_2 0x02
63#define DISP_GS_16 0x01
64#define DISP_GS_64 0x00
65#define DISP_CMD_SLPIN 0x95
66#define DISP_CMD_SLPOUT 0x94
67#define DISP_CMD_SD_PSET 0x75
68#define DISP_CMD_MD_PSET 0x76
69#define DISP_CMD_SD_CSET 0x15
70#define DISP_CMD_MD_CSET 0x16
71#define DISP_CMD_DATCTL 0xbc
72#define DISP_DATCTL_666 0x08
73#define DISP_DATCTL_565 0x28
74#define DISP_DATCTL_444 0x38
75#define DISP_CMD_RAMWR 0x5c
76#define DISP_CMD_RAMRD 0x5d
77#define DISP_CMD_PTLIN 0xa8
78#define DISP_CMD_PTLOUT 0xa9
79#define DISP_CMD_ASCSET 0xaa
80#define DISP_CMD_SCSTART 0xab
81#define DISP_CMD_VOLCTL 0xc6
82#define DISP_VOLCTL_TONE 0x80
83#define DISP_CMD_NOp 0x25
84#define DISP_CMD_OSSEL 0xd0
85#define DISP_CMD_3500KSET 0xd1
86#define DISP_CMD_3500KEND 0xd2
87#define DISP_CMD_14MSET 0xd3
88#define DISP_CMD_14MEND 0xd4
89
90#define DISP_CMD_OUT(cmd) outpw(DISP_CMD_PORT, cmd);
91
92#define DISP_DATA_OUT(data) outpw(DISP_DATA_PORT, data);
93
94#define DISP_DATA_IN() inpw(DISP_DATA_PORT);
95
96/* Epson device column number starts at 2
97*/
98#define DISP_SET_RECT(ulhc_row, lrhc_row, ulhc_col, lrhc_col) \
99 DISP_CMD_OUT(DISP_CMD_SD_PSET) \
100 DISP_DATA_OUT((ulhc_row) & 0xFF) \
101 DISP_DATA_OUT((ulhc_row) >> 8) \
102 DISP_DATA_OUT((lrhc_row) & 0xFF) \
103 DISP_DATA_OUT((lrhc_row) >> 8) \
104 DISP_CMD_OUT(DISP_CMD_SD_CSET) \
105 DISP_DATA_OUT(((ulhc_col)+2) & 0xFF) \
106 DISP_DATA_OUT(((ulhc_col)+2) >> 8) \
107 DISP_DATA_OUT(((lrhc_col)+2) & 0xFF) \
108 DISP_DATA_OUT(((lrhc_col)+2) >> 8)
109
110#define DISP_MIN_CONTRAST 0
111#define DISP_MAX_CONTRAST 127
112#define DISP_DEFAULT_CONTRAST 80
113
114#define DISP_MIN_BACKLIGHT 0
115#define DISP_MAX_BACKLIGHT 15
116#define DISP_DEFAULT_BACKLIGHT 2
117
118#define WAIT_SEC(sec) mdelay((sec)/1000)
119
120static word disp_area_start_row;
121static word disp_area_end_row;
122static byte disp_contrast = DISP_DEFAULT_CONTRAST;
123static boolean disp_powered_up;
124static boolean disp_initialized = FALSE;
125/* For some reason the contrast set at init time is not good. Need to do
126 * it again
127 */
128static boolean display_on = FALSE;
129static void epsonQcif_disp_init(struct platform_device *pdev);
130static void epsonQcif_disp_set_contrast(word contrast);
131static void epsonQcif_disp_set_display_area(word start_row, word end_row);
132static int epsonQcif_disp_off(struct platform_device *pdev);
133static int epsonQcif_disp_on(struct platform_device *pdev);
134static void epsonQcif_disp_set_rect(int x, int y, int xres, int yres);
135
136volatile word databack;
137static void epsonQcif_disp_init(struct platform_device *pdev)
138{
139 struct msm_fb_data_type *mfd;
140
141 int i;
142
143 if (disp_initialized)
144 return;
145
146 mfd = platform_get_drvdata(pdev);
147
148 DISP_CMD_PORT = mfd->cmd_port;
149 DISP_DATA_PORT = mfd->data_port;
150
151 /* Sleep in */
152 DISP_CMD_OUT(DISP_CMD_SLPIN);
153
154 /* Display off */
155 DISP_CMD_OUT(DISP_CMD_DISOFF);
156
157 /* Display normal */
158 DISP_CMD_OUT(DISP_CMD_DISNOR);
159
160 /* Set data mode */
161 DISP_CMD_OUT(DISP_CMD_DATCTL);
162 DISP_DATA_OUT(DISP_DATCTL_565);
163
164 /* Set display timing */
165 DISP_CMD_OUT(DISP_CMD_DISCTL);
166 DISP_DATA_OUT(0x1c); /* p1 */
167 DISP_DATA_OUT(0x02); /* p1 */
168 DISP_DATA_OUT(0x82); /* p2 */
169 DISP_DATA_OUT(0x00); /* p3 */
170 DISP_DATA_OUT(0x00); /* p4 */
171 DISP_DATA_OUT(0xe0); /* p5 */
172 DISP_DATA_OUT(0x00); /* p5 */
173 DISP_DATA_OUT(0xdc); /* p6 */
174 DISP_DATA_OUT(0x00); /* p6 */
175 DISP_DATA_OUT(0x02); /* p7 */
176 DISP_DATA_OUT(0x00); /* p8 */
177
178 /* Set 64 gray scale level */
179 DISP_CMD_OUT(DISP_CMD_GCP64);
180 DISP_DATA_OUT(0x08); /* p01 */
181 DISP_DATA_OUT(0x00);
182 DISP_DATA_OUT(0x2a); /* p02 */
183 DISP_DATA_OUT(0x00);
184 DISP_DATA_OUT(0x4e); /* p03 */
185 DISP_DATA_OUT(0x00);
186 DISP_DATA_OUT(0x6b); /* p04 */
187 DISP_DATA_OUT(0x00);
188 DISP_DATA_OUT(0x88); /* p05 */
189 DISP_DATA_OUT(0x00);
190 DISP_DATA_OUT(0xa3); /* p06 */
191 DISP_DATA_OUT(0x00);
192 DISP_DATA_OUT(0xba); /* p07 */
193 DISP_DATA_OUT(0x00);
194 DISP_DATA_OUT(0xd1); /* p08 */
195 DISP_DATA_OUT(0x00);
196 DISP_DATA_OUT(0xe5); /* p09 */
197 DISP_DATA_OUT(0x00);
198 DISP_DATA_OUT(0xf3); /* p10 */
199 DISP_DATA_OUT(0x00);
200 DISP_DATA_OUT(0x03); /* p11 */
201 DISP_DATA_OUT(0x01);
202 DISP_DATA_OUT(0x13); /* p12 */
203 DISP_DATA_OUT(0x01);
204 DISP_DATA_OUT(0x22); /* p13 */
205 DISP_DATA_OUT(0x01);
206 DISP_DATA_OUT(0x2f); /* p14 */
207 DISP_DATA_OUT(0x01);
208 DISP_DATA_OUT(0x3b); /* p15 */
209 DISP_DATA_OUT(0x01);
210 DISP_DATA_OUT(0x46); /* p16 */
211 DISP_DATA_OUT(0x01);
212 DISP_DATA_OUT(0x51); /* p17 */
213 DISP_DATA_OUT(0x01);
214 DISP_DATA_OUT(0x5b); /* p18 */
215 DISP_DATA_OUT(0x01);
216 DISP_DATA_OUT(0x64); /* p19 */
217 DISP_DATA_OUT(0x01);
218 DISP_DATA_OUT(0x6c); /* p20 */
219 DISP_DATA_OUT(0x01);
220 DISP_DATA_OUT(0x74); /* p21 */
221 DISP_DATA_OUT(0x01);
222 DISP_DATA_OUT(0x7c); /* p22 */
223 DISP_DATA_OUT(0x01);
224 DISP_DATA_OUT(0x83); /* p23 */
225 DISP_DATA_OUT(0x01);
226 DISP_DATA_OUT(0x8a); /* p24 */
227 DISP_DATA_OUT(0x01);
228 DISP_DATA_OUT(0x91); /* p25 */
229 DISP_DATA_OUT(0x01);
230 DISP_DATA_OUT(0x98); /* p26 */
231 DISP_DATA_OUT(0x01);
232 DISP_DATA_OUT(0x9f); /* p27 */
233 DISP_DATA_OUT(0x01);
234 DISP_DATA_OUT(0xa6); /* p28 */
235 DISP_DATA_OUT(0x01);
236 DISP_DATA_OUT(0xac); /* p29 */
237 DISP_DATA_OUT(0x01);
238 DISP_DATA_OUT(0xb2); /* p30 */
239 DISP_DATA_OUT(0x01);
240 DISP_DATA_OUT(0xb7); /* p31 */
241 DISP_DATA_OUT(0x01);
242 DISP_DATA_OUT(0xbc); /* p32 */
243 DISP_DATA_OUT(0x01);
244 DISP_DATA_OUT(0xc1); /* p33 */
245 DISP_DATA_OUT(0x01);
246 DISP_DATA_OUT(0xc6); /* p34 */
247 DISP_DATA_OUT(0x01);
248 DISP_DATA_OUT(0xcb); /* p35 */
249 DISP_DATA_OUT(0x01);
250 DISP_DATA_OUT(0xd0); /* p36 */
251 DISP_DATA_OUT(0x01);
252 DISP_DATA_OUT(0xd4); /* p37 */
253 DISP_DATA_OUT(0x01);
254 DISP_DATA_OUT(0xd8); /* p38 */
255 DISP_DATA_OUT(0x01);
256 DISP_DATA_OUT(0xdc); /* p39 */
257 DISP_DATA_OUT(0x01);
258 DISP_DATA_OUT(0xe0); /* p40 */
259 DISP_DATA_OUT(0x01);
260 DISP_DATA_OUT(0xe4); /* p41 */
261 DISP_DATA_OUT(0x01);
262 DISP_DATA_OUT(0xe8); /* p42 */
263 DISP_DATA_OUT(0x01);
264 DISP_DATA_OUT(0xec); /* p43 */
265 DISP_DATA_OUT(0x01);
266 DISP_DATA_OUT(0xf0); /* p44 */
267 DISP_DATA_OUT(0x01);
268 DISP_DATA_OUT(0xf4); /* p45 */
269 DISP_DATA_OUT(0x01);
270 DISP_DATA_OUT(0xf8); /* p46 */
271 DISP_DATA_OUT(0x01);
272 DISP_DATA_OUT(0xfb); /* p47 */
273 DISP_DATA_OUT(0x01);
274 DISP_DATA_OUT(0xfe); /* p48 */
275 DISP_DATA_OUT(0x01);
276 DISP_DATA_OUT(0x01); /* p49 */
277 DISP_DATA_OUT(0x02);
278 DISP_DATA_OUT(0x03); /* p50 */
279 DISP_DATA_OUT(0x02);
280 DISP_DATA_OUT(0x05); /* p51 */
281 DISP_DATA_OUT(0x02);
282 DISP_DATA_OUT(0x07); /* p52 */
283 DISP_DATA_OUT(0x02);
284 DISP_DATA_OUT(0x09); /* p53 */
285 DISP_DATA_OUT(0x02);
286 DISP_DATA_OUT(0x0b); /* p54 */
287 DISP_DATA_OUT(0x02);
288 DISP_DATA_OUT(0x0d); /* p55 */
289 DISP_DATA_OUT(0x02);
290 DISP_DATA_OUT(0x0f); /* p56 */
291 DISP_DATA_OUT(0x02);
292 DISP_DATA_OUT(0x11); /* p57 */
293 DISP_DATA_OUT(0x02);
294 DISP_DATA_OUT(0x13); /* p58 */
295 DISP_DATA_OUT(0x02);
296 DISP_DATA_OUT(0x15); /* p59 */
297 DISP_DATA_OUT(0x02);
298 DISP_DATA_OUT(0x17); /* p60 */
299 DISP_DATA_OUT(0x02);
300 DISP_DATA_OUT(0x19); /* p61 */
301 DISP_DATA_OUT(0x02);
302 DISP_DATA_OUT(0x1b); /* p62 */
303 DISP_DATA_OUT(0x02);
304 DISP_DATA_OUT(0x1c); /* p63 */
305 DISP_DATA_OUT(0x02);
306
307 /* Set 16 gray scale level */
308 DISP_CMD_OUT(DISP_CMD_GCP16);
309 DISP_DATA_OUT(0x1a); /* p01 */
310 DISP_DATA_OUT(0x32); /* p02 */
311 DISP_DATA_OUT(0x42); /* p03 */
312 DISP_DATA_OUT(0x4c); /* p04 */
313 DISP_DATA_OUT(0x58); /* p05 */
314 DISP_DATA_OUT(0x5f); /* p06 */
315 DISP_DATA_OUT(0x66); /* p07 */
316 DISP_DATA_OUT(0x6b); /* p08 */
317 DISP_DATA_OUT(0x70); /* p09 */
318 DISP_DATA_OUT(0x74); /* p10 */
319 DISP_DATA_OUT(0x78); /* p11 */
320 DISP_DATA_OUT(0x7b); /* p12 */
321 DISP_DATA_OUT(0x7e); /* p13 */
322 DISP_DATA_OUT(0x80); /* p14 */
323 DISP_DATA_OUT(0x82); /* p15 */
324
325 /* Set DSP column */
326 DISP_CMD_OUT(DISP_CMD_MD_CSET);
327 DISP_DATA_OUT(0xff);
328 DISP_DATA_OUT(0x03);
329 DISP_DATA_OUT(0xff);
330 DISP_DATA_OUT(0x03);
331
332 /* Set DSP page */
333 DISP_CMD_OUT(DISP_CMD_MD_PSET);
334 DISP_DATA_OUT(0xff);
335 DISP_DATA_OUT(0x01);
336 DISP_DATA_OUT(0xff);
337 DISP_DATA_OUT(0x01);
338
339 /* Set ARM column */
340 DISP_CMD_OUT(DISP_CMD_SD_CSET);
341 DISP_DATA_OUT(0x02);
342 DISP_DATA_OUT(0x00);
343 DISP_DATA_OUT((QCIF_WIDTH + 1) & 0xFF);
344 DISP_DATA_OUT((QCIF_WIDTH + 1) >> 8);
345
346 /* Set ARM page */
347 DISP_CMD_OUT(DISP_CMD_SD_PSET);
348 DISP_DATA_OUT(0x00);
349 DISP_DATA_OUT(0x00);
350 DISP_DATA_OUT((QCIF_HEIGHT - 1) & 0xFF);
351 DISP_DATA_OUT((QCIF_HEIGHT - 1) >> 8);
352
353 /* Set 64 gray scales */
354 DISP_CMD_OUT(DISP_CMD_GSSET);
355 DISP_DATA_OUT(DISP_GS_64);
356
357 DISP_CMD_OUT(DISP_CMD_OSSEL);
358 DISP_DATA_OUT(0);
359
360 /* Sleep out */
361 DISP_CMD_OUT(DISP_CMD_SLPOUT);
362
363 WAIT_SEC(40000);
364
365 /* Initialize power IC */
366 DISP_CMD_OUT(DISP_CMD_VOLCTL);
367 DISP_DATA_OUT(DISP_VOLCTL_TONE);
368
369 WAIT_SEC(40000);
370
371 /* Set electronic volume, d'xx */
372 DISP_CMD_OUT(DISP_CMD_VOLCTL);
373 DISP_DATA_OUT(DISP_DEFAULT_CONTRAST); /* value from 0 to 127 */
374
375 /* Initialize display data */
376 DISP_SET_RECT(0, (QCIF_HEIGHT - 1), 0, (QCIF_WIDTH - 1));
377 DISP_CMD_OUT(DISP_CMD_RAMWR);
378 for (i = 0; i < QCIF_HEIGHT * QCIF_WIDTH; i++)
379 DISP_DATA_OUT(0xffff);
380
381 DISP_CMD_OUT(DISP_CMD_RAMRD);
382 databack = DISP_DATA_IN();
383 databack = DISP_DATA_IN();
384 databack = DISP_DATA_IN();
385 databack = DISP_DATA_IN();
386
387 WAIT_SEC(80000);
388
389 DISP_CMD_OUT(DISP_CMD_DISON);
390
391 disp_area_start_row = 0;
392 disp_area_end_row = QCIF_HEIGHT - 1;
393 disp_powered_up = TRUE;
394 disp_initialized = TRUE;
395 epsonQcif_disp_set_display_area(0, QCIF_HEIGHT - 1);
396 display_on = TRUE;
397}
398
399static void epsonQcif_disp_set_rect(int x, int y, int xres, int yres)
400{
401 if (!disp_initialized)
402 return;
403
404 DISP_SET_RECT(y, y + yres - 1, x, x + xres - 1);
405 DISP_CMD_OUT(DISP_CMD_RAMWR);
406}
407
408static void epsonQcif_disp_set_display_area(word start_row, word end_row)
409{
410 if (!disp_initialized)
411 return;
412
413 if ((start_row == disp_area_start_row)
414 && (end_row == disp_area_end_row))
415 return;
416 disp_area_start_row = start_row;
417 disp_area_end_row = end_row;
418
419 /* Range checking
420 */
421 if (end_row >= QCIF_HEIGHT)
422 end_row = QCIF_HEIGHT - 1;
423 if (start_row > end_row)
424 start_row = end_row;
425
426 /* When display is not the full screen, gray scale is set to
427 ** 2; otherwise it is set to 64.
428 */
429 if ((start_row == 0) && (end_row == (QCIF_HEIGHT - 1))) {
430 /* The whole screen */
431 DISP_CMD_OUT(DISP_CMD_PTLOUT);
432 WAIT_SEC(10000);
433 DISP_CMD_OUT(DISP_CMD_DISOFF);
434 WAIT_SEC(100000);
435 DISP_CMD_OUT(DISP_CMD_GSSET);
436 DISP_DATA_OUT(DISP_GS_64);
437 WAIT_SEC(100000);
438 DISP_CMD_OUT(DISP_CMD_DISON);
439 } else {
440 /* partial screen */
441 DISP_CMD_OUT(DISP_CMD_PTLIN);
442 DISP_DATA_OUT(start_row);
443 DISP_DATA_OUT(start_row >> 8);
444 DISP_DATA_OUT(end_row);
445 DISP_DATA_OUT(end_row >> 8);
446 DISP_CMD_OUT(DISP_CMD_GSSET);
447 DISP_DATA_OUT(DISP_GS_2);
448 }
449}
450
451static int epsonQcif_disp_off(struct platform_device *pdev)
452{
453 if (!disp_initialized)
454 epsonQcif_disp_init(pdev);
455
456 if (display_on) {
457 DISP_CMD_OUT(DISP_CMD_DISOFF);
458 DISP_CMD_OUT(DISP_CMD_SLPIN);
459 display_on = FALSE;
460 }
461
462 return 0;
463}
464
465static int epsonQcif_disp_on(struct platform_device *pdev)
466{
467 if (!disp_initialized)
468 epsonQcif_disp_init(pdev);
469
470 if (!display_on) {
471 DISP_CMD_OUT(DISP_CMD_SLPOUT);
472 WAIT_SEC(40000);
473 DISP_CMD_OUT(DISP_CMD_DISON);
474 epsonQcif_disp_set_contrast(disp_contrast);
475 display_on = TRUE;
476 }
477
478 return 0;
479}
480
481static void epsonQcif_disp_set_contrast(word contrast)
482{
483 if (!disp_initialized)
484 return;
485
486 /* Initialize power IC, d'24 */
487 DISP_CMD_OUT(DISP_CMD_VOLCTL);
488 DISP_DATA_OUT(DISP_VOLCTL_TONE);
489
490 WAIT_SEC(40000);
491
492 /* Set electronic volume, d'xx */
493 DISP_CMD_OUT(DISP_CMD_VOLCTL);
494 if (contrast > 127)
495 contrast = 127;
496 DISP_DATA_OUT(contrast); /* value from 0 to 127 */
497 disp_contrast = (byte) contrast;
498} /* End disp_set_contrast */
499
500static void epsonQcif_disp_clear_screen_area(
501 word start_row, word end_row, word start_column, word end_column) {
502 int32 i;
503
504 /* Clear the display screen */
505 DISP_SET_RECT(start_row, end_row, start_column, end_column);
506 DISP_CMD_OUT(DISP_CMD_RAMWR);
507 i = (end_row - start_row + 1) * (end_column - start_column + 1);
508 for (; i > 0; i--)
509 DISP_DATA_OUT(0xffff);
510}
511
512static int __init epsonQcif_probe(struct platform_device *pdev)
513{
514 msm_fb_add_device(pdev);
515
516 return 0;
517}
518
519static struct platform_driver this_driver = {
520 .probe = epsonQcif_probe,
521 .driver = {
522 .name = "ebi2_epson_qcif",
523 },
524};
525
526static struct msm_fb_panel_data epsonQcif_panel_data = {
527 .on = epsonQcif_disp_on,
528 .off = epsonQcif_disp_off,
529 .set_rect = epsonQcif_disp_set_rect,
530};
531
532static struct platform_device this_device = {
533 .name = "ebi2_epson_qcif",
534 .id = 0,
535 .dev = {
536 .platform_data = &epsonQcif_panel_data,
537 }
538};
539
540static int __init epsonQcif_init(void)
541{
542 int ret;
543 struct msm_panel_info *pinfo;
544
545 ret = platform_driver_register(&this_driver);
546 if (!ret) {
547 pinfo = &epsonQcif_panel_data.panel_info;
548 pinfo->xres = QCIF_WIDTH;
549 pinfo->yres = QCIF_HEIGHT;
550 MSM_FB_SINGLE_MODE_PANEL(pinfo);
551 pinfo->type = EBI2_PANEL;
552 pinfo->pdest = DISPLAY_2;
553 pinfo->wait_cycle = 0x808000;
554 pinfo->bpp = 16;
555 pinfo->fb_num = 2;
556 pinfo->lcd.vsync_enable = FALSE;
557
558 ret = platform_device_register(&this_device);
559 if (ret)
560 platform_driver_unregister(&this_driver);
561 }
562
563 return ret;
564}
565
566module_init(epsonQcif_init);