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.38 565 lines 13 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 int ret = 0; 124 125 memset(&var->transp, 0, sizeof(var->transp)); 126 127 var->red.msb_right = 0; 128 var->green.msb_right = 0; 129 var->blue.msb_right = 0; 130 131 switch (var->bits_per_pixel) { 132 case 1: 133 case 2: 134 case 4: 135 case 8: 136 var->red.length = var->bits_per_pixel; 137 var->red.offset = 0; 138 var->green.length = var->bits_per_pixel; 139 var->green.offset = 0; 140 var->blue.length = var->bits_per_pixel; 141 var->blue.offset = 0; 142 break; 143 case 16: 144 var->red.length = 5; 145 var->blue.length = 5; 146 /* 147 * Green length can be 5 or 6 depending whether 148 * we're operating in RGB555 or RGB565 mode. 149 */ 150 if (var->green.length != 5 && var->green.length != 6) 151 var->green.length = 6; 152 break; 153 case 32: 154 if (fb->panel->cntl & CNTL_LCDTFT) { 155 var->red.length = 8; 156 var->green.length = 8; 157 var->blue.length = 8; 158 break; 159 } 160 default: 161 ret = -EINVAL; 162 break; 163 } 164 165 /* 166 * >= 16bpp displays have separate colour component bitfields 167 * encoded in the pixel data. Calculate their position from 168 * the bitfield length defined above. 169 */ 170 if (ret == 0 && var->bits_per_pixel >= 16) { 171 if (fb->panel->cntl & CNTL_BGR) { 172 var->blue.offset = 0; 173 var->green.offset = var->blue.offset + var->blue.length; 174 var->red.offset = var->green.offset + var->green.length; 175 } else { 176 var->red.offset = 0; 177 var->green.offset = var->red.offset + var->red.length; 178 var->blue.offset = var->green.offset + var->green.length; 179 } 180 } 181 182 return ret; 183} 184 185static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 186{ 187 struct clcd_fb *fb = to_clcd(info); 188 int ret = -EINVAL; 189 190 if (fb->board->check) 191 ret = fb->board->check(fb, var); 192 193 if (ret == 0 && 194 var->xres_virtual * var->bits_per_pixel / 8 * 195 var->yres_virtual > fb->fb.fix.smem_len) 196 ret = -EINVAL; 197 198 if (ret == 0) 199 ret = clcdfb_set_bitfields(fb, var); 200 201 return ret; 202} 203 204static int clcdfb_set_par(struct fb_info *info) 205{ 206 struct clcd_fb *fb = to_clcd(info); 207 struct clcd_regs regs; 208 209 fb->fb.fix.line_length = fb->fb.var.xres_virtual * 210 fb->fb.var.bits_per_pixel / 8; 211 212 if (fb->fb.var.bits_per_pixel <= 8) 213 fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; 214 else 215 fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; 216 217 fb->board->decode(fb, &regs); 218 219 clcdfb_disable(fb); 220 221 writel(regs.tim0, fb->regs + CLCD_TIM0); 222 writel(regs.tim1, fb->regs + CLCD_TIM1); 223 writel(regs.tim2, fb->regs + CLCD_TIM2); 224 writel(regs.tim3, fb->regs + CLCD_TIM3); 225 226 clcdfb_set_start(fb); 227 228 clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000); 229 230 fb->clcd_cntl = regs.cntl; 231 232 clcdfb_enable(fb, regs.cntl); 233 234#ifdef DEBUG 235 printk(KERN_INFO 236 "CLCD: Registers set to\n" 237 " %08x %08x %08x %08x\n" 238 " %08x %08x %08x %08x\n", 239 readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1), 240 readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3), 241 readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS), 242 readl(fb->regs + fb->off_ienb), readl(fb->regs + fb->off_cntl)); 243#endif 244 245 return 0; 246} 247 248static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) 249{ 250 unsigned int mask = (1 << bf->length) - 1; 251 252 return (val >> (16 - bf->length) & mask) << bf->offset; 253} 254 255/* 256 * Set a single color register. The values supplied have a 16 bit 257 * magnitude. Return != 0 for invalid regno. 258 */ 259static int 260clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, 261 unsigned int blue, unsigned int transp, struct fb_info *info) 262{ 263 struct clcd_fb *fb = to_clcd(info); 264 265 if (regno < 16) 266 fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | 267 convert_bitfield(blue, &fb->fb.var.blue) | 268 convert_bitfield(green, &fb->fb.var.green) | 269 convert_bitfield(red, &fb->fb.var.red); 270 271 if (fb->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { 272 int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3); 273 u32 val, mask, newval; 274 275 newval = (red >> 11) & 0x001f; 276 newval |= (green >> 6) & 0x03e0; 277 newval |= (blue >> 1) & 0x7c00; 278 279 /* 280 * 3.2.11: if we're configured for big endian 281 * byte order, the palette entries are swapped. 282 */ 283 if (fb->clcd_cntl & CNTL_BEBO) 284 regno ^= 1; 285 286 if (regno & 1) { 287 newval <<= 16; 288 mask = 0x0000ffff; 289 } else { 290 mask = 0xffff0000; 291 } 292 293 val = readl(fb->regs + hw_reg) & mask; 294 writel(val | newval, fb->regs + hw_reg); 295 } 296 297 return regno > 255; 298} 299 300/* 301 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL 302 * then the caller blanks by setting the CLUT (Color Look Up Table) to all 303 * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due 304 * to e.g. a video mode which doesn't support it. Implements VESA suspend 305 * and powerdown modes on hardware that supports disabling hsync/vsync: 306 * blank_mode == 2: suspend vsync 307 * blank_mode == 3: suspend hsync 308 * blank_mode == 4: powerdown 309 */ 310static int clcdfb_blank(int blank_mode, struct fb_info *info) 311{ 312 struct clcd_fb *fb = to_clcd(info); 313 314 if (blank_mode != 0) { 315 clcdfb_disable(fb); 316 } else { 317 clcdfb_enable(fb, fb->clcd_cntl); 318 } 319 return 0; 320} 321 322static int clcdfb_mmap(struct fb_info *info, 323 struct vm_area_struct *vma) 324{ 325 struct clcd_fb *fb = to_clcd(info); 326 unsigned long len, off = vma->vm_pgoff << PAGE_SHIFT; 327 int ret = -EINVAL; 328 329 len = info->fix.smem_len; 330 331 if (off <= len && vma->vm_end - vma->vm_start <= len - off && 332 fb->board->mmap) 333 ret = fb->board->mmap(fb, vma); 334 335 return ret; 336} 337 338static struct fb_ops clcdfb_ops = { 339 .owner = THIS_MODULE, 340 .fb_check_var = clcdfb_check_var, 341 .fb_set_par = clcdfb_set_par, 342 .fb_setcolreg = clcdfb_setcolreg, 343 .fb_blank = clcdfb_blank, 344 .fb_fillrect = cfb_fillrect, 345 .fb_copyarea = cfb_copyarea, 346 .fb_imageblit = cfb_imageblit, 347 .fb_mmap = clcdfb_mmap, 348}; 349 350static int clcdfb_register(struct clcd_fb *fb) 351{ 352 int ret; 353 354 /* 355 * ARM PL111 always has IENB at 0x1c; it's only PL110 356 * which is reversed on some platforms. 357 */ 358 if (amba_manf(fb->dev) == 0x41 && amba_part(fb->dev) == 0x111) { 359 fb->off_ienb = CLCD_PL111_IENB; 360 fb->off_cntl = CLCD_PL111_CNTL; 361 } else { 362#ifdef CONFIG_ARCH_VERSATILE 363 fb->off_ienb = CLCD_PL111_IENB; 364 fb->off_cntl = CLCD_PL111_CNTL; 365#else 366 fb->off_ienb = CLCD_PL110_IENB; 367 fb->off_cntl = CLCD_PL110_CNTL; 368#endif 369 } 370 371 fb->clk = clk_get(&fb->dev->dev, NULL); 372 if (IS_ERR(fb->clk)) { 373 ret = PTR_ERR(fb->clk); 374 goto out; 375 } 376 377 fb->fb.fix.mmio_start = fb->dev->res.start; 378 fb->fb.fix.mmio_len = resource_size(&fb->dev->res); 379 380 fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); 381 if (!fb->regs) { 382 printk(KERN_ERR "CLCD: unable to remap registers\n"); 383 ret = -ENOMEM; 384 goto free_clk; 385 } 386 387 fb->fb.fbops = &clcdfb_ops; 388 fb->fb.flags = FBINFO_FLAG_DEFAULT; 389 fb->fb.pseudo_palette = fb->cmap; 390 391 strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); 392 fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; 393 fb->fb.fix.type_aux = 0; 394 fb->fb.fix.xpanstep = 0; 395 fb->fb.fix.ypanstep = 0; 396 fb->fb.fix.ywrapstep = 0; 397 fb->fb.fix.accel = FB_ACCEL_NONE; 398 399 fb->fb.var.xres = fb->panel->mode.xres; 400 fb->fb.var.yres = fb->panel->mode.yres; 401 fb->fb.var.xres_virtual = fb->panel->mode.xres; 402 fb->fb.var.yres_virtual = fb->panel->mode.yres; 403 fb->fb.var.bits_per_pixel = fb->panel->bpp; 404 fb->fb.var.grayscale = fb->panel->grayscale; 405 fb->fb.var.pixclock = fb->panel->mode.pixclock; 406 fb->fb.var.left_margin = fb->panel->mode.left_margin; 407 fb->fb.var.right_margin = fb->panel->mode.right_margin; 408 fb->fb.var.upper_margin = fb->panel->mode.upper_margin; 409 fb->fb.var.lower_margin = fb->panel->mode.lower_margin; 410 fb->fb.var.hsync_len = fb->panel->mode.hsync_len; 411 fb->fb.var.vsync_len = fb->panel->mode.vsync_len; 412 fb->fb.var.sync = fb->panel->mode.sync; 413 fb->fb.var.vmode = fb->panel->mode.vmode; 414 fb->fb.var.activate = FB_ACTIVATE_NOW; 415 fb->fb.var.nonstd = 0; 416 fb->fb.var.height = fb->panel->height; 417 fb->fb.var.width = fb->panel->width; 418 fb->fb.var.accel_flags = 0; 419 420 fb->fb.monspecs.hfmin = 0; 421 fb->fb.monspecs.hfmax = 100000; 422 fb->fb.monspecs.vfmin = 0; 423 fb->fb.monspecs.vfmax = 400; 424 fb->fb.monspecs.dclkmin = 1000000; 425 fb->fb.monspecs.dclkmax = 100000000; 426 427 /* 428 * Make sure that the bitfields are set appropriately. 429 */ 430 clcdfb_set_bitfields(fb, &fb->fb.var); 431 432 /* 433 * Allocate colourmap. 434 */ 435 ret = fb_alloc_cmap(&fb->fb.cmap, 256, 0); 436 if (ret) 437 goto unmap; 438 439 /* 440 * Ensure interrupts are disabled. 441 */ 442 writel(0, fb->regs + fb->off_ienb); 443 444 fb_set_var(&fb->fb, &fb->fb.var); 445 446 printk(KERN_INFO "CLCD: %s hardware, %s display\n", 447 fb->board->name, fb->panel->mode.name); 448 449 ret = register_framebuffer(&fb->fb); 450 if (ret == 0) 451 goto out; 452 453 printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); 454 455 fb_dealloc_cmap(&fb->fb.cmap); 456 unmap: 457 iounmap(fb->regs); 458 free_clk: 459 clk_put(fb->clk); 460 out: 461 return ret; 462} 463 464static int clcdfb_probe(struct amba_device *dev, struct amba_id *id) 465{ 466 struct clcd_board *board = dev->dev.platform_data; 467 struct clcd_fb *fb; 468 int ret; 469 470 if (!board) 471 return -EINVAL; 472 473 ret = amba_request_regions(dev, NULL); 474 if (ret) { 475 printk(KERN_ERR "CLCD: unable to reserve regs region\n"); 476 goto out; 477 } 478 479 fb = kzalloc(sizeof(struct clcd_fb), GFP_KERNEL); 480 if (!fb) { 481 printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n"); 482 ret = -ENOMEM; 483 goto free_region; 484 } 485 486 fb->dev = dev; 487 fb->board = board; 488 489 ret = fb->board->setup(fb); 490 if (ret) 491 goto free_fb; 492 493 ret = clcdfb_register(fb); 494 if (ret == 0) { 495 amba_set_drvdata(dev, fb); 496 goto out; 497 } 498 499 fb->board->remove(fb); 500 free_fb: 501 kfree(fb); 502 free_region: 503 amba_release_regions(dev); 504 out: 505 return ret; 506} 507 508static int clcdfb_remove(struct amba_device *dev) 509{ 510 struct clcd_fb *fb = amba_get_drvdata(dev); 511 512 amba_set_drvdata(dev, NULL); 513 514 clcdfb_disable(fb); 515 unregister_framebuffer(&fb->fb); 516 if (fb->fb.cmap.len) 517 fb_dealloc_cmap(&fb->fb.cmap); 518 iounmap(fb->regs); 519 clk_put(fb->clk); 520 521 fb->board->remove(fb); 522 523 kfree(fb); 524 525 amba_release_regions(dev); 526 527 return 0; 528} 529 530static struct amba_id clcdfb_id_table[] = { 531 { 532 .id = 0x00041110, 533 .mask = 0x000ffffe, 534 }, 535 { 0, 0 }, 536}; 537 538static struct amba_driver clcd_driver = { 539 .drv = { 540 .name = "clcd-pl11x", 541 }, 542 .probe = clcdfb_probe, 543 .remove = clcdfb_remove, 544 .id_table = clcdfb_id_table, 545}; 546 547static int __init amba_clcdfb_init(void) 548{ 549 if (fb_get_options("ambafb", NULL)) 550 return -ENODEV; 551 552 return amba_driver_register(&clcd_driver); 553} 554 555module_init(amba_clcdfb_init); 556 557static void __exit amba_clcdfb_exit(void) 558{ 559 amba_driver_unregister(&clcd_driver); 560} 561 562module_exit(amba_clcdfb_exit); 563 564MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver"); 565MODULE_LICENSE("GPL");