Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.5-rc1 653 lines 15 kB view raw
1/* 2 * linux/drivers/video/amba-clcd.c 3 * 4 * Copyright (C) 2001 ARM Limited, by David A Rusling 5 * Updated to 2.5, Deep Blue Solutions Ltd. 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 9 * for more details. 10 * 11 * ARM PrimeCell PL110 Color LCD Controller 12 */ 13#include <linux/module.h> 14#include <linux/kernel.h> 15#include <linux/errno.h> 16#include <linux/string.h> 17#include <linux/slab.h> 18#include <linux/delay.h> 19#include <linux/mm.h> 20#include <linux/fb.h> 21#include <linux/init.h> 22#include <linux/ioport.h> 23#include <linux/list.h> 24#include <linux/amba/bus.h> 25#include <linux/amba/clcd.h> 26#include <linux/clk.h> 27#include <linux/hardirq.h> 28 29#include <asm/sizes.h> 30 31#define to_clcd(info) container_of(info, struct clcd_fb, fb) 32 33/* This is limited to 16 characters when displayed by X startup */ 34static const char *clcd_name = "CLCD FB"; 35 36/* 37 * Unfortunately, the enable/disable functions may be called either from 38 * process or IRQ context, and we _need_ to delay. This is _not_ good. 39 */ 40static inline void clcdfb_sleep(unsigned int ms) 41{ 42 if (in_atomic()) { 43 mdelay(ms); 44 } else { 45 msleep(ms); 46 } 47} 48 49static inline void clcdfb_set_start(struct clcd_fb *fb) 50{ 51 unsigned long ustart = fb->fb.fix.smem_start; 52 unsigned long lstart; 53 54 ustart += fb->fb.var.yoffset * fb->fb.fix.line_length; 55 lstart = ustart + fb->fb.var.yres * fb->fb.fix.line_length / 2; 56 57 writel(ustart, fb->regs + CLCD_UBAS); 58 writel(lstart, fb->regs + CLCD_LBAS); 59} 60 61static void clcdfb_disable(struct clcd_fb *fb) 62{ 63 u32 val; 64 65 if (fb->board->disable) 66 fb->board->disable(fb); 67 68 val = readl(fb->regs + fb->off_cntl); 69 if (val & CNTL_LCDPWR) { 70 val &= ~CNTL_LCDPWR; 71 writel(val, fb->regs + fb->off_cntl); 72 73 clcdfb_sleep(20); 74 } 75 if (val & CNTL_LCDEN) { 76 val &= ~CNTL_LCDEN; 77 writel(val, fb->regs + fb->off_cntl); 78 } 79 80 /* 81 * Disable CLCD clock source. 82 */ 83 if (fb->clk_enabled) { 84 fb->clk_enabled = false; 85 clk_disable(fb->clk); 86 } 87} 88 89static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) 90{ 91 /* 92 * Enable the CLCD clock source. 93 */ 94 if (!fb->clk_enabled) { 95 fb->clk_enabled = true; 96 clk_enable(fb->clk); 97 } 98 99 /* 100 * Bring up by first enabling.. 101 */ 102 cntl |= CNTL_LCDEN; 103 writel(cntl, fb->regs + fb->off_cntl); 104 105 clcdfb_sleep(20); 106 107 /* 108 * and now apply power. 109 */ 110 cntl |= CNTL_LCDPWR; 111 writel(cntl, fb->regs + fb->off_cntl); 112 113 /* 114 * finally, enable the interface. 115 */ 116 if (fb->board->enable) 117 fb->board->enable(fb); 118} 119 120static int 121clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) 122{ 123 u32 caps; 124 int ret = 0; 125 126 if (fb->panel->caps && fb->board->caps) 127 caps = fb->panel->caps & fb->board->caps; 128 else { 129 /* Old way of specifying what can be used */ 130 caps = fb->panel->cntl & CNTL_BGR ? 131 CLCD_CAP_BGR : CLCD_CAP_RGB; 132 /* But mask out 444 modes as they weren't supported */ 133 caps &= ~CLCD_CAP_444; 134 } 135 136 /* Only TFT panels can do RGB888/BGR888 */ 137 if (!(fb->panel->cntl & CNTL_LCDTFT)) 138 caps &= ~CLCD_CAP_888; 139 140 memset(&var->transp, 0, sizeof(var->transp)); 141 142 var->red.msb_right = 0; 143 var->green.msb_right = 0; 144 var->blue.msb_right = 0; 145 146 switch (var->bits_per_pixel) { 147 case 1: 148 case 2: 149 case 4: 150 case 8: 151 /* If we can't do 5551, reject */ 152 caps &= CLCD_CAP_5551; 153 if (!caps) { 154 ret = -EINVAL; 155 break; 156 } 157 158 var->red.length = var->bits_per_pixel; 159 var->red.offset = 0; 160 var->green.length = var->bits_per_pixel; 161 var->green.offset = 0; 162 var->blue.length = var->bits_per_pixel; 163 var->blue.offset = 0; 164 break; 165 166 case 16: 167 /* If we can't do 444, 5551 or 565, reject */ 168 if (!(caps & (CLCD_CAP_444 | CLCD_CAP_5551 | CLCD_CAP_565))) { 169 ret = -EINVAL; 170 break; 171 } 172 173 /* 174 * Green length can be 4, 5 or 6 depending whether 175 * we're operating in 444, 5551 or 565 mode. 176 */ 177 if (var->green.length == 4 && caps & CLCD_CAP_444) 178 caps &= CLCD_CAP_444; 179 if (var->green.length == 5 && caps & CLCD_CAP_5551) 180 caps &= CLCD_CAP_5551; 181 else if (var->green.length == 6 && caps & CLCD_CAP_565) 182 caps &= CLCD_CAP_565; 183 else { 184 /* 185 * PL110 officially only supports RGB555, 186 * but may be wired up to allow RGB565. 187 */ 188 if (caps & CLCD_CAP_565) { 189 var->green.length = 6; 190 caps &= CLCD_CAP_565; 191 } else if (caps & CLCD_CAP_5551) { 192 var->green.length = 5; 193 caps &= CLCD_CAP_5551; 194 } else { 195 var->green.length = 4; 196 caps &= CLCD_CAP_444; 197 } 198 } 199 200 if (var->green.length >= 5) { 201 var->red.length = 5; 202 var->blue.length = 5; 203 } else { 204 var->red.length = 4; 205 var->blue.length = 4; 206 } 207 break; 208 case 32: 209 /* If we can't do 888, reject */ 210 caps &= CLCD_CAP_888; 211 if (!caps) { 212 ret = -EINVAL; 213 break; 214 } 215 216 var->red.length = 8; 217 var->green.length = 8; 218 var->blue.length = 8; 219 break; 220 default: 221 ret = -EINVAL; 222 break; 223 } 224 225 /* 226 * >= 16bpp displays have separate colour component bitfields 227 * encoded in the pixel data. Calculate their position from 228 * the bitfield length defined above. 229 */ 230 if (ret == 0 && var->bits_per_pixel >= 16) { 231 bool bgr, rgb; 232 233 bgr = caps & CLCD_CAP_BGR && var->blue.offset == 0; 234 rgb = caps & CLCD_CAP_RGB && var->red.offset == 0; 235 236 if (!bgr && !rgb) 237 /* 238 * The requested format was not possible, try just 239 * our capabilities. One of BGR or RGB must be 240 * supported. 241 */ 242 bgr = caps & CLCD_CAP_BGR; 243 244 if (bgr) { 245 var->blue.offset = 0; 246 var->green.offset = var->blue.offset + var->blue.length; 247 var->red.offset = var->green.offset + var->green.length; 248 } else { 249 var->red.offset = 0; 250 var->green.offset = var->red.offset + var->red.length; 251 var->blue.offset = var->green.offset + var->green.length; 252 } 253 } 254 255 return ret; 256} 257 258static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 259{ 260 struct clcd_fb *fb = to_clcd(info); 261 int ret = -EINVAL; 262 263 if (fb->board->check) 264 ret = fb->board->check(fb, var); 265 266 if (ret == 0 && 267 var->xres_virtual * var->bits_per_pixel / 8 * 268 var->yres_virtual > fb->fb.fix.smem_len) 269 ret = -EINVAL; 270 271 if (ret == 0) 272 ret = clcdfb_set_bitfields(fb, var); 273 274 return ret; 275} 276 277static int clcdfb_set_par(struct fb_info *info) 278{ 279 struct clcd_fb *fb = to_clcd(info); 280 struct clcd_regs regs; 281 282 fb->fb.fix.line_length = fb->fb.var.xres_virtual * 283 fb->fb.var.bits_per_pixel / 8; 284 285 if (fb->fb.var.bits_per_pixel <= 8) 286 fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; 287 else 288 fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; 289 290 fb->board->decode(fb, &regs); 291 292 clcdfb_disable(fb); 293 294 writel(regs.tim0, fb->regs + CLCD_TIM0); 295 writel(regs.tim1, fb->regs + CLCD_TIM1); 296 writel(regs.tim2, fb->regs + CLCD_TIM2); 297 writel(regs.tim3, fb->regs + CLCD_TIM3); 298 299 clcdfb_set_start(fb); 300 301 clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000); 302 303 fb->clcd_cntl = regs.cntl; 304 305 clcdfb_enable(fb, regs.cntl); 306 307#ifdef DEBUG 308 printk(KERN_INFO 309 "CLCD: Registers set to\n" 310 " %08x %08x %08x %08x\n" 311 " %08x %08x %08x %08x\n", 312 readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1), 313 readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3), 314 readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS), 315 readl(fb->regs + fb->off_ienb), readl(fb->regs + fb->off_cntl)); 316#endif 317 318 return 0; 319} 320 321static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) 322{ 323 unsigned int mask = (1 << bf->length) - 1; 324 325 return (val >> (16 - bf->length) & mask) << bf->offset; 326} 327 328/* 329 * Set a single color register. The values supplied have a 16 bit 330 * magnitude. Return != 0 for invalid regno. 331 */ 332static int 333clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, 334 unsigned int blue, unsigned int transp, struct fb_info *info) 335{ 336 struct clcd_fb *fb = to_clcd(info); 337 338 if (regno < 16) 339 fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | 340 convert_bitfield(blue, &fb->fb.var.blue) | 341 convert_bitfield(green, &fb->fb.var.green) | 342 convert_bitfield(red, &fb->fb.var.red); 343 344 if (fb->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { 345 int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3); 346 u32 val, mask, newval; 347 348 newval = (red >> 11) & 0x001f; 349 newval |= (green >> 6) & 0x03e0; 350 newval |= (blue >> 1) & 0x7c00; 351 352 /* 353 * 3.2.11: if we're configured for big endian 354 * byte order, the palette entries are swapped. 355 */ 356 if (fb->clcd_cntl & CNTL_BEBO) 357 regno ^= 1; 358 359 if (regno & 1) { 360 newval <<= 16; 361 mask = 0x0000ffff; 362 } else { 363 mask = 0xffff0000; 364 } 365 366 val = readl(fb->regs + hw_reg) & mask; 367 writel(val | newval, fb->regs + hw_reg); 368 } 369 370 return regno > 255; 371} 372 373/* 374 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL 375 * then the caller blanks by setting the CLUT (Color Look Up Table) to all 376 * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due 377 * to e.g. a video mode which doesn't support it. Implements VESA suspend 378 * and powerdown modes on hardware that supports disabling hsync/vsync: 379 * blank_mode == 2: suspend vsync 380 * blank_mode == 3: suspend hsync 381 * blank_mode == 4: powerdown 382 */ 383static int clcdfb_blank(int blank_mode, struct fb_info *info) 384{ 385 struct clcd_fb *fb = to_clcd(info); 386 387 if (blank_mode != 0) { 388 clcdfb_disable(fb); 389 } else { 390 clcdfb_enable(fb, fb->clcd_cntl); 391 } 392 return 0; 393} 394 395static int clcdfb_mmap(struct fb_info *info, 396 struct vm_area_struct *vma) 397{ 398 struct clcd_fb *fb = to_clcd(info); 399 unsigned long len, off = vma->vm_pgoff << PAGE_SHIFT; 400 int ret = -EINVAL; 401 402 len = info->fix.smem_len; 403 404 if (off <= len && vma->vm_end - vma->vm_start <= len - off && 405 fb->board->mmap) 406 ret = fb->board->mmap(fb, vma); 407 408 return ret; 409} 410 411static struct fb_ops clcdfb_ops = { 412 .owner = THIS_MODULE, 413 .fb_check_var = clcdfb_check_var, 414 .fb_set_par = clcdfb_set_par, 415 .fb_setcolreg = clcdfb_setcolreg, 416 .fb_blank = clcdfb_blank, 417 .fb_fillrect = cfb_fillrect, 418 .fb_copyarea = cfb_copyarea, 419 .fb_imageblit = cfb_imageblit, 420 .fb_mmap = clcdfb_mmap, 421}; 422 423static int clcdfb_register(struct clcd_fb *fb) 424{ 425 int ret; 426 427 /* 428 * ARM PL111 always has IENB at 0x1c; it's only PL110 429 * which is reversed on some platforms. 430 */ 431 if (amba_manf(fb->dev) == 0x41 && amba_part(fb->dev) == 0x111) { 432 fb->off_ienb = CLCD_PL111_IENB; 433 fb->off_cntl = CLCD_PL111_CNTL; 434 } else { 435#ifdef CONFIG_ARCH_VERSATILE 436 fb->off_ienb = CLCD_PL111_IENB; 437 fb->off_cntl = CLCD_PL111_CNTL; 438#else 439 fb->off_ienb = CLCD_PL110_IENB; 440 fb->off_cntl = CLCD_PL110_CNTL; 441#endif 442 } 443 444 fb->clk = clk_get(&fb->dev->dev, NULL); 445 if (IS_ERR(fb->clk)) { 446 ret = PTR_ERR(fb->clk); 447 goto out; 448 } 449 450 ret = clk_prepare(fb->clk); 451 if (ret) 452 goto free_clk; 453 454 fb->fb.device = &fb->dev->dev; 455 456 fb->fb.fix.mmio_start = fb->dev->res.start; 457 fb->fb.fix.mmio_len = resource_size(&fb->dev->res); 458 459 fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); 460 if (!fb->regs) { 461 printk(KERN_ERR "CLCD: unable to remap registers\n"); 462 ret = -ENOMEM; 463 goto clk_unprep; 464 } 465 466 fb->fb.fbops = &clcdfb_ops; 467 fb->fb.flags = FBINFO_FLAG_DEFAULT; 468 fb->fb.pseudo_palette = fb->cmap; 469 470 strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); 471 fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; 472 fb->fb.fix.type_aux = 0; 473 fb->fb.fix.xpanstep = 0; 474 fb->fb.fix.ypanstep = 0; 475 fb->fb.fix.ywrapstep = 0; 476 fb->fb.fix.accel = FB_ACCEL_NONE; 477 478 fb->fb.var.xres = fb->panel->mode.xres; 479 fb->fb.var.yres = fb->panel->mode.yres; 480 fb->fb.var.xres_virtual = fb->panel->mode.xres; 481 fb->fb.var.yres_virtual = fb->panel->mode.yres; 482 fb->fb.var.bits_per_pixel = fb->panel->bpp; 483 fb->fb.var.grayscale = fb->panel->grayscale; 484 fb->fb.var.pixclock = fb->panel->mode.pixclock; 485 fb->fb.var.left_margin = fb->panel->mode.left_margin; 486 fb->fb.var.right_margin = fb->panel->mode.right_margin; 487 fb->fb.var.upper_margin = fb->panel->mode.upper_margin; 488 fb->fb.var.lower_margin = fb->panel->mode.lower_margin; 489 fb->fb.var.hsync_len = fb->panel->mode.hsync_len; 490 fb->fb.var.vsync_len = fb->panel->mode.vsync_len; 491 fb->fb.var.sync = fb->panel->mode.sync; 492 fb->fb.var.vmode = fb->panel->mode.vmode; 493 fb->fb.var.activate = FB_ACTIVATE_NOW; 494 fb->fb.var.nonstd = 0; 495 fb->fb.var.height = fb->panel->height; 496 fb->fb.var.width = fb->panel->width; 497 fb->fb.var.accel_flags = 0; 498 499 fb->fb.monspecs.hfmin = 0; 500 fb->fb.monspecs.hfmax = 100000; 501 fb->fb.monspecs.vfmin = 0; 502 fb->fb.monspecs.vfmax = 400; 503 fb->fb.monspecs.dclkmin = 1000000; 504 fb->fb.monspecs.dclkmax = 100000000; 505 506 /* 507 * Make sure that the bitfields are set appropriately. 508 */ 509 clcdfb_set_bitfields(fb, &fb->fb.var); 510 511 /* 512 * Allocate colourmap. 513 */ 514 ret = fb_alloc_cmap(&fb->fb.cmap, 256, 0); 515 if (ret) 516 goto unmap; 517 518 /* 519 * Ensure interrupts are disabled. 520 */ 521 writel(0, fb->regs + fb->off_ienb); 522 523 fb_set_var(&fb->fb, &fb->fb.var); 524 525 dev_info(&fb->dev->dev, "%s hardware, %s display\n", 526 fb->board->name, fb->panel->mode.name); 527 528 ret = register_framebuffer(&fb->fb); 529 if (ret == 0) 530 goto out; 531 532 printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); 533 534 fb_dealloc_cmap(&fb->fb.cmap); 535 unmap: 536 iounmap(fb->regs); 537 clk_unprep: 538 clk_unprepare(fb->clk); 539 free_clk: 540 clk_put(fb->clk); 541 out: 542 return ret; 543} 544 545static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) 546{ 547 struct clcd_board *board = dev->dev.platform_data; 548 struct clcd_fb *fb; 549 int ret; 550 551 if (!board) 552 return -EINVAL; 553 554 ret = amba_request_regions(dev, NULL); 555 if (ret) { 556 printk(KERN_ERR "CLCD: unable to reserve regs region\n"); 557 goto out; 558 } 559 560 fb = kzalloc(sizeof(struct clcd_fb), GFP_KERNEL); 561 if (!fb) { 562 printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n"); 563 ret = -ENOMEM; 564 goto free_region; 565 } 566 567 fb->dev = dev; 568 fb->board = board; 569 570 dev_info(&fb->dev->dev, "PL%03x rev%u at 0x%08llx\n", 571 amba_part(dev), amba_rev(dev), 572 (unsigned long long)dev->res.start); 573 574 ret = fb->board->setup(fb); 575 if (ret) 576 goto free_fb; 577 578 ret = clcdfb_register(fb); 579 if (ret == 0) { 580 amba_set_drvdata(dev, fb); 581 goto out; 582 } 583 584 fb->board->remove(fb); 585 free_fb: 586 kfree(fb); 587 free_region: 588 amba_release_regions(dev); 589 out: 590 return ret; 591} 592 593static int clcdfb_remove(struct amba_device *dev) 594{ 595 struct clcd_fb *fb = amba_get_drvdata(dev); 596 597 amba_set_drvdata(dev, NULL); 598 599 clcdfb_disable(fb); 600 unregister_framebuffer(&fb->fb); 601 if (fb->fb.cmap.len) 602 fb_dealloc_cmap(&fb->fb.cmap); 603 iounmap(fb->regs); 604 clk_unprepare(fb->clk); 605 clk_put(fb->clk); 606 607 fb->board->remove(fb); 608 609 kfree(fb); 610 611 amba_release_regions(dev); 612 613 return 0; 614} 615 616static struct amba_id clcdfb_id_table[] = { 617 { 618 .id = 0x00041110, 619 .mask = 0x000ffffe, 620 }, 621 { 0, 0 }, 622}; 623 624MODULE_DEVICE_TABLE(amba, clcdfb_id_table); 625 626static struct amba_driver clcd_driver = { 627 .drv = { 628 .name = "clcd-pl11x", 629 }, 630 .probe = clcdfb_probe, 631 .remove = clcdfb_remove, 632 .id_table = clcdfb_id_table, 633}; 634 635static int __init amba_clcdfb_init(void) 636{ 637 if (fb_get_options("ambafb", NULL)) 638 return -ENODEV; 639 640 return amba_driver_register(&clcd_driver); 641} 642 643module_init(amba_clcdfb_init); 644 645static void __exit amba_clcdfb_exit(void) 646{ 647 amba_driver_unregister(&clcd_driver); 648} 649 650module_exit(amba_clcdfb_exit); 651 652MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver"); 653MODULE_LICENSE("GPL");