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 v2.6.28-rc1 659 lines 16 kB view raw
1/* 2 * SH7760/SH7763 LCDC Framebuffer driver. 3 * 4 * (c) 2006-2008 MSC Vertriebsges.m.b.H., 5 * Manuel Lauss <mano@roarinelk.homelinux.net> 6 * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> 7 * 8 * This file is subject to the terms and conditions of the GNU General 9 * Public License. See the file COPYING in the main directory of this 10 * archive for more details. 11 * 12 * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.txt! 13 * 14 * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de> 15 * for his original source and testing! 16 */ 17 18#include <linux/completion.h> 19#include <linux/delay.h> 20#include <linux/dma-mapping.h> 21#include <linux/fb.h> 22#include <linux/interrupt.h> 23#include <linux/io.h> 24#include <linux/kernel.h> 25#include <linux/module.h> 26#include <linux/platform_device.h> 27 28#include <asm/sh7760fb.h> 29 30struct sh7760fb_par { 31 void __iomem *base; 32 int irq; 33 34 struct sh7760fb_platdata *pd; /* display information */ 35 36 dma_addr_t fbdma; /* physical address */ 37 38 int rot; /* rotation enabled? */ 39 40 u32 pseudo_palette[16]; 41 42 struct platform_device *dev; 43 struct resource *ioarea; 44 struct completion vsync; /* vsync irq event */ 45}; 46 47static irqreturn_t sh7760fb_irq(int irq, void *data) 48{ 49 struct completion *c = data; 50 51 complete(c); 52 53 return IRQ_HANDLED; 54} 55 56static void sh7760fb_wait_vsync(struct fb_info *info) 57{ 58 struct sh7760fb_par *par = info->par; 59 60 if (par->pd->novsync) 61 return; 62 63 iowrite16(ioread16(par->base + LDINTR) & ~VINT_CHECK, 64 par->base + LDINTR); 65 66 if (par->irq < 0) { 67 /* poll for vert. retrace: status bit is sticky */ 68 while (!(ioread16(par->base + LDINTR) & VINT_CHECK)) 69 cpu_relax(); 70 } else { 71 /* a "wait_for_irq_event(par->irq)" would be extremely nice */ 72 init_completion(&par->vsync); 73 enable_irq(par->irq); 74 wait_for_completion(&par->vsync); 75 disable_irq_nosync(par->irq); 76 } 77} 78 79/* wait_for_lps - wait until power supply has reached a certain state. */ 80static int wait_for_lps(struct sh7760fb_par *par, int val) 81{ 82 int i = 100; 83 while (--i && ((ioread16(par->base + LDPMMR) & 3) != val)) 84 msleep(1); 85 86 if (i <= 0) 87 return -ETIMEDOUT; 88 89 return 0; 90} 91 92/* en/disable the LCDC */ 93static int sh7760fb_blank(int blank, struct fb_info *info) 94{ 95 struct sh7760fb_par *par = info->par; 96 struct sh7760fb_platdata *pd = par->pd; 97 unsigned short cntr = ioread16(par->base + LDCNTR); 98 unsigned short intr = ioread16(par->base + LDINTR); 99 int lps; 100 101 if (blank == FB_BLANK_UNBLANK) { 102 intr |= VINT_START; 103 cntr = LDCNTR_DON2 | LDCNTR_DON; 104 lps = 3; 105 } else { 106 intr &= ~VINT_START; 107 cntr = LDCNTR_DON2; 108 lps = 0; 109 } 110 111 if (pd->blank) 112 pd->blank(blank); 113 114 iowrite16(intr, par->base + LDINTR); 115 iowrite16(cntr, par->base + LDCNTR); 116 117 return wait_for_lps(par, lps); 118} 119 120/* set color registers */ 121static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info) 122{ 123 struct sh7760fb_par *par = info->par; 124 u32 s = cmap->start; 125 u32 l = cmap->len; 126 u16 *r = cmap->red; 127 u16 *g = cmap->green; 128 u16 *b = cmap->blue; 129 u32 col, tmo; 130 int ret; 131 132 ret = 0; 133 134 sh7760fb_wait_vsync(info); 135 136 /* request palette access */ 137 iowrite16(LDPALCR_PALEN, par->base + LDPALCR); 138 139 /* poll for access grant */ 140 tmo = 100; 141 while (!(ioread16(par->base + LDPALCR) & LDPALCR_PALS) && (--tmo)) 142 cpu_relax(); 143 144 if (!tmo) { 145 ret = 1; 146 dev_dbg(info->dev, "no palette access!\n"); 147 goto out; 148 } 149 150 while (l && (s < 256)) { 151 col = ((*r) & 0xff) << 16; 152 col |= ((*g) & 0xff) << 8; 153 col |= ((*b) & 0xff); 154 col &= SH7760FB_PALETTE_MASK; 155 iowrite32(col, par->base + LDPR(s)); 156 157 if (s < 16) 158 ((u32 *) (info->pseudo_palette))[s] = s; 159 160 s++; 161 l--; 162 r++; 163 g++; 164 b++; 165 } 166out: 167 iowrite16(0, par->base + LDPALCR); 168 return ret; 169} 170 171static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info, 172 unsigned long stride) 173{ 174 memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 175 strcpy(fix->id, "sh7760-lcdc"); 176 177 fix->smem_start = (unsigned long)info->screen_base; 178 fix->smem_len = info->screen_size; 179 180 fix->line_length = stride; 181} 182 183static int sh7760fb_get_color_info(struct device *dev, 184 u16 lddfr, int *bpp, int *gray) 185{ 186 int lbpp, lgray; 187 188 lgray = lbpp = 0; 189 190 switch (lddfr & LDDFR_COLOR_MASK) { 191 case LDDFR_1BPP_MONO: 192 lgray = 1; 193 lbpp = 1; 194 break; 195 case LDDFR_2BPP_MONO: 196 lgray = 1; 197 lbpp = 2; 198 break; 199 case LDDFR_4BPP_MONO: 200 lgray = 1; 201 case LDDFR_4BPP: 202 lbpp = 4; 203 break; 204 case LDDFR_6BPP_MONO: 205 lgray = 1; 206 case LDDFR_8BPP: 207 lbpp = 8; 208 break; 209 case LDDFR_16BPP_RGB555: 210 case LDDFR_16BPP_RGB565: 211 lbpp = 16; 212 lgray = 0; 213 break; 214 default: 215 dev_dbg(dev, "unsupported LDDFR bit depth.\n"); 216 return -EINVAL; 217 } 218 219 if (bpp) 220 *bpp = lbpp; 221 if (gray) 222 *gray = lgray; 223 224 return 0; 225} 226 227static int sh7760fb_check_var(struct fb_var_screeninfo *var, 228 struct fb_info *info) 229{ 230 struct fb_fix_screeninfo *fix = &info->fix; 231 struct sh7760fb_par *par = info->par; 232 int ret, bpp; 233 234 /* get color info from register value */ 235 ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); 236 if (ret) 237 return ret; 238 239 var->bits_per_pixel = bpp; 240 241 if ((var->grayscale) && (var->bits_per_pixel == 1)) 242 fix->visual = FB_VISUAL_MONO10; 243 else if (var->bits_per_pixel >= 15) 244 fix->visual = FB_VISUAL_TRUECOLOR; 245 else 246 fix->visual = FB_VISUAL_PSEUDOCOLOR; 247 248 /* TODO: add some more validation here */ 249 return 0; 250} 251 252/* 253 * sh7760fb_set_par - set videomode. 254 * 255 * NOTE: The rotation, grayscale and DSTN codepaths are 256 * totally untested! 257 */ 258static int sh7760fb_set_par(struct fb_info *info) 259{ 260 struct sh7760fb_par *par = info->par; 261 struct fb_videomode *vm = par->pd->def_mode; 262 unsigned long sbase, dstn_off, ldsarl, stride; 263 unsigned short hsynp, hsynw, htcn, hdcn; 264 unsigned short vsynp, vsynw, vtln, vdln; 265 unsigned short lddfr, ldmtr; 266 int ret, bpp, gray; 267 268 par->rot = par->pd->rotate; 269 270 /* rotate only works with xres <= 320 */ 271 if (par->rot && (vm->xres > 320)) { 272 dev_dbg(info->dev, "rotation disabled due to display size\n"); 273 par->rot = 0; 274 } 275 276 /* calculate LCDC reg vals from display parameters */ 277 hsynp = vm->right_margin + vm->xres; 278 hsynw = vm->hsync_len; 279 htcn = vm->left_margin + hsynp + hsynw; 280 hdcn = vm->xres; 281 vsynp = vm->lower_margin + vm->yres; 282 vsynw = vm->vsync_len; 283 vtln = vm->upper_margin + vsynp + vsynw; 284 vdln = vm->yres; 285 286 /* get color info from register value */ 287 ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray); 288 if (ret) 289 return ret; 290 291 dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn, 292 vdln, bpp, gray ? "grayscale" : "color", 293 par->rot ? "rotated" : "normal"); 294 295#ifdef CONFIG_CPU_LITTLE_ENDIAN 296 lddfr = par->pd->lddfr | (1 << 8); 297#else 298 lddfr = par->pd->lddfr & ~(1 << 8); 299#endif 300 301 ldmtr = par->pd->ldmtr; 302 303 if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT)) 304 ldmtr |= LDMTR_CL1POL; 305 if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT)) 306 ldmtr |= LDMTR_FLMPOL; 307 308 /* shut down LCDC before changing display parameters */ 309 sh7760fb_blank(FB_BLANK_POWERDOWN, info); 310 311 iowrite16(par->pd->ldickr, par->base + LDICKR); /* pixclock */ 312 iowrite16(ldmtr, par->base + LDMTR); /* polarities */ 313 iowrite16(lddfr, par->base + LDDFR); /* color/depth */ 314 iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR); /* rotate */ 315 iowrite16(par->pd->ldpmmr, par->base + LDPMMR); /* Power Management */ 316 iowrite16(par->pd->ldpspr, par->base + LDPSPR); /* Power Supply Ctrl */ 317 318 /* display resolution */ 319 iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8), 320 par->base + LDHCNR); 321 iowrite16(vdln - 1, par->base + LDVDLNR); 322 iowrite16(vtln - 1, par->base + LDVTLNR); 323 /* h/v sync signals */ 324 iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR); 325 iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12), 326 par->base + LDHSYNR); 327 /* AC modulation sig */ 328 iowrite16(par->pd->ldaclnr, par->base + LDACLNR); 329 330 stride = (par->rot) ? vtln : hdcn; 331 if (!gray) 332 stride *= (bpp + 7) >> 3; 333 else { 334 if (bpp == 1) 335 stride >>= 3; 336 else if (bpp == 2) 337 stride >>= 2; 338 else if (bpp == 4) 339 stride >>= 1; 340 /* 6 bpp == 8 bpp */ 341 } 342 343 /* if rotated, stride must be power of 2 */ 344 if (par->rot) { 345 unsigned long bit = 1 << 31; 346 while (bit) { 347 if (stride & bit) 348 break; 349 bit >>= 1; 350 } 351 if (stride & ~bit) 352 stride = bit << 1; /* not P-o-2, round up */ 353 } 354 iowrite16(stride, par->base + LDLAOR); 355 356 /* set display mem start address */ 357 sbase = (unsigned long)par->fbdma; 358 if (par->rot) 359 sbase += (hdcn - 1) * stride; 360 361 iowrite32(sbase, par->base + LDSARU); 362 363 /* 364 * for DSTN need to set address for lower half. 365 * I (mlau) don't know which address to set it to, 366 * so I guessed at (stride * yres/2). 367 */ 368 if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) && 369 ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) { 370 371 dev_dbg(info->dev, " ***** DSTN untested! *****\n"); 372 373 dstn_off = stride; 374 if (par->rot) 375 dstn_off *= hdcn >> 1; 376 else 377 dstn_off *= vdln >> 1; 378 379 ldsarl = sbase + dstn_off; 380 } else 381 ldsarl = 0; 382 383 iowrite32(ldsarl, par->base + LDSARL); /* mem for lower half of DSTN */ 384 385 encode_fix(&info->fix, info, stride); 386 sh7760fb_check_var(&info->var, info); 387 388 sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */ 389 390 dev_dbg(info->dev, "hdcn : %6d htcn : %6d\n", hdcn, htcn); 391 dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp); 392 dev_dbg(info->dev, "vdln : %6d vtln : %6d\n", vdln, vtln); 393 dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp); 394 dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n", 395 (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f); 396 dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr, 397 par->pd->ldpspr); 398 dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr); 399 dev_dbg(info->dev, "ldlaor: %ld\n", stride); 400 dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl); 401 402 return 0; 403} 404 405static struct fb_ops sh7760fb_ops = { 406 .owner = THIS_MODULE, 407 .fb_blank = sh7760fb_blank, 408 .fb_check_var = sh7760fb_check_var, 409 .fb_setcmap = sh7760fb_setcmap, 410 .fb_set_par = sh7760fb_set_par, 411 .fb_fillrect = cfb_fillrect, 412 .fb_copyarea = cfb_copyarea, 413 .fb_imageblit = cfb_imageblit, 414}; 415 416static void sh7760fb_free_mem(struct fb_info *info) 417{ 418 struct sh7760fb_par *par = info->par; 419 420 if (!info->screen_base) 421 return; 422 423 dma_free_coherent(info->dev, info->screen_size, 424 info->screen_base, par->fbdma); 425 426 par->fbdma = 0; 427 info->screen_base = NULL; 428 info->screen_size = 0; 429} 430 431/* allocate the framebuffer memory. This memory must be in Area3, 432 * (dictated by the DMA engine) and contiguous, at a 512 byte boundary. 433 */ 434static int sh7760fb_alloc_mem(struct fb_info *info) 435{ 436 struct sh7760fb_par *par = info->par; 437 void *fbmem; 438 unsigned long vram; 439 int ret, bpp; 440 441 if (info->screen_base) 442 return 0; 443 444 /* get color info from register value */ 445 ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); 446 if (ret) { 447 printk(KERN_ERR "colinfo\n"); 448 return ret; 449 } 450 451 /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page 452 max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */ 453 454 vram = info->var.xres * info->var.yres; 455 if (info->var.grayscale) { 456 if (bpp == 1) 457 vram >>= 3; 458 else if (bpp == 2) 459 vram >>= 2; 460 else if (bpp == 4) 461 vram >>= 1; 462 } else if (bpp > 8) 463 vram *= 2; 464 if ((vram < 1) || (vram > 1024 * 2048)) { 465 dev_dbg(info->dev, "too much VRAM required. Check settings\n"); 466 return -ENODEV; 467 } 468 469 if (vram < PAGE_SIZE) 470 vram = PAGE_SIZE; 471 472 fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL); 473 474 if (!fbmem) 475 return -ENOMEM; 476 477 if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) { 478 sh7760fb_free_mem(info); 479 dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is" 480 "unusable for the LCDC\n", (unsigned long)par->fbdma); 481 return -ENOMEM; 482 } 483 484 info->screen_base = fbmem; 485 info->screen_size = vram; 486 487 return 0; 488} 489 490static int __devinit sh7760fb_probe(struct platform_device *pdev) 491{ 492 struct fb_info *info; 493 struct resource *res; 494 struct sh7760fb_par *par; 495 int ret; 496 497 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 498 if (unlikely(res == NULL)) { 499 dev_err(&pdev->dev, "invalid resource\n"); 500 return -EINVAL; 501 } 502 503 info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev); 504 if (!info) 505 return -ENOMEM; 506 507 par = info->par; 508 par->dev = pdev; 509 510 par->pd = pdev->dev.platform_data; 511 if (!par->pd) { 512 dev_dbg(info->dev, "no display setup data!\n"); 513 ret = -ENODEV; 514 goto out_fb; 515 } 516 517 par->ioarea = request_mem_region(res->start, 518 (res->end - res->start), pdev->name); 519 if (!par->ioarea) { 520 dev_err(&pdev->dev, "mmio area busy\n"); 521 ret = -EBUSY; 522 goto out_fb; 523 } 524 525 par->base = ioremap_nocache(res->start, res->end - res->start + 1); 526 if (!par->base) { 527 dev_err(&pdev->dev, "cannot remap\n"); 528 ret = -ENODEV; 529 goto out_res; 530 } 531 532 iowrite16(0, par->base + LDINTR); /* disable vsync irq */ 533 par->irq = platform_get_irq(pdev, 0); 534 if (par->irq >= 0) { 535 ret = request_irq(par->irq, sh7760fb_irq, 0, 536 "sh7760-lcdc", &par->vsync); 537 if (ret) { 538 dev_err(&pdev->dev, "cannot grab IRQ\n"); 539 par->irq = -ENXIO; 540 } else 541 disable_irq_nosync(par->irq); 542 } 543 544 fb_videomode_to_var(&info->var, par->pd->def_mode); 545 546 ret = sh7760fb_alloc_mem(info); 547 if (ret) { 548 dev_dbg(info->dev, "framebuffer memory allocation failed!\n"); 549 goto out_unmap; 550 } 551 552 info->pseudo_palette = par->pseudo_palette; 553 554 /* fixup color register bitpositions. These are fixed by hardware */ 555 info->var.red.offset = 11; 556 info->var.red.length = 5; 557 info->var.red.msb_right = 0; 558 559 info->var.green.offset = 5; 560 info->var.green.length = 6; 561 info->var.green.msb_right = 0; 562 563 info->var.blue.offset = 0; 564 info->var.blue.length = 5; 565 info->var.blue.msb_right = 0; 566 567 info->var.transp.offset = 0; 568 info->var.transp.length = 0; 569 info->var.transp.msb_right = 0; 570 571 /* set the DON2 bit now, before cmap allocation, as it will randomize 572 * palette memory. 573 */ 574 iowrite16(LDCNTR_DON2, par->base + LDCNTR); 575 info->fbops = &sh7760fb_ops; 576 577 ret = fb_alloc_cmap(&info->cmap, 256, 0); 578 if (ret) { 579 dev_dbg(info->dev, "Unable to allocate cmap memory\n"); 580 goto out_mem; 581 } 582 583 ret = register_framebuffer(info); 584 if (ret < 0) { 585 dev_dbg(info->dev, "cannot register fb!\n"); 586 goto out_cmap; 587 } 588 platform_set_drvdata(pdev, info); 589 590 printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n", 591 pdev->name, 592 (unsigned long)par->fbdma, 593 (unsigned long)(par->fbdma + info->screen_size - 1), 594 info->screen_size >> 10); 595 596 return 0; 597 598out_cmap: 599 sh7760fb_blank(FB_BLANK_POWERDOWN, info); 600 fb_dealloc_cmap(&info->cmap); 601out_mem: 602 sh7760fb_free_mem(info); 603out_unmap: 604 if (par->irq >= 0) 605 free_irq(par->irq, &par->vsync); 606 iounmap(par->base); 607out_res: 608 release_resource(par->ioarea); 609 kfree(par->ioarea); 610out_fb: 611 framebuffer_release(info); 612 return ret; 613} 614 615static int __devexit sh7760fb_remove(struct platform_device *dev) 616{ 617 struct fb_info *info = platform_get_drvdata(dev); 618 struct sh7760fb_par *par = info->par; 619 620 sh7760fb_blank(FB_BLANK_POWERDOWN, info); 621 unregister_framebuffer(info); 622 fb_dealloc_cmap(&info->cmap); 623 sh7760fb_free_mem(info); 624 if (par->irq >= 0) 625 free_irq(par->irq, par); 626 iounmap(par->base); 627 release_resource(par->ioarea); 628 kfree(par->ioarea); 629 framebuffer_release(info); 630 platform_set_drvdata(dev, NULL); 631 632 return 0; 633} 634 635static struct platform_driver sh7760_lcdc_driver = { 636 .driver = { 637 .name = "sh7760-lcdc", 638 .owner = THIS_MODULE, 639 }, 640 .probe = sh7760fb_probe, 641 .remove = __devexit_p(sh7760fb_remove), 642}; 643 644static int __init sh7760fb_init(void) 645{ 646 return platform_driver_register(&sh7760_lcdc_driver); 647} 648 649static void __exit sh7760fb_exit(void) 650{ 651 platform_driver_unregister(&sh7760_lcdc_driver); 652} 653 654module_init(sh7760fb_init); 655module_exit(sh7760fb_exit); 656 657MODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss"); 658MODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller"); 659MODULE_LICENSE("GPL");