Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.0-rc5 1142 lines 28 kB view raw
1/* linux/drivers/video/s3c2410fb.c 2 * Copyright (c) 2004,2005 Arnaud Patard 3 * Copyright (c) 2004-2008 Ben Dooks 4 * 5 * S3C2410 LCD Framebuffer Driver 6 * 7 * This file is subject to the terms and conditions of the GNU General Public 8 * License. See the file COPYING in the main directory of this archive for 9 * more details. 10 * 11 * Driver based on skeletonfb.c, sa1100fb.c and others. 12*/ 13 14#include <linux/module.h> 15#include <linux/kernel.h> 16#include <linux/err.h> 17#include <linux/errno.h> 18#include <linux/string.h> 19#include <linux/mm.h> 20#include <linux/slab.h> 21#include <linux/delay.h> 22#include <linux/fb.h> 23#include <linux/init.h> 24#include <linux/dma-mapping.h> 25#include <linux/interrupt.h> 26#include <linux/platform_device.h> 27#include <linux/clk.h> 28#include <linux/cpufreq.h> 29 30#include <asm/io.h> 31#include <asm/div64.h> 32 33#include <asm/mach/map.h> 34#include <mach/regs-lcd.h> 35#include <mach/regs-gpio.h> 36#include <mach/fb.h> 37 38#ifdef CONFIG_PM 39#include <linux/pm.h> 40#endif 41 42#include "s3c2410fb.h" 43 44/* Debugging stuff */ 45#ifdef CONFIG_FB_S3C2410_DEBUG 46static int debug = 1; 47#else 48static int debug = 0; 49#endif 50 51#define dprintk(msg...) if (debug) { printk(KERN_DEBUG "s3c2410fb: " msg); } 52 53/* useful functions */ 54 55static int is_s3c2412(struct s3c2410fb_info *fbi) 56{ 57 return (fbi->drv_type == DRV_S3C2412); 58} 59 60/* s3c2410fb_set_lcdaddr 61 * 62 * initialise lcd controller address pointers 63 */ 64static void s3c2410fb_set_lcdaddr(struct fb_info *info) 65{ 66 unsigned long saddr1, saddr2, saddr3; 67 struct s3c2410fb_info *fbi = info->par; 68 void __iomem *regs = fbi->io; 69 70 saddr1 = info->fix.smem_start >> 1; 71 saddr2 = info->fix.smem_start; 72 saddr2 += info->fix.line_length * info->var.yres; 73 saddr2 >>= 1; 74 75 saddr3 = S3C2410_OFFSIZE(0) | 76 S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); 77 78 dprintk("LCDSADDR1 = 0x%08lx\n", saddr1); 79 dprintk("LCDSADDR2 = 0x%08lx\n", saddr2); 80 dprintk("LCDSADDR3 = 0x%08lx\n", saddr3); 81 82 writel(saddr1, regs + S3C2410_LCDSADDR1); 83 writel(saddr2, regs + S3C2410_LCDSADDR2); 84 writel(saddr3, regs + S3C2410_LCDSADDR3); 85} 86 87/* s3c2410fb_calc_pixclk() 88 * 89 * calculate divisor for clk->pixclk 90 */ 91static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi, 92 unsigned long pixclk) 93{ 94 unsigned long clk = fbi->clk_rate; 95 unsigned long long div; 96 97 /* pixclk is in picoseconds, our clock is in Hz 98 * 99 * Hz -> picoseconds is / 10^-12 100 */ 101 102 div = (unsigned long long)clk * pixclk; 103 div >>= 12; /* div / 2^12 */ 104 do_div(div, 625 * 625UL * 625); /* div / 5^12 */ 105 106 dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div); 107 return div; 108} 109 110/* 111 * s3c2410fb_check_var(): 112 * Get the video params out of 'var'. If a value doesn't fit, round it up, 113 * if it's too big, return -EINVAL. 114 * 115 */ 116static int s3c2410fb_check_var(struct fb_var_screeninfo *var, 117 struct fb_info *info) 118{ 119 struct s3c2410fb_info *fbi = info->par; 120 struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data; 121 struct s3c2410fb_display *display = NULL; 122 struct s3c2410fb_display *default_display = mach_info->displays + 123 mach_info->default_display; 124 int type = default_display->type; 125 unsigned i; 126 127 dprintk("check_var(var=%p, info=%p)\n", var, info); 128 129 /* validate x/y resolution */ 130 /* choose default mode if possible */ 131 if (var->yres == default_display->yres && 132 var->xres == default_display->xres && 133 var->bits_per_pixel == default_display->bpp) 134 display = default_display; 135 else 136 for (i = 0; i < mach_info->num_displays; i++) 137 if (type == mach_info->displays[i].type && 138 var->yres == mach_info->displays[i].yres && 139 var->xres == mach_info->displays[i].xres && 140 var->bits_per_pixel == mach_info->displays[i].bpp) { 141 display = mach_info->displays + i; 142 break; 143 } 144 145 if (!display) { 146 dprintk("wrong resolution or depth %dx%d at %d bpp\n", 147 var->xres, var->yres, var->bits_per_pixel); 148 return -EINVAL; 149 } 150 151 /* it is always the size as the display */ 152 var->xres_virtual = display->xres; 153 var->yres_virtual = display->yres; 154 var->height = display->height; 155 var->width = display->width; 156 157 /* copy lcd settings */ 158 var->pixclock = display->pixclock; 159 var->left_margin = display->left_margin; 160 var->right_margin = display->right_margin; 161 var->upper_margin = display->upper_margin; 162 var->lower_margin = display->lower_margin; 163 var->vsync_len = display->vsync_len; 164 var->hsync_len = display->hsync_len; 165 166 fbi->regs.lcdcon5 = display->lcdcon5; 167 /* set display type */ 168 fbi->regs.lcdcon1 = display->type; 169 170 var->transp.offset = 0; 171 var->transp.length = 0; 172 /* set r/g/b positions */ 173 switch (var->bits_per_pixel) { 174 case 1: 175 case 2: 176 case 4: 177 var->red.offset = 0; 178 var->red.length = var->bits_per_pixel; 179 var->green = var->red; 180 var->blue = var->red; 181 break; 182 case 8: 183 if (display->type != S3C2410_LCDCON1_TFT) { 184 /* 8 bpp 332 */ 185 var->red.length = 3; 186 var->red.offset = 5; 187 var->green.length = 3; 188 var->green.offset = 2; 189 var->blue.length = 2; 190 var->blue.offset = 0; 191 } else { 192 var->red.offset = 0; 193 var->red.length = 8; 194 var->green = var->red; 195 var->blue = var->red; 196 } 197 break; 198 case 12: 199 /* 12 bpp 444 */ 200 var->red.length = 4; 201 var->red.offset = 8; 202 var->green.length = 4; 203 var->green.offset = 4; 204 var->blue.length = 4; 205 var->blue.offset = 0; 206 break; 207 208 default: 209 case 16: 210 if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { 211 /* 16 bpp, 565 format */ 212 var->red.offset = 11; 213 var->green.offset = 5; 214 var->blue.offset = 0; 215 var->red.length = 5; 216 var->green.length = 6; 217 var->blue.length = 5; 218 } else { 219 /* 16 bpp, 5551 format */ 220 var->red.offset = 11; 221 var->green.offset = 6; 222 var->blue.offset = 1; 223 var->red.length = 5; 224 var->green.length = 5; 225 var->blue.length = 5; 226 } 227 break; 228 case 32: 229 /* 24 bpp 888 and 8 dummy */ 230 var->red.length = 8; 231 var->red.offset = 16; 232 var->green.length = 8; 233 var->green.offset = 8; 234 var->blue.length = 8; 235 var->blue.offset = 0; 236 break; 237 } 238 return 0; 239} 240 241/* s3c2410fb_calculate_stn_lcd_regs 242 * 243 * calculate register values from var settings 244 */ 245static void s3c2410fb_calculate_stn_lcd_regs(const struct fb_info *info, 246 struct s3c2410fb_hw *regs) 247{ 248 const struct s3c2410fb_info *fbi = info->par; 249 const struct fb_var_screeninfo *var = &info->var; 250 int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT; 251 int hs = var->xres >> 2; 252 unsigned wdly = (var->left_margin >> 4) - 1; 253 unsigned wlh = (var->hsync_len >> 4) - 1; 254 255 if (type != S3C2410_LCDCON1_STN4) 256 hs >>= 1; 257 258 switch (var->bits_per_pixel) { 259 case 1: 260 regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP; 261 break; 262 case 2: 263 regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY; 264 break; 265 case 4: 266 regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY; 267 break; 268 case 8: 269 regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP; 270 hs *= 3; 271 break; 272 case 12: 273 regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP; 274 hs *= 3; 275 break; 276 277 default: 278 /* invalid pixel depth */ 279 dev_err(fbi->dev, "invalid bpp %d\n", 280 var->bits_per_pixel); 281 } 282 /* update X/Y info */ 283 dprintk("setting horz: lft=%d, rt=%d, sync=%d\n", 284 var->left_margin, var->right_margin, var->hsync_len); 285 286 regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1); 287 288 if (wdly > 3) 289 wdly = 3; 290 291 if (wlh > 3) 292 wlh = 3; 293 294 regs->lcdcon3 = S3C2410_LCDCON3_WDLY(wdly) | 295 S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) | 296 S3C2410_LCDCON3_HOZVAL(hs - 1); 297 298 regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh); 299} 300 301/* s3c2410fb_calculate_tft_lcd_regs 302 * 303 * calculate register values from var settings 304 */ 305static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info, 306 struct s3c2410fb_hw *regs) 307{ 308 const struct s3c2410fb_info *fbi = info->par; 309 const struct fb_var_screeninfo *var = &info->var; 310 311 switch (var->bits_per_pixel) { 312 case 1: 313 regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP; 314 break; 315 case 2: 316 regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP; 317 break; 318 case 4: 319 regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP; 320 break; 321 case 8: 322 regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP; 323 regs->lcdcon5 |= S3C2410_LCDCON5_BSWP | 324 S3C2410_LCDCON5_FRM565; 325 regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP; 326 break; 327 case 16: 328 regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP; 329 regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP; 330 regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP; 331 break; 332 case 32: 333 regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP; 334 regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP | 335 S3C2410_LCDCON5_HWSWP | 336 S3C2410_LCDCON5_BPP24BL); 337 break; 338 default: 339 /* invalid pixel depth */ 340 dev_err(fbi->dev, "invalid bpp %d\n", 341 var->bits_per_pixel); 342 } 343 /* update X/Y info */ 344 dprintk("setting vert: up=%d, low=%d, sync=%d\n", 345 var->upper_margin, var->lower_margin, var->vsync_len); 346 347 dprintk("setting horz: lft=%d, rt=%d, sync=%d\n", 348 var->left_margin, var->right_margin, var->hsync_len); 349 350 regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) | 351 S3C2410_LCDCON2_VBPD(var->upper_margin - 1) | 352 S3C2410_LCDCON2_VFPD(var->lower_margin - 1) | 353 S3C2410_LCDCON2_VSPW(var->vsync_len - 1); 354 355 regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) | 356 S3C2410_LCDCON3_HFPD(var->left_margin - 1) | 357 S3C2410_LCDCON3_HOZVAL(var->xres - 1); 358 359 regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1); 360} 361 362/* s3c2410fb_activate_var 363 * 364 * activate (set) the controller from the given framebuffer 365 * information 366 */ 367static void s3c2410fb_activate_var(struct fb_info *info) 368{ 369 struct s3c2410fb_info *fbi = info->par; 370 void __iomem *regs = fbi->io; 371 int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; 372 struct fb_var_screeninfo *var = &info->var; 373 int clkdiv; 374 375 clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2); 376 377 dprintk("%s: var->xres = %d\n", __func__, var->xres); 378 dprintk("%s: var->yres = %d\n", __func__, var->yres); 379 dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel); 380 381 if (type == S3C2410_LCDCON1_TFT) { 382 s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs); 383 --clkdiv; 384 if (clkdiv < 0) 385 clkdiv = 0; 386 } else { 387 s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs); 388 if (clkdiv < 2) 389 clkdiv = 2; 390 } 391 392 fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv); 393 394 /* write new registers */ 395 396 dprintk("new register set:\n"); 397 dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1); 398 dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2); 399 dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3); 400 dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4); 401 dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5); 402 403 writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, 404 regs + S3C2410_LCDCON1); 405 writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2); 406 writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3); 407 writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4); 408 writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5); 409 410 /* set lcd address pointers */ 411 s3c2410fb_set_lcdaddr(info); 412 413 fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID, 414 writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); 415} 416 417/* 418 * s3c2410fb_set_par - Alters the hardware state. 419 * @info: frame buffer structure that represents a single frame buffer 420 * 421 */ 422static int s3c2410fb_set_par(struct fb_info *info) 423{ 424 struct fb_var_screeninfo *var = &info->var; 425 426 switch (var->bits_per_pixel) { 427 case 32: 428 case 16: 429 case 12: 430 info->fix.visual = FB_VISUAL_TRUECOLOR; 431 break; 432 case 1: 433 info->fix.visual = FB_VISUAL_MONO01; 434 break; 435 default: 436 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 437 break; 438 } 439 440 info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; 441 442 /* activate this new configuration */ 443 444 s3c2410fb_activate_var(info); 445 return 0; 446} 447 448static void schedule_palette_update(struct s3c2410fb_info *fbi, 449 unsigned int regno, unsigned int val) 450{ 451 unsigned long flags; 452 unsigned long irqen; 453 void __iomem *irq_base = fbi->irq_base; 454 455 local_irq_save(flags); 456 457 fbi->palette_buffer[regno] = val; 458 459 if (!fbi->palette_ready) { 460 fbi->palette_ready = 1; 461 462 /* enable IRQ */ 463 irqen = readl(irq_base + S3C24XX_LCDINTMSK); 464 irqen &= ~S3C2410_LCDINT_FRSYNC; 465 writel(irqen, irq_base + S3C24XX_LCDINTMSK); 466 } 467 468 local_irq_restore(flags); 469} 470 471/* from pxafb.c */ 472static inline unsigned int chan_to_field(unsigned int chan, 473 struct fb_bitfield *bf) 474{ 475 chan &= 0xffff; 476 chan >>= 16 - bf->length; 477 return chan << bf->offset; 478} 479 480static int s3c2410fb_setcolreg(unsigned regno, 481 unsigned red, unsigned green, unsigned blue, 482 unsigned transp, struct fb_info *info) 483{ 484 struct s3c2410fb_info *fbi = info->par; 485 void __iomem *regs = fbi->io; 486 unsigned int val; 487 488 /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", 489 regno, red, green, blue); */ 490 491 switch (info->fix.visual) { 492 case FB_VISUAL_TRUECOLOR: 493 /* true-colour, use pseudo-palette */ 494 495 if (regno < 16) { 496 u32 *pal = info->pseudo_palette; 497 498 val = chan_to_field(red, &info->var.red); 499 val |= chan_to_field(green, &info->var.green); 500 val |= chan_to_field(blue, &info->var.blue); 501 502 pal[regno] = val; 503 } 504 break; 505 506 case FB_VISUAL_PSEUDOCOLOR: 507 if (regno < 256) { 508 /* currently assume RGB 5-6-5 mode */ 509 510 val = (red >> 0) & 0xf800; 511 val |= (green >> 5) & 0x07e0; 512 val |= (blue >> 11) & 0x001f; 513 514 writel(val, regs + S3C2410_TFTPAL(regno)); 515 schedule_palette_update(fbi, regno, val); 516 } 517 518 break; 519 520 default: 521 return 1; /* unknown type */ 522 } 523 524 return 0; 525} 526 527/* s3c2410fb_lcd_enable 528 * 529 * shutdown the lcd controller 530 */ 531static void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable) 532{ 533 unsigned long flags; 534 535 local_irq_save(flags); 536 537 if (enable) 538 fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID; 539 else 540 fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID; 541 542 writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1); 543 544 local_irq_restore(flags); 545} 546 547 548/* 549 * s3c2410fb_blank 550 * @blank_mode: the blank mode we want. 551 * @info: frame buffer structure that represents a single frame buffer 552 * 553 * Blank the screen if blank_mode != 0, else unblank. Return 0 if 554 * blanking succeeded, != 0 if un-/blanking failed due to e.g. a 555 * video mode which doesn't support it. Implements VESA suspend 556 * and powerdown modes on hardware that supports disabling hsync/vsync: 557 * 558 * Returns negative errno on error, or zero on success. 559 * 560 */ 561static int s3c2410fb_blank(int blank_mode, struct fb_info *info) 562{ 563 struct s3c2410fb_info *fbi = info->par; 564 void __iomem *tpal_reg = fbi->io; 565 566 dprintk("blank(mode=%d, info=%p)\n", blank_mode, info); 567 568 tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL; 569 570 if (blank_mode == FB_BLANK_POWERDOWN) { 571 s3c2410fb_lcd_enable(fbi, 0); 572 } else { 573 s3c2410fb_lcd_enable(fbi, 1); 574 } 575 576 if (blank_mode == FB_BLANK_UNBLANK) 577 writel(0x0, tpal_reg); 578 else { 579 dprintk("setting TPAL to output 0x000000\n"); 580 writel(S3C2410_TPAL_EN, tpal_reg); 581 } 582 583 return 0; 584} 585 586static int s3c2410fb_debug_show(struct device *dev, 587 struct device_attribute *attr, char *buf) 588{ 589 return snprintf(buf, PAGE_SIZE, "%s\n", debug ? "on" : "off"); 590} 591 592static int s3c2410fb_debug_store(struct device *dev, 593 struct device_attribute *attr, 594 const char *buf, size_t len) 595{ 596 if (len < 1) 597 return -EINVAL; 598 599 if (strnicmp(buf, "on", 2) == 0 || 600 strnicmp(buf, "1", 1) == 0) { 601 debug = 1; 602 printk(KERN_DEBUG "s3c2410fb: Debug On"); 603 } else if (strnicmp(buf, "off", 3) == 0 || 604 strnicmp(buf, "0", 1) == 0) { 605 debug = 0; 606 printk(KERN_DEBUG "s3c2410fb: Debug Off"); 607 } else { 608 return -EINVAL; 609 } 610 611 return len; 612} 613 614static DEVICE_ATTR(debug, 0666, s3c2410fb_debug_show, s3c2410fb_debug_store); 615 616static struct fb_ops s3c2410fb_ops = { 617 .owner = THIS_MODULE, 618 .fb_check_var = s3c2410fb_check_var, 619 .fb_set_par = s3c2410fb_set_par, 620 .fb_blank = s3c2410fb_blank, 621 .fb_setcolreg = s3c2410fb_setcolreg, 622 .fb_fillrect = cfb_fillrect, 623 .fb_copyarea = cfb_copyarea, 624 .fb_imageblit = cfb_imageblit, 625}; 626 627/* 628 * s3c2410fb_map_video_memory(): 629 * Allocates the DRAM memory for the frame buffer. This buffer is 630 * remapped into a non-cached, non-buffered, memory region to 631 * allow palette and pixel writes to occur without flushing the 632 * cache. Once this area is remapped, all virtual memory 633 * access to the video memory should occur at the new region. 634 */ 635static int __devinit s3c2410fb_map_video_memory(struct fb_info *info) 636{ 637 struct s3c2410fb_info *fbi = info->par; 638 dma_addr_t map_dma; 639 unsigned map_size = PAGE_ALIGN(info->fix.smem_len); 640 641 dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size); 642 643 info->screen_base = dma_alloc_writecombine(fbi->dev, map_size, 644 &map_dma, GFP_KERNEL); 645 646 if (info->screen_base) { 647 /* prevent initial garbage on screen */ 648 dprintk("map_video_memory: clear %p:%08x\n", 649 info->screen_base, map_size); 650 memset(info->screen_base, 0x00, map_size); 651 652 info->fix.smem_start = map_dma; 653 654 dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n", 655 info->fix.smem_start, info->screen_base, map_size); 656 } 657 658 return info->screen_base ? 0 : -ENOMEM; 659} 660 661static inline void s3c2410fb_unmap_video_memory(struct fb_info *info) 662{ 663 struct s3c2410fb_info *fbi = info->par; 664 665 dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len), 666 info->screen_base, info->fix.smem_start); 667} 668 669static inline void modify_gpio(void __iomem *reg, 670 unsigned long set, unsigned long mask) 671{ 672 unsigned long tmp; 673 674 tmp = readl(reg) & ~mask; 675 writel(tmp | set, reg); 676} 677 678/* 679 * s3c2410fb_init_registers - Initialise all LCD-related registers 680 */ 681static int s3c2410fb_init_registers(struct fb_info *info) 682{ 683 struct s3c2410fb_info *fbi = info->par; 684 struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data; 685 unsigned long flags; 686 void __iomem *regs = fbi->io; 687 void __iomem *tpal; 688 void __iomem *lpcsel; 689 690 if (is_s3c2412(fbi)) { 691 tpal = regs + S3C2412_TPAL; 692 lpcsel = regs + S3C2412_TCONSEL; 693 } else { 694 tpal = regs + S3C2410_TPAL; 695 lpcsel = regs + S3C2410_LPCSEL; 696 } 697 698 /* Initialise LCD with values from haret */ 699 700 local_irq_save(flags); 701 702 /* modify the gpio(s) with interrupts set (bjd) */ 703 704 modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask); 705 modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask); 706 modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask); 707 modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask); 708 709 local_irq_restore(flags); 710 711 dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel); 712 writel(mach_info->lpcsel, lpcsel); 713 714 dprintk("replacing TPAL %08x\n", readl(tpal)); 715 716 /* ensure temporary palette disabled */ 717 writel(0x00, tpal); 718 719 return 0; 720} 721 722static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi) 723{ 724 unsigned int i; 725 void __iomem *regs = fbi->io; 726 727 fbi->palette_ready = 0; 728 729 for (i = 0; i < 256; i++) { 730 unsigned long ent = fbi->palette_buffer[i]; 731 if (ent == PALETTE_BUFF_CLEAR) 732 continue; 733 734 writel(ent, regs + S3C2410_TFTPAL(i)); 735 736 /* it seems the only way to know exactly 737 * if the palette wrote ok, is to check 738 * to see if the value verifies ok 739 */ 740 741 if (readw(regs + S3C2410_TFTPAL(i)) == ent) 742 fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR; 743 else 744 fbi->palette_ready = 1; /* retry */ 745 } 746} 747 748static irqreturn_t s3c2410fb_irq(int irq, void *dev_id) 749{ 750 struct s3c2410fb_info *fbi = dev_id; 751 void __iomem *irq_base = fbi->irq_base; 752 unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND); 753 754 if (lcdirq & S3C2410_LCDINT_FRSYNC) { 755 if (fbi->palette_ready) 756 s3c2410fb_write_palette(fbi); 757 758 writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND); 759 writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND); 760 } 761 762 return IRQ_HANDLED; 763} 764 765#ifdef CONFIG_CPU_FREQ 766 767static int s3c2410fb_cpufreq_transition(struct notifier_block *nb, 768 unsigned long val, void *data) 769{ 770 struct cpufreq_freqs *freqs = data; 771 struct s3c2410fb_info *info; 772 struct fb_info *fbinfo; 773 long delta_f; 774 775 info = container_of(nb, struct s3c2410fb_info, freq_transition); 776 fbinfo = platform_get_drvdata(to_platform_device(info->dev)); 777 778 /* work out change, <0 for speed-up */ 779 delta_f = info->clk_rate - clk_get_rate(info->clk); 780 781 if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) || 782 (val == CPUFREQ_PRECHANGE && delta_f < 0)) { 783 info->clk_rate = clk_get_rate(info->clk); 784 s3c2410fb_activate_var(fbinfo); 785 } 786 787 return 0; 788} 789 790static inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info) 791{ 792 info->freq_transition.notifier_call = s3c2410fb_cpufreq_transition; 793 794 return cpufreq_register_notifier(&info->freq_transition, 795 CPUFREQ_TRANSITION_NOTIFIER); 796} 797 798static inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info) 799{ 800 cpufreq_unregister_notifier(&info->freq_transition, 801 CPUFREQ_TRANSITION_NOTIFIER); 802} 803 804#else 805static inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info) 806{ 807 return 0; 808} 809 810static inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info) 811{ 812} 813#endif 814 815 816static char driver_name[] = "s3c2410fb"; 817 818static int __devinit s3c24xxfb_probe(struct platform_device *pdev, 819 enum s3c_drv_type drv_type) 820{ 821 struct s3c2410fb_info *info; 822 struct s3c2410fb_display *display; 823 struct fb_info *fbinfo; 824 struct s3c2410fb_mach_info *mach_info; 825 struct resource *res; 826 int ret; 827 int irq; 828 int i; 829 int size; 830 u32 lcdcon1; 831 832 mach_info = pdev->dev.platform_data; 833 if (mach_info == NULL) { 834 dev_err(&pdev->dev, 835 "no platform data for lcd, cannot attach\n"); 836 return -EINVAL; 837 } 838 839 if (mach_info->default_display >= mach_info->num_displays) { 840 dev_err(&pdev->dev, "default is %d but only %d displays\n", 841 mach_info->default_display, mach_info->num_displays); 842 return -EINVAL; 843 } 844 845 display = mach_info->displays + mach_info->default_display; 846 847 irq = platform_get_irq(pdev, 0); 848 if (irq < 0) { 849 dev_err(&pdev->dev, "no irq for device\n"); 850 return -ENOENT; 851 } 852 853 fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); 854 if (!fbinfo) 855 return -ENOMEM; 856 857 platform_set_drvdata(pdev, fbinfo); 858 859 info = fbinfo->par; 860 info->dev = &pdev->dev; 861 info->drv_type = drv_type; 862 863 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 864 if (res == NULL) { 865 dev_err(&pdev->dev, "failed to get memory registers\n"); 866 ret = -ENXIO; 867 goto dealloc_fb; 868 } 869 870 size = resource_size(res); 871 info->mem = request_mem_region(res->start, size, pdev->name); 872 if (info->mem == NULL) { 873 dev_err(&pdev->dev, "failed to get memory region\n"); 874 ret = -ENOENT; 875 goto dealloc_fb; 876 } 877 878 info->io = ioremap(res->start, size); 879 if (info->io == NULL) { 880 dev_err(&pdev->dev, "ioremap() of registers failed\n"); 881 ret = -ENXIO; 882 goto release_mem; 883 } 884 885 info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); 886 887 dprintk("devinit\n"); 888 889 strcpy(fbinfo->fix.id, driver_name); 890 891 /* Stop the video */ 892 lcdcon1 = readl(info->io + S3C2410_LCDCON1); 893 writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); 894 895 fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 896 fbinfo->fix.type_aux = 0; 897 fbinfo->fix.xpanstep = 0; 898 fbinfo->fix.ypanstep = 0; 899 fbinfo->fix.ywrapstep = 0; 900 fbinfo->fix.accel = FB_ACCEL_NONE; 901 902 fbinfo->var.nonstd = 0; 903 fbinfo->var.activate = FB_ACTIVATE_NOW; 904 fbinfo->var.accel_flags = 0; 905 fbinfo->var.vmode = FB_VMODE_NONINTERLACED; 906 907 fbinfo->fbops = &s3c2410fb_ops; 908 fbinfo->flags = FBINFO_FLAG_DEFAULT; 909 fbinfo->pseudo_palette = &info->pseudo_pal; 910 911 for (i = 0; i < 256; i++) 912 info->palette_buffer[i] = PALETTE_BUFF_CLEAR; 913 914 ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); 915 if (ret) { 916 dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret); 917 ret = -EBUSY; 918 goto release_regs; 919 } 920 921 info->clk = clk_get(NULL, "lcd"); 922 if (IS_ERR(info->clk)) { 923 printk(KERN_ERR "failed to get lcd clock source\n"); 924 ret = PTR_ERR(info->clk); 925 goto release_irq; 926 } 927 928 clk_enable(info->clk); 929 dprintk("got and enabled clock\n"); 930 931 msleep(1); 932 933 info->clk_rate = clk_get_rate(info->clk); 934 935 /* find maximum required memory size for display */ 936 for (i = 0; i < mach_info->num_displays; i++) { 937 unsigned long smem_len = mach_info->displays[i].xres; 938 939 smem_len *= mach_info->displays[i].yres; 940 smem_len *= mach_info->displays[i].bpp; 941 smem_len >>= 3; 942 if (fbinfo->fix.smem_len < smem_len) 943 fbinfo->fix.smem_len = smem_len; 944 } 945 946 /* Initialize video memory */ 947 ret = s3c2410fb_map_video_memory(fbinfo); 948 if (ret) { 949 printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret); 950 ret = -ENOMEM; 951 goto release_clock; 952 } 953 954 dprintk("got video memory\n"); 955 956 fbinfo->var.xres = display->xres; 957 fbinfo->var.yres = display->yres; 958 fbinfo->var.bits_per_pixel = display->bpp; 959 960 s3c2410fb_init_registers(fbinfo); 961 962 s3c2410fb_check_var(&fbinfo->var, fbinfo); 963 964 ret = s3c2410fb_cpufreq_register(info); 965 if (ret < 0) { 966 dev_err(&pdev->dev, "Failed to register cpufreq\n"); 967 goto free_video_memory; 968 } 969 970 ret = register_framebuffer(fbinfo); 971 if (ret < 0) { 972 printk(KERN_ERR "Failed to register framebuffer device: %d\n", 973 ret); 974 goto free_cpufreq; 975 } 976 977 /* create device files */ 978 ret = device_create_file(&pdev->dev, &dev_attr_debug); 979 if (ret) { 980 printk(KERN_ERR "failed to add debug attribute\n"); 981 } 982 983 printk(KERN_INFO "fb%d: %s frame buffer device\n", 984 fbinfo->node, fbinfo->fix.id); 985 986 return 0; 987 988 free_cpufreq: 989 s3c2410fb_cpufreq_deregister(info); 990free_video_memory: 991 s3c2410fb_unmap_video_memory(fbinfo); 992release_clock: 993 clk_disable(info->clk); 994 clk_put(info->clk); 995release_irq: 996 free_irq(irq, info); 997release_regs: 998 iounmap(info->io); 999release_mem: 1000 release_mem_region(res->start, size); 1001dealloc_fb: 1002 platform_set_drvdata(pdev, NULL); 1003 framebuffer_release(fbinfo); 1004 return ret; 1005} 1006 1007static int __devinit s3c2410fb_probe(struct platform_device *pdev) 1008{ 1009 return s3c24xxfb_probe(pdev, DRV_S3C2410); 1010} 1011 1012static int __devinit s3c2412fb_probe(struct platform_device *pdev) 1013{ 1014 return s3c24xxfb_probe(pdev, DRV_S3C2412); 1015} 1016 1017 1018/* 1019 * Cleanup 1020 */ 1021static int __devexit s3c2410fb_remove(struct platform_device *pdev) 1022{ 1023 struct fb_info *fbinfo = platform_get_drvdata(pdev); 1024 struct s3c2410fb_info *info = fbinfo->par; 1025 int irq; 1026 1027 unregister_framebuffer(fbinfo); 1028 s3c2410fb_cpufreq_deregister(info); 1029 1030 s3c2410fb_lcd_enable(info, 0); 1031 msleep(1); 1032 1033 s3c2410fb_unmap_video_memory(fbinfo); 1034 1035 if (info->clk) { 1036 clk_disable(info->clk); 1037 clk_put(info->clk); 1038 info->clk = NULL; 1039 } 1040 1041 irq = platform_get_irq(pdev, 0); 1042 free_irq(irq, info); 1043 1044 iounmap(info->io); 1045 1046 release_mem_region(info->mem->start, resource_size(info->mem)); 1047 1048 platform_set_drvdata(pdev, NULL); 1049 framebuffer_release(fbinfo); 1050 1051 return 0; 1052} 1053 1054#ifdef CONFIG_PM 1055 1056/* suspend and resume support for the lcd controller */ 1057static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state) 1058{ 1059 struct fb_info *fbinfo = platform_get_drvdata(dev); 1060 struct s3c2410fb_info *info = fbinfo->par; 1061 1062 s3c2410fb_lcd_enable(info, 0); 1063 1064 /* sleep before disabling the clock, we need to ensure 1065 * the LCD DMA engine is not going to get back on the bus 1066 * before the clock goes off again (bjd) */ 1067 1068 msleep(1); 1069 clk_disable(info->clk); 1070 1071 return 0; 1072} 1073 1074static int s3c2410fb_resume(struct platform_device *dev) 1075{ 1076 struct fb_info *fbinfo = platform_get_drvdata(dev); 1077 struct s3c2410fb_info *info = fbinfo->par; 1078 1079 clk_enable(info->clk); 1080 msleep(1); 1081 1082 s3c2410fb_init_registers(fbinfo); 1083 1084 /* re-activate our display after resume */ 1085 s3c2410fb_activate_var(fbinfo); 1086 s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo); 1087 1088 return 0; 1089} 1090 1091#else 1092#define s3c2410fb_suspend NULL 1093#define s3c2410fb_resume NULL 1094#endif 1095 1096static struct platform_driver s3c2410fb_driver = { 1097 .probe = s3c2410fb_probe, 1098 .remove = __devexit_p(s3c2410fb_remove), 1099 .suspend = s3c2410fb_suspend, 1100 .resume = s3c2410fb_resume, 1101 .driver = { 1102 .name = "s3c2410-lcd", 1103 .owner = THIS_MODULE, 1104 }, 1105}; 1106 1107static struct platform_driver s3c2412fb_driver = { 1108 .probe = s3c2412fb_probe, 1109 .remove = __devexit_p(s3c2410fb_remove), 1110 .suspend = s3c2410fb_suspend, 1111 .resume = s3c2410fb_resume, 1112 .driver = { 1113 .name = "s3c2412-lcd", 1114 .owner = THIS_MODULE, 1115 }, 1116}; 1117 1118int __init s3c2410fb_init(void) 1119{ 1120 int ret = platform_driver_register(&s3c2410fb_driver); 1121 1122 if (ret == 0) 1123 ret = platform_driver_register(&s3c2412fb_driver); 1124 1125 return ret; 1126} 1127 1128static void __exit s3c2410fb_cleanup(void) 1129{ 1130 platform_driver_unregister(&s3c2410fb_driver); 1131 platform_driver_unregister(&s3c2412fb_driver); 1132} 1133 1134module_init(s3c2410fb_init); 1135module_exit(s3c2410fb_cleanup); 1136 1137MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, " 1138 "Ben Dooks <ben-linux@fluff.org>"); 1139MODULE_DESCRIPTION("Framebuffer driver for the s3c2410"); 1140MODULE_LICENSE("GPL"); 1141MODULE_ALIAS("platform:s3c2410-lcd"); 1142MODULE_ALIAS("platform:s3c2412-lcd");