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.24-rc2 549 lines 12 kB view raw
1/* tcx.c: TCX frame buffer driver 2 * 3 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net) 4 * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) 5 * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) 6 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) 7 * 8 * Driver layout based loosely on tgafb.c, see that file for credits. 9 */ 10 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/errno.h> 14#include <linux/string.h> 15#include <linux/slab.h> 16#include <linux/delay.h> 17#include <linux/init.h> 18#include <linux/fb.h> 19#include <linux/mm.h> 20 21#include <asm/io.h> 22#include <asm/prom.h> 23#include <asm/of_device.h> 24#include <asm/fbio.h> 25 26#include "sbuslib.h" 27 28/* 29 * Local functions. 30 */ 31 32static int tcx_setcolreg(unsigned, unsigned, unsigned, unsigned, 33 unsigned, struct fb_info *); 34static int tcx_blank(int, struct fb_info *); 35 36static int tcx_mmap(struct fb_info *, struct vm_area_struct *); 37static int tcx_ioctl(struct fb_info *, unsigned int, unsigned long); 38static int tcx_pan_display(struct fb_var_screeninfo *, struct fb_info *); 39 40/* 41 * Frame buffer operations 42 */ 43 44static struct fb_ops tcx_ops = { 45 .owner = THIS_MODULE, 46 .fb_setcolreg = tcx_setcolreg, 47 .fb_blank = tcx_blank, 48 .fb_pan_display = tcx_pan_display, 49 .fb_fillrect = cfb_fillrect, 50 .fb_copyarea = cfb_copyarea, 51 .fb_imageblit = cfb_imageblit, 52 .fb_mmap = tcx_mmap, 53 .fb_ioctl = tcx_ioctl, 54#ifdef CONFIG_COMPAT 55 .fb_compat_ioctl = sbusfb_compat_ioctl, 56#endif 57}; 58 59/* THC definitions */ 60#define TCX_THC_MISC_REV_SHIFT 16 61#define TCX_THC_MISC_REV_MASK 15 62#define TCX_THC_MISC_VSYNC_DIS (1 << 25) 63#define TCX_THC_MISC_HSYNC_DIS (1 << 24) 64#define TCX_THC_MISC_RESET (1 << 12) 65#define TCX_THC_MISC_VIDEO (1 << 10) 66#define TCX_THC_MISC_SYNC (1 << 9) 67#define TCX_THC_MISC_VSYNC (1 << 8) 68#define TCX_THC_MISC_SYNC_ENAB (1 << 7) 69#define TCX_THC_MISC_CURS_RES (1 << 6) 70#define TCX_THC_MISC_INT_ENAB (1 << 5) 71#define TCX_THC_MISC_INT (1 << 4) 72#define TCX_THC_MISC_INIT 0x9f 73#define TCX_THC_REV_REV_SHIFT 20 74#define TCX_THC_REV_REV_MASK 15 75#define TCX_THC_REV_MINREV_SHIFT 28 76#define TCX_THC_REV_MINREV_MASK 15 77 78/* The contents are unknown */ 79struct tcx_tec { 80 u32 tec_matrix; 81 u32 tec_clip; 82 u32 tec_vdc; 83}; 84 85struct tcx_thc { 86 u32 thc_rev; 87 u32 thc_pad0[511]; 88 u32 thc_hs; /* hsync timing */ 89 u32 thc_hsdvs; 90 u32 thc_hd; 91 u32 thc_vs; /* vsync timing */ 92 u32 thc_vd; 93 u32 thc_refresh; 94 u32 thc_misc; 95 u32 thc_pad1[56]; 96 u32 thc_cursxy; /* cursor x,y position (16 bits each) */ 97 u32 thc_cursmask[32]; /* cursor mask bits */ 98 u32 thc_cursbits[32]; /* what to show where mask enabled */ 99}; 100 101struct bt_regs { 102 u32 addr; 103 u32 color_map; 104 u32 control; 105 u32 cursor; 106}; 107 108#define TCX_MMAP_ENTRIES 14 109 110struct tcx_par { 111 spinlock_t lock; 112 struct bt_regs __iomem *bt; 113 struct tcx_thc __iomem *thc; 114 struct tcx_tec __iomem *tec; 115 u32 __iomem *cplane; 116 117 u32 flags; 118#define TCX_FLAG_BLANKED 0x00000001 119 120 unsigned long physbase; 121 unsigned long which_io; 122 unsigned long fbsize; 123 124 struct sbus_mmap_map mmap_map[TCX_MMAP_ENTRIES]; 125 int lowdepth; 126}; 127 128/* Reset control plane so that WID is 8-bit plane. */ 129static void __tcx_set_control_plane (struct tcx_par *par) 130{ 131 u32 __iomem *p, *pend; 132 133 if (par->lowdepth) 134 return; 135 136 p = par->cplane; 137 if (p == NULL) 138 return; 139 for (pend = p + par->fbsize; p < pend; p++) { 140 u32 tmp = sbus_readl(p); 141 142 tmp &= 0xffffff; 143 sbus_writel(tmp, p); 144 } 145} 146 147static void tcx_reset (struct fb_info *info) 148{ 149 struct tcx_par *par = (struct tcx_par *) info->par; 150 unsigned long flags; 151 152 spin_lock_irqsave(&par->lock, flags); 153 __tcx_set_control_plane(par); 154 spin_unlock_irqrestore(&par->lock, flags); 155} 156 157static int tcx_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 158{ 159 tcx_reset(info); 160 return 0; 161} 162 163/** 164 * tcx_setcolreg - Optional function. Sets a color register. 165 * @regno: boolean, 0 copy local, 1 get_user() function 166 * @red: frame buffer colormap structure 167 * @green: The green value which can be up to 16 bits wide 168 * @blue: The blue value which can be up to 16 bits wide. 169 * @transp: If supported the alpha value which can be up to 16 bits wide. 170 * @info: frame buffer info structure 171 */ 172static int tcx_setcolreg(unsigned regno, 173 unsigned red, unsigned green, unsigned blue, 174 unsigned transp, struct fb_info *info) 175{ 176 struct tcx_par *par = (struct tcx_par *) info->par; 177 struct bt_regs __iomem *bt = par->bt; 178 unsigned long flags; 179 180 if (regno >= 256) 181 return 1; 182 183 red >>= 8; 184 green >>= 8; 185 blue >>= 8; 186 187 spin_lock_irqsave(&par->lock, flags); 188 189 sbus_writel(regno << 24, &bt->addr); 190 sbus_writel(red << 24, &bt->color_map); 191 sbus_writel(green << 24, &bt->color_map); 192 sbus_writel(blue << 24, &bt->color_map); 193 194 spin_unlock_irqrestore(&par->lock, flags); 195 196 return 0; 197} 198 199/** 200 * tcx_blank - Optional function. Blanks the display. 201 * @blank_mode: the blank mode we want. 202 * @info: frame buffer structure that represents a single frame buffer 203 */ 204static int 205tcx_blank(int blank, struct fb_info *info) 206{ 207 struct tcx_par *par = (struct tcx_par *) info->par; 208 struct tcx_thc __iomem *thc = par->thc; 209 unsigned long flags; 210 u32 val; 211 212 spin_lock_irqsave(&par->lock, flags); 213 214 val = sbus_readl(&thc->thc_misc); 215 216 switch (blank) { 217 case FB_BLANK_UNBLANK: /* Unblanking */ 218 val &= ~(TCX_THC_MISC_VSYNC_DIS | 219 TCX_THC_MISC_HSYNC_DIS); 220 val |= TCX_THC_MISC_VIDEO; 221 par->flags &= ~TCX_FLAG_BLANKED; 222 break; 223 224 case FB_BLANK_NORMAL: /* Normal blanking */ 225 val &= ~TCX_THC_MISC_VIDEO; 226 par->flags |= TCX_FLAG_BLANKED; 227 break; 228 229 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */ 230 val |= TCX_THC_MISC_VSYNC_DIS; 231 break; 232 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */ 233 val |= TCX_THC_MISC_HSYNC_DIS; 234 break; 235 236 case FB_BLANK_POWERDOWN: /* Poweroff */ 237 break; 238 }; 239 240 sbus_writel(val, &thc->thc_misc); 241 242 spin_unlock_irqrestore(&par->lock, flags); 243 244 return 0; 245} 246 247static struct sbus_mmap_map __tcx_mmap_map[TCX_MMAP_ENTRIES] = { 248 { 249 .voff = TCX_RAM8BIT, 250 .size = SBUS_MMAP_FBSIZE(1) 251 }, 252 { 253 .voff = TCX_RAM24BIT, 254 .size = SBUS_MMAP_FBSIZE(4) 255 }, 256 { 257 .voff = TCX_UNK3, 258 .size = SBUS_MMAP_FBSIZE(8) 259 }, 260 { 261 .voff = TCX_UNK4, 262 .size = SBUS_MMAP_FBSIZE(8) 263 }, 264 { 265 .voff = TCX_CONTROLPLANE, 266 .size = SBUS_MMAP_FBSIZE(4) 267 }, 268 { 269 .voff = TCX_UNK6, 270 .size = SBUS_MMAP_FBSIZE(8) 271 }, 272 { 273 .voff = TCX_UNK7, 274 .size = SBUS_MMAP_FBSIZE(8) 275 }, 276 { 277 .voff = TCX_TEC, 278 .size = PAGE_SIZE 279 }, 280 { 281 .voff = TCX_BTREGS, 282 .size = PAGE_SIZE 283 }, 284 { 285 .voff = TCX_THC, 286 .size = PAGE_SIZE 287 }, 288 { 289 .voff = TCX_DHC, 290 .size = PAGE_SIZE 291 }, 292 { 293 .voff = TCX_ALT, 294 .size = PAGE_SIZE 295 }, 296 { 297 .voff = TCX_UNK2, 298 .size = 0x20000 299 }, 300 { .size = 0 } 301}; 302 303static int tcx_mmap(struct fb_info *info, struct vm_area_struct *vma) 304{ 305 struct tcx_par *par = (struct tcx_par *)info->par; 306 307 return sbusfb_mmap_helper(par->mmap_map, 308 par->physbase, par->fbsize, 309 par->which_io, vma); 310} 311 312static int tcx_ioctl(struct fb_info *info, unsigned int cmd, 313 unsigned long arg) 314{ 315 struct tcx_par *par = (struct tcx_par *) info->par; 316 317 return sbusfb_ioctl_helper(cmd, arg, info, 318 FBTYPE_TCXCOLOR, 319 (par->lowdepth ? 8 : 24), 320 par->fbsize); 321} 322 323/* 324 * Initialisation 325 */ 326 327static void 328tcx_init_fix(struct fb_info *info, int linebytes) 329{ 330 struct tcx_par *par = (struct tcx_par *)info->par; 331 const char *tcx_name; 332 333 if (par->lowdepth) 334 tcx_name = "TCX8"; 335 else 336 tcx_name = "TCX24"; 337 338 strlcpy(info->fix.id, tcx_name, sizeof(info->fix.id)); 339 340 info->fix.type = FB_TYPE_PACKED_PIXELS; 341 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 342 343 info->fix.line_length = linebytes; 344 345 info->fix.accel = FB_ACCEL_SUN_TCX; 346} 347 348static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, 349 struct tcx_par *par) 350{ 351 if (par->tec) 352 of_iounmap(&op->resource[7], 353 par->tec, sizeof(struct tcx_tec)); 354 if (par->thc) 355 of_iounmap(&op->resource[9], 356 par->thc, sizeof(struct tcx_thc)); 357 if (par->bt) 358 of_iounmap(&op->resource[8], 359 par->bt, sizeof(struct bt_regs)); 360 if (par->cplane) 361 of_iounmap(&op->resource[4], 362 par->cplane, par->fbsize * sizeof(u32)); 363 if (info->screen_base) 364 of_iounmap(&op->resource[0], 365 info->screen_base, par->fbsize); 366} 367 368static int __devinit tcx_init_one(struct of_device *op) 369{ 370 struct device_node *dp = op->node; 371 struct fb_info *info; 372 struct tcx_par *par; 373 int linebytes, i, err; 374 375 info = framebuffer_alloc(sizeof(struct tcx_par), &op->dev); 376 377 err = -ENOMEM; 378 if (!info) 379 goto out_err; 380 par = info->par; 381 382 spin_lock_init(&par->lock); 383 384 par->lowdepth = 385 (of_find_property(dp, "tcx-8-bit", NULL) != NULL); 386 387 sbusfb_fill_var(&info->var, dp->node, 8); 388 info->var.red.length = 8; 389 info->var.green.length = 8; 390 info->var.blue.length = 8; 391 392 linebytes = of_getintprop_default(dp, "linebytes", 393 info->var.xres); 394 par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); 395 396 par->tec = of_ioremap(&op->resource[7], 0, 397 sizeof(struct tcx_tec), "tcx tec"); 398 par->thc = of_ioremap(&op->resource[9], 0, 399 sizeof(struct tcx_thc), "tcx thc"); 400 par->bt = of_ioremap(&op->resource[8], 0, 401 sizeof(struct bt_regs), "tcx dac"); 402 info->screen_base = of_ioremap(&op->resource[0], 0, 403 par->fbsize, "tcx ram"); 404 if (!par->tec || !par->thc || 405 !par->bt || !info->screen_base) 406 goto out_unmap_regs; 407 408 memcpy(&par->mmap_map, &__tcx_mmap_map, sizeof(par->mmap_map)); 409 if (!par->lowdepth) { 410 par->cplane = of_ioremap(&op->resource[4], 0, 411 par->fbsize * sizeof(u32), 412 "tcx cplane"); 413 if (!par->cplane) 414 goto out_unmap_regs; 415 } else { 416 par->mmap_map[1].size = SBUS_MMAP_EMPTY; 417 par->mmap_map[4].size = SBUS_MMAP_EMPTY; 418 par->mmap_map[5].size = SBUS_MMAP_EMPTY; 419 par->mmap_map[6].size = SBUS_MMAP_EMPTY; 420 } 421 422 par->physbase = 0; 423 par->which_io = op->resource[0].flags & IORESOURCE_BITS; 424 425 for (i = 0; i < TCX_MMAP_ENTRIES; i++) { 426 int j; 427 428 switch (i) { 429 case 10: 430 j = 12; 431 break; 432 433 case 11: case 12: 434 j = i - 1; 435 break; 436 437 default: 438 j = i; 439 break; 440 }; 441 par->mmap_map[i].poff = op->resource[j].start; 442 } 443 444 info->flags = FBINFO_DEFAULT; 445 info->fbops = &tcx_ops; 446 447 /* Initialize brooktree DAC. */ 448 sbus_writel(0x04 << 24, &par->bt->addr); /* color planes */ 449 sbus_writel(0xff << 24, &par->bt->control); 450 sbus_writel(0x05 << 24, &par->bt->addr); 451 sbus_writel(0x00 << 24, &par->bt->control); 452 sbus_writel(0x06 << 24, &par->bt->addr); /* overlay plane */ 453 sbus_writel(0x73 << 24, &par->bt->control); 454 sbus_writel(0x07 << 24, &par->bt->addr); 455 sbus_writel(0x00 << 24, &par->bt->control); 456 457 tcx_reset(info); 458 459 tcx_blank(FB_BLANK_UNBLANK, info); 460 461 if (fb_alloc_cmap(&info->cmap, 256, 0)) 462 goto out_unmap_regs; 463 464 fb_set_cmap(&info->cmap, info); 465 tcx_init_fix(info, linebytes); 466 467 err = register_framebuffer(info); 468 if (err < 0) 469 goto out_dealloc_cmap; 470 471 dev_set_drvdata(&op->dev, info); 472 473 printk("%s: TCX at %lx:%lx, %s\n", 474 dp->full_name, 475 par->which_io, 476 op->resource[0].start, 477 par->lowdepth ? "8-bit only" : "24-bit depth"); 478 479 return 0; 480 481out_dealloc_cmap: 482 fb_dealloc_cmap(&info->cmap); 483 484out_unmap_regs: 485 tcx_unmap_regs(op, info, par); 486 487out_err: 488 return err; 489} 490 491static int __devinit tcx_probe(struct of_device *dev, const struct of_device_id *match) 492{ 493 struct of_device *op = to_of_device(&dev->dev); 494 495 return tcx_init_one(op); 496} 497 498static int __devexit tcx_remove(struct of_device *op) 499{ 500 struct fb_info *info = dev_get_drvdata(&op->dev); 501 struct tcx_par *par = info->par; 502 503 unregister_framebuffer(info); 504 fb_dealloc_cmap(&info->cmap); 505 506 tcx_unmap_regs(op, info, par); 507 508 framebuffer_release(info); 509 510 dev_set_drvdata(&op->dev, NULL); 511 512 return 0; 513} 514 515static struct of_device_id tcx_match[] = { 516 { 517 .name = "SUNW,tcx", 518 }, 519 {}, 520}; 521MODULE_DEVICE_TABLE(of, tcx_match); 522 523static struct of_platform_driver tcx_driver = { 524 .name = "tcx", 525 .match_table = tcx_match, 526 .probe = tcx_probe, 527 .remove = __devexit_p(tcx_remove), 528}; 529 530int __init tcx_init(void) 531{ 532 if (fb_get_options("tcxfb", NULL)) 533 return -ENODEV; 534 535 return of_register_driver(&tcx_driver, &of_bus_type); 536} 537 538void __exit tcx_exit(void) 539{ 540 of_unregister_driver(&tcx_driver); 541} 542 543module_init(tcx_init); 544module_exit(tcx_exit); 545 546MODULE_DESCRIPTION("framebuffer driver for TCX chipsets"); 547MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); 548MODULE_VERSION("2.0"); 549MODULE_LICENSE("GPL");