1/* 2 * linux/arch/arm/mach-pxa/corgi_lcd.c 3 * 4 * Corgi/Spitz LCD Specific Code 5 * 6 * Copyright (C) 2005 Richard Purdie 7 * 8 * Connectivity: 9 * Corgi - LCD to ATI Imageon w100 (Wallaby) 10 * Spitz - LCD to PXA Framebuffer 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License version 2 as 14 * published by the Free Software Foundation. 15 * 16 */ 17 18#include <linux/delay.h> 19#include <linux/kernel.h> 20#include <linux/platform_device.h> 21#include <linux/module.h> 22#include <linux/string.h> 23#include <asm/arch/akita.h> 24#include <asm/arch/corgi.h> 25#include <asm/hardware.h> 26#include <asm/arch/pxa-regs.h> 27#include <asm/arch/sharpsl.h> 28#include <asm/arch/spitz.h> 29#include <asm/hardware/scoop.h> 30#include <asm/mach/sharpsl_param.h> 31#include "generic.h" 32 33/* Register Addresses */ 34#define RESCTL_ADRS 0x00 35#define PHACTRL_ADRS 0x01 36#define DUTYCTRL_ADRS 0x02 37#define POWERREG0_ADRS 0x03 38#define POWERREG1_ADRS 0x04 39#define GPOR3_ADRS 0x05 40#define PICTRL_ADRS 0x06 41#define POLCTRL_ADRS 0x07 42 43/* Register Bit Definitions */ 44#define RESCTL_QVGA 0x01 45#define RESCTL_VGA 0x00 46 47#define POWER1_VW_ON 0x01 /* VW Supply FET ON */ 48#define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */ 49#define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */ 50 51#define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */ 52#define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */ 53#define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */ 54 55#define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */ 56#define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */ 57#define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */ 58#define POWER0_COM_ON 0x08 /* COM Power Supply ON */ 59#define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */ 60 61#define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */ 62#define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */ 63#define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */ 64 65#define PICTRL_INIT_STATE 0x01 66#define PICTRL_INIOFF 0x02 67#define PICTRL_POWER_DOWN 0x04 68#define PICTRL_COM_SIGNAL_OFF 0x08 69#define PICTRL_DAC_SIGNAL_OFF 0x10 70 71#define POLCTRL_SYNC_POL_FALL 0x01 72#define POLCTRL_EN_POL_FALL 0x02 73#define POLCTRL_DATA_POL_FALL 0x04 74#define POLCTRL_SYNC_ACT_H 0x08 75#define POLCTRL_EN_ACT_L 0x10 76 77#define POLCTRL_SYNC_POL_RISE 0x00 78#define POLCTRL_EN_POL_RISE 0x00 79#define POLCTRL_DATA_POL_RISE 0x00 80#define POLCTRL_SYNC_ACT_L 0x00 81#define POLCTRL_EN_ACT_H 0x00 82 83#define PHACTRL_PHASE_MANUAL 0x01 84#define DEFAULT_PHAD_QVGA (9) 85#define DEFAULT_COMADJ (125) 86 87/* 88 * This is only a psuedo I2C interface. We can't use the standard kernel 89 * routines as the interface is write only. We just assume the data is acked... 90 */ 91static void lcdtg_ssp_i2c_send(u8 data) 92{ 93 corgi_ssp_lcdtg_send(POWERREG0_ADRS, data); 94 udelay(10); 95} 96 97static void lcdtg_i2c_send_bit(u8 data) 98{ 99 lcdtg_ssp_i2c_send(data); 100 lcdtg_ssp_i2c_send(data | POWER0_COM_DCLK); 101 lcdtg_ssp_i2c_send(data); 102} 103 104static void lcdtg_i2c_send_start(u8 base) 105{ 106 lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK | POWER0_COM_DOUT); 107 lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK); 108 lcdtg_ssp_i2c_send(base); 109} 110 111static void lcdtg_i2c_send_stop(u8 base) 112{ 113 lcdtg_ssp_i2c_send(base); 114 lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK); 115 lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK | POWER0_COM_DOUT); 116} 117 118static void lcdtg_i2c_send_byte(u8 base, u8 data) 119{ 120 int i; 121 for (i = 0; i < 8; i++) { 122 if (data & 0x80) 123 lcdtg_i2c_send_bit(base | POWER0_COM_DOUT); 124 else 125 lcdtg_i2c_send_bit(base); 126 data <<= 1; 127 } 128} 129 130static void lcdtg_i2c_wait_ack(u8 base) 131{ 132 lcdtg_i2c_send_bit(base); 133} 134 135static void lcdtg_set_common_voltage(u8 base_data, u8 data) 136{ 137 /* Set Common Voltage to M62332FP via I2C */ 138 lcdtg_i2c_send_start(base_data); 139 lcdtg_i2c_send_byte(base_data, 0x9c); 140 lcdtg_i2c_wait_ack(base_data); 141 lcdtg_i2c_send_byte(base_data, 0x00); 142 lcdtg_i2c_wait_ack(base_data); 143 lcdtg_i2c_send_byte(base_data, data); 144 lcdtg_i2c_wait_ack(base_data); 145 lcdtg_i2c_send_stop(base_data); 146} 147 148/* Set Phase Adjust */ 149static void lcdtg_set_phadadj(int mode) 150{ 151 int adj; 152 switch(mode) { 153 case 480: 154 case 640: 155 /* Setting for VGA */ 156 adj = sharpsl_param.phadadj; 157 if (adj < 0) { 158 adj = PHACTRL_PHASE_MANUAL; 159 } else { 160 adj = ((adj & 0x0f) << 1) | PHACTRL_PHASE_MANUAL; 161 } 162 break; 163 case 240: 164 case 320: 165 default: 166 /* Setting for QVGA */ 167 adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL; 168 break; 169 } 170 171 corgi_ssp_lcdtg_send(PHACTRL_ADRS, adj); 172} 173 174static int lcd_inited; 175 176static void lcdtg_hw_init(int mode) 177{ 178 if (!lcd_inited) { 179 int comadj; 180 181 /* Initialize Internal Logic & Port */ 182 corgi_ssp_lcdtg_send(PICTRL_ADRS, PICTRL_POWER_DOWN | PICTRL_INIOFF | PICTRL_INIT_STATE 183 | PICTRL_COM_SIGNAL_OFF | PICTRL_DAC_SIGNAL_OFF); 184 185 corgi_ssp_lcdtg_send(POWERREG0_ADRS, POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF 186 | POWER0_COM_OFF | POWER0_VCC5_OFF); 187 188 corgi_ssp_lcdtg_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); 189 190 /* VDD(+8V), SVSS(-4V) ON */ 191 corgi_ssp_lcdtg_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); 192 mdelay(3); 193 194 /* DAC ON */ 195 corgi_ssp_lcdtg_send(POWERREG0_ADRS, POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON 196 | POWER0_COM_OFF | POWER0_VCC5_OFF); 197 198 /* INIB = H, INI = L */ 199 /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */ 200 corgi_ssp_lcdtg_send(PICTRL_ADRS, PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF); 201 202 /* Set Common Voltage */ 203 comadj = sharpsl_param.comadj; 204 if (comadj < 0) 205 comadj = DEFAULT_COMADJ; 206 lcdtg_set_common_voltage((POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF), comadj); 207 208 /* VCC5 ON, DAC ON */ 209 corgi_ssp_lcdtg_send(POWERREG0_ADRS, POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | 210 POWER0_COM_OFF | POWER0_VCC5_ON); 211 212 /* GVSS(-8V) ON, VDD ON */ 213 corgi_ssp_lcdtg_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); 214 mdelay(2); 215 216 /* COM SIGNAL ON (PICTL[3] = L) */ 217 corgi_ssp_lcdtg_send(PICTRL_ADRS, PICTRL_INIT_STATE); 218 219 /* COM ON, DAC ON, VCC5_ON */ 220 corgi_ssp_lcdtg_send(POWERREG0_ADRS, POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON 221 | POWER0_COM_ON | POWER0_VCC5_ON); 222 223 /* VW ON, GVSS ON, VDD ON */ 224 corgi_ssp_lcdtg_send(POWERREG1_ADRS, POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON); 225 226 /* Signals output enable */ 227 corgi_ssp_lcdtg_send(PICTRL_ADRS, 0); 228 229 /* Set Phase Adjust */ 230 lcdtg_set_phadadj(mode); 231 232 /* Initialize for Input Signals from ATI */ 233 corgi_ssp_lcdtg_send(POLCTRL_ADRS, POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE 234 | POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L | POLCTRL_EN_ACT_H); 235 udelay(1000); 236 237 lcd_inited=1; 238 } else { 239 lcdtg_set_phadadj(mode); 240 } 241 242 switch(mode) { 243 case 480: 244 case 640: 245 /* Set Lcd Resolution (VGA) */ 246 corgi_ssp_lcdtg_send(RESCTL_ADRS, RESCTL_VGA); 247 break; 248 case 240: 249 case 320: 250 default: 251 /* Set Lcd Resolution (QVGA) */ 252 corgi_ssp_lcdtg_send(RESCTL_ADRS, RESCTL_QVGA); 253 break; 254 } 255} 256 257static void lcdtg_suspend(void) 258{ 259 /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */ 260 mdelay(34); 261 262 /* (1)VW OFF */ 263 corgi_ssp_lcdtg_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); 264 265 /* (2)COM OFF */ 266 corgi_ssp_lcdtg_send(PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF); 267 corgi_ssp_lcdtg_send(POWERREG0_ADRS, POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON); 268 269 /* (3)Set Common Voltage Bias 0V */ 270 lcdtg_set_common_voltage(POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON, 0); 271 272 /* (4)GVSS OFF */ 273 corgi_ssp_lcdtg_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); 274 275 /* (5)VCC5 OFF */ 276 corgi_ssp_lcdtg_send(POWERREG0_ADRS, POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF); 277 278 /* (6)Set PDWN, INIOFF, DACOFF */ 279 corgi_ssp_lcdtg_send(PICTRL_ADRS, PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF | 280 PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF); 281 282 /* (7)DAC OFF */ 283 corgi_ssp_lcdtg_send(POWERREG0_ADRS, POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF); 284 285 /* (8)VDD OFF */ 286 corgi_ssp_lcdtg_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); 287 288 lcd_inited = 0; 289} 290 291 292/* 293 * Corgi w100 Frame Buffer Device 294 */ 295#ifdef CONFIG_PXA_SHARP_C7xx 296 297#include <video/w100fb.h> 298 299static void w100_lcdtg_suspend(struct w100fb_par *par) 300{ 301 lcdtg_suspend(); 302} 303 304static void w100_lcdtg_init(struct w100fb_par *par) 305{ 306 lcdtg_hw_init(par->xres); 307} 308 309 310static struct w100_tg_info corgi_lcdtg_info = { 311 .change = w100_lcdtg_init, 312 .suspend = w100_lcdtg_suspend, 313 .resume = w100_lcdtg_init, 314}; 315 316static struct w100_mem_info corgi_fb_mem = { 317 .ext_cntl = 0x00040003, 318 .sdram_mode_reg = 0x00650021, 319 .ext_timing_cntl = 0x10002a4a, 320 .io_cntl = 0x7ff87012, 321 .size = 0x1fffff, 322}; 323 324static struct w100_gen_regs corgi_fb_regs = { 325 .lcd_format = 0x00000003, 326 .lcdd_cntl1 = 0x01CC0000, 327 .lcdd_cntl2 = 0x0003FFFF, 328 .genlcd_cntl1 = 0x00FFFF0D, 329 .genlcd_cntl2 = 0x003F3003, 330 .genlcd_cntl3 = 0x000102aa, 331}; 332 333static struct w100_gpio_regs corgi_fb_gpio = { 334 .init_data1 = 0x000000bf, 335 .init_data2 = 0x00000000, 336 .gpio_dir1 = 0x00000000, 337 .gpio_oe1 = 0x03c0feff, 338 .gpio_dir2 = 0x00000000, 339 .gpio_oe2 = 0x00000000, 340}; 341 342static struct w100_mode corgi_fb_modes[] = { 343{ 344 .xres = 480, 345 .yres = 640, 346 .left_margin = 0x56, 347 .right_margin = 0x55, 348 .upper_margin = 0x03, 349 .lower_margin = 0x00, 350 .crtc_ss = 0x82360056, 351 .crtc_ls = 0xA0280000, 352 .crtc_gs = 0x80280028, 353 .crtc_vpos_gs = 0x02830002, 354 .crtc_rev = 0x00400008, 355 .crtc_dclk = 0xA0000000, 356 .crtc_gclk = 0x8015010F, 357 .crtc_goe = 0x80100110, 358 .crtc_ps1_active = 0x41060010, 359 .pll_freq = 75, 360 .fast_pll_freq = 100, 361 .sysclk_src = CLK_SRC_PLL, 362 .sysclk_divider = 0, 363 .pixclk_src = CLK_SRC_PLL, 364 .pixclk_divider = 2, 365 .pixclk_divider_rotated = 6, 366},{ 367 .xres = 240, 368 .yres = 320, 369 .left_margin = 0x27, 370 .right_margin = 0x2e, 371 .upper_margin = 0x01, 372 .lower_margin = 0x00, 373 .crtc_ss = 0x81170027, 374 .crtc_ls = 0xA0140000, 375 .crtc_gs = 0xC0140014, 376 .crtc_vpos_gs = 0x00010141, 377 .crtc_rev = 0x00400008, 378 .crtc_dclk = 0xA0000000, 379 .crtc_gclk = 0x8015010F, 380 .crtc_goe = 0x80100110, 381 .crtc_ps1_active = 0x41060010, 382 .pll_freq = 0, 383 .fast_pll_freq = 0, 384 .sysclk_src = CLK_SRC_XTAL, 385 .sysclk_divider = 0, 386 .pixclk_src = CLK_SRC_XTAL, 387 .pixclk_divider = 1, 388 .pixclk_divider_rotated = 1, 389}, 390 391}; 392 393static struct w100fb_mach_info corgi_fb_info = { 394 .tg = &corgi_lcdtg_info, 395 .init_mode = INIT_MODE_ROTATED, 396 .mem = &corgi_fb_mem, 397 .regs = &corgi_fb_regs, 398 .modelist = &corgi_fb_modes[0], 399 .num_modes = 2, 400 .gpio = &corgi_fb_gpio, 401 .xtal_freq = 12500000, 402 .xtal_dbl = 0, 403}; 404 405static struct resource corgi_fb_resources[] = { 406 [0] = { 407 .start = 0x08000000, 408 .end = 0x08ffffff, 409 .flags = IORESOURCE_MEM, 410 }, 411}; 412 413struct platform_device corgifb_device = { 414 .name = "w100fb", 415 .id = -1, 416 .num_resources = ARRAY_SIZE(corgi_fb_resources), 417 .resource = corgi_fb_resources, 418 .dev = { 419 .platform_data = &corgi_fb_info, 420 .parent = &corgissp_device.dev, 421 }, 422 423}; 424#endif 425 426 427/* 428 * Spitz PXA Frame Buffer Device 429 */ 430#ifdef CONFIG_PXA_SHARP_Cxx00 431 432#include <asm/arch/pxafb.h> 433 434void spitz_lcd_power(int on, struct fb_var_screeninfo *var) 435{ 436 if (on) 437 lcdtg_hw_init(var->xres); 438 else 439 lcdtg_suspend(); 440} 441 442#endif 443 444 445/* 446 * Corgi/Spitz Touchscreen to LCD interface 447 */ 448static unsigned long (*get_hsync_time)(struct device *dev); 449 450static void inline sharpsl_wait_sync(int gpio) 451{ 452 while((GPLR(gpio) & GPIO_bit(gpio)) == 0); 453 while((GPLR(gpio) & GPIO_bit(gpio)) != 0); 454} 455 456#ifdef CONFIG_PXA_SHARP_C7xx 457unsigned long corgi_get_hsync_len(void) 458{ 459 if (!get_hsync_time) 460 get_hsync_time = symbol_get(w100fb_get_hsynclen); 461 if (!get_hsync_time) 462 return 0; 463 464 return get_hsync_time(&corgifb_device.dev); 465} 466 467void corgi_put_hsync(void) 468{ 469 if (get_hsync_time) 470 symbol_put(w100fb_get_hsynclen); 471 get_hsync_time = NULL; 472} 473 474void corgi_wait_hsync(void) 475{ 476 sharpsl_wait_sync(CORGI_GPIO_HSYNC); 477} 478#endif 479 480#ifdef CONFIG_PXA_SHARP_Cxx00 481static struct device *spitz_pxafb_dev; 482 483static int is_pxafb_device(struct device * dev, void * data) 484{ 485 struct platform_device *pdev = container_of(dev, struct platform_device, dev); 486 487 return (strncmp(pdev->name, "pxa2xx-fb", 9) == 0); 488} 489 490unsigned long spitz_get_hsync_len(void) 491{ 492#ifdef CONFIG_FB_PXA 493 if (!spitz_pxafb_dev) { 494 spitz_pxafb_dev = bus_find_device(&platform_bus_type, NULL, NULL, is_pxafb_device); 495 if (!spitz_pxafb_dev) 496 return 0; 497 } 498 if (!get_hsync_time) 499 get_hsync_time = symbol_get(pxafb_get_hsync_time); 500 if (!get_hsync_time) 501#endif 502 return 0; 503 504 return pxafb_get_hsync_time(spitz_pxafb_dev); 505} 506 507void spitz_put_hsync(void) 508{ 509 put_device(spitz_pxafb_dev); 510 if (get_hsync_time) 511 symbol_put(pxafb_get_hsync_time); 512 spitz_pxafb_dev = NULL; 513 get_hsync_time = NULL; 514} 515 516void spitz_wait_hsync(void) 517{ 518 sharpsl_wait_sync(SPITZ_GPIO_HSYNC); 519} 520#endif 521 522/* 523 * Corgi/Spitz Backlight Power 524 */ 525#ifdef CONFIG_PXA_SHARP_C7xx 526void corgi_bl_set_intensity(int intensity) 527{ 528 if (intensity > 0x10) 529 intensity += 0x10; 530 531 /* Bits 0-4 are accessed via the SSP interface */ 532 corgi_ssp_blduty_set(intensity & 0x1f); 533 534 /* Bit 5 is via SCOOP */ 535 if (intensity & 0x0020) 536 set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT); 537 else 538 reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT); 539} 540#endif 541 542 543#if defined(CONFIG_MACH_SPITZ) || defined(CONFIG_MACH_BORZOI) 544void spitz_bl_set_intensity(int intensity) 545{ 546 if (intensity > 0x10) 547 intensity += 0x10; 548 549 /* Bits 0-4 are accessed via the SSP interface */ 550 corgi_ssp_blduty_set(intensity & 0x1f); 551 552 /* Bit 5 is via SCOOP */ 553 if (intensity & 0x0020) 554 reset_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_BACKLIGHT_CONT); 555 else 556 set_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_BACKLIGHT_CONT); 557 558 if (intensity) 559 set_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_BACKLIGHT_ON); 560 else 561 reset_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_BACKLIGHT_ON); 562} 563#endif 564 565#ifdef CONFIG_MACH_AKITA 566void akita_bl_set_intensity(int intensity) 567{ 568 if (intensity > 0x10) 569 intensity += 0x10; 570 571 /* Bits 0-4 are accessed via the SSP interface */ 572 corgi_ssp_blduty_set(intensity & 0x1f); 573 574 /* Bit 5 is via IO-Expander */ 575 if (intensity & 0x0020) 576 akita_reset_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_BACKLIGHT_CONT); 577 else 578 akita_set_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_BACKLIGHT_CONT); 579 580 if (intensity) 581 akita_set_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_BACKLIGHT_ON); 582 else 583 akita_reset_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_BACKLIGHT_ON); 584} 585#endif 586