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