1/* $Id: cgsixfb.c,v 1.1.1.1 2008/10/15 03:27:04 james26_jang Exp $ 2 * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver 3 * 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 9#include <linux/module.h> 10#include <linux/sched.h> 11#include <linux/kernel.h> 12#include <linux/errno.h> 13#include <linux/string.h> 14#include <linux/mm.h> 15#include <linux/tty.h> 16#include <linux/slab.h> 17#include <linux/vmalloc.h> 18#include <linux/delay.h> 19#include <linux/interrupt.h> 20#include <linux/fb.h> 21#include <linux/init.h> 22#include <linux/selection.h> 23 24#include <video/sbusfb.h> 25#include <asm/io.h> 26 27/* Offset of interesting structures in the OBIO space */ 28/* 29 * Brooktree is the video dac and is funny to program on the cg6. 30 * (it's even funnier on the cg3) 31 * The FBC could be the frame buffer control 32 * The FHC could is the frame buffer hardware control. 33 */ 34#define CG6_ROM_OFFSET 0x0UL 35#define CG6_BROOKTREE_OFFSET 0x200000UL 36#define CG6_DHC_OFFSET 0x240000UL 37#define CG6_ALT_OFFSET 0x280000UL 38#define CG6_FHC_OFFSET 0x300000UL 39#define CG6_THC_OFFSET 0x301000UL 40#define CG6_FBC_OFFSET 0x700000UL 41#define CG6_TEC_OFFSET 0x701000UL 42#define CG6_RAM_OFFSET 0x800000UL 43 44/* FHC definitions */ 45#define CG6_FHC_FBID_SHIFT 24 46#define CG6_FHC_FBID_MASK 255 47#define CG6_FHC_REV_SHIFT 20 48#define CG6_FHC_REV_MASK 15 49#define CG6_FHC_FROP_DISABLE (1 << 19) 50#define CG6_FHC_ROW_DISABLE (1 << 18) 51#define CG6_FHC_SRC_DISABLE (1 << 17) 52#define CG6_FHC_DST_DISABLE (1 << 16) 53#define CG6_FHC_RESET (1 << 15) 54#define CG6_FHC_LITTLE_ENDIAN (1 << 13) 55#define CG6_FHC_RES_MASK (3 << 11) 56#define CG6_FHC_1024 (0 << 11) 57#define CG6_FHC_1152 (1 << 11) 58#define CG6_FHC_1280 (2 << 11) 59#define CG6_FHC_1600 (3 << 11) 60#define CG6_FHC_CPU_MASK (3 << 9) 61#define CG6_FHC_CPU_SPARC (0 << 9) 62#define CG6_FHC_CPU_68020 (1 << 9) 63#define CG6_FHC_CPU_386 (2 << 9) 64#define CG6_FHC_TEST (1 << 8) 65#define CG6_FHC_TEST_X_SHIFT 4 66#define CG6_FHC_TEST_X_MASK 15 67#define CG6_FHC_TEST_Y_SHIFT 0 68#define CG6_FHC_TEST_Y_MASK 15 69 70/* FBC mode definitions */ 71#define CG6_FBC_BLIT_IGNORE 0x00000000 72#define CG6_FBC_BLIT_NOSRC 0x00100000 73#define CG6_FBC_BLIT_SRC 0x00200000 74#define CG6_FBC_BLIT_ILLEGAL 0x00300000 75#define CG6_FBC_BLIT_MASK 0x00300000 76 77#define CG6_FBC_VBLANK 0x00080000 78 79#define CG6_FBC_MODE_IGNORE 0x00000000 80#define CG6_FBC_MODE_COLOR8 0x00020000 81#define CG6_FBC_MODE_COLOR1 0x00040000 82#define CG6_FBC_MODE_HRMONO 0x00060000 83#define CG6_FBC_MODE_MASK 0x00060000 84 85#define CG6_FBC_DRAW_IGNORE 0x00000000 86#define CG6_FBC_DRAW_RENDER 0x00008000 87#define CG6_FBC_DRAW_PICK 0x00010000 88#define CG6_FBC_DRAW_ILLEGAL 0x00018000 89#define CG6_FBC_DRAW_MASK 0x00018000 90 91#define CG6_FBC_BWRITE0_IGNORE 0x00000000 92#define CG6_FBC_BWRITE0_ENABLE 0x00002000 93#define CG6_FBC_BWRITE0_DISABLE 0x00004000 94#define CG6_FBC_BWRITE0_ILLEGAL 0x00006000 95#define CG6_FBC_BWRITE0_MASK 0x00006000 96 97#define CG6_FBC_BWRITE1_IGNORE 0x00000000 98#define CG6_FBC_BWRITE1_ENABLE 0x00000800 99#define CG6_FBC_BWRITE1_DISABLE 0x00001000 100#define CG6_FBC_BWRITE1_ILLEGAL 0x00001800 101#define CG6_FBC_BWRITE1_MASK 0x00001800 102 103#define CG6_FBC_BREAD_IGNORE 0x00000000 104#define CG6_FBC_BREAD_0 0x00000200 105#define CG6_FBC_BREAD_1 0x00000400 106#define CG6_FBC_BREAD_ILLEGAL 0x00000600 107#define CG6_FBC_BREAD_MASK 0x00000600 108 109#define CG6_FBC_BDISP_IGNORE 0x00000000 110#define CG6_FBC_BDISP_0 0x00000080 111#define CG6_FBC_BDISP_1 0x00000100 112#define CG6_FBC_BDISP_ILLEGAL 0x00000180 113#define CG6_FBC_BDISP_MASK 0x00000180 114 115#define CG6_FBC_INDEX_MOD 0x00000040 116#define CG6_FBC_INDEX_MASK 0x00000030 117 118/* THC definitions */ 119#define CG6_THC_MISC_REV_SHIFT 16 120#define CG6_THC_MISC_REV_MASK 15 121#define CG6_THC_MISC_RESET (1 << 12) 122#define CG6_THC_MISC_VIDEO (1 << 10) 123#define CG6_THC_MISC_SYNC (1 << 9) 124#define CG6_THC_MISC_VSYNC (1 << 8) 125#define CG6_THC_MISC_SYNC_ENAB (1 << 7) 126#define CG6_THC_MISC_CURS_RES (1 << 6) 127#define CG6_THC_MISC_INT_ENAB (1 << 5) 128#define CG6_THC_MISC_INT (1 << 4) 129#define CG6_THC_MISC_INIT 0x9f 130 131MODULE_LICENSE("GPL"); 132 133/* The contents are unknown */ 134struct cg6_tec { 135 volatile int tec_matrix; 136 volatile int tec_clip; 137 volatile int tec_vdc; 138}; 139 140struct cg6_thc { 141 uint thc_pad0[512]; 142 volatile uint thc_hs; /* hsync timing */ 143 volatile uint thc_hsdvs; 144 volatile uint thc_hd; 145 volatile uint thc_vs; /* vsync timing */ 146 volatile uint thc_vd; 147 volatile uint thc_refresh; 148 volatile uint thc_misc; 149 uint thc_pad1[56]; 150 volatile uint thc_cursxy; /* cursor x,y position (16 bits each) */ 151 volatile uint thc_cursmask[32]; /* cursor mask bits */ 152 volatile uint thc_cursbits[32]; /* what to show where mask enabled */ 153}; 154 155struct cg6_fbc { 156 u32 xxx0[1]; 157 volatile u32 mode; 158 volatile u32 clip; 159 u32 xxx1[1]; 160 volatile u32 s; 161 volatile u32 draw; 162 volatile u32 blit; 163 volatile u32 font; 164 u32 xxx2[24]; 165 volatile u32 x0, y0, z0, color0; 166 volatile u32 x1, y1, z1, color1; 167 volatile u32 x2, y2, z2, color2; 168 volatile u32 x3, y3, z3, color3; 169 volatile u32 offx, offy; 170 u32 xxx3[2]; 171 volatile u32 incx, incy; 172 u32 xxx4[2]; 173 volatile u32 clipminx, clipminy; 174 u32 xxx5[2]; 175 volatile u32 clipmaxx, clipmaxy; 176 u32 xxx6[2]; 177 volatile u32 fg; 178 volatile u32 bg; 179 volatile u32 alu; 180 volatile u32 pm; 181 volatile u32 pixelm; 182 u32 xxx7[2]; 183 volatile u32 patalign; 184 volatile u32 pattern[8]; 185 u32 xxx8[432]; 186 volatile u32 apointx, apointy, apointz; 187 u32 xxx9[1]; 188 volatile u32 rpointx, rpointy, rpointz; 189 u32 xxx10[5]; 190 volatile u32 pointr, pointg, pointb, pointa; 191 volatile u32 alinex, aliney, alinez; 192 u32 xxx11[1]; 193 volatile u32 rlinex, rliney, rlinez; 194 u32 xxx12[5]; 195 volatile u32 liner, lineg, lineb, linea; 196 volatile u32 atrix, atriy, atriz; 197 u32 xxx13[1]; 198 volatile u32 rtrix, rtriy, rtriz; 199 u32 xxx14[5]; 200 volatile u32 trir, trig, trib, tria; 201 volatile u32 aquadx, aquady, aquadz; 202 u32 xxx15[1]; 203 volatile u32 rquadx, rquady, rquadz; 204 u32 xxx16[5]; 205 volatile u32 quadr, quadg, quadb, quada; 206 volatile u32 arectx, arecty, arectz; 207 u32 xxx17[1]; 208 volatile u32 rrectx, rrecty, rrectz; 209 u32 xxx18[5]; 210 volatile u32 rectr, rectg, rectb, recta; 211}; 212 213static struct sbus_mmap_map cg6_mmap_map[] = { 214 { CG6_FBC, CG6_FBC_OFFSET, PAGE_SIZE }, 215 { CG6_TEC, CG6_TEC_OFFSET, PAGE_SIZE }, 216 { CG6_BTREGS, CG6_BROOKTREE_OFFSET, PAGE_SIZE }, 217 { CG6_FHC, CG6_FHC_OFFSET, PAGE_SIZE }, 218 { CG6_THC, CG6_THC_OFFSET, PAGE_SIZE }, 219 { CG6_ROM, CG6_ROM_OFFSET, 0x10000 }, 220 { CG6_RAM, CG6_RAM_OFFSET, SBUS_MMAP_FBSIZE(1) }, 221 { CG6_DHC, CG6_DHC_OFFSET, 0x40000 }, 222 { 0, 0, 0 } 223}; 224 225static void cg6_setup(struct display *p) 226{ 227 p->next_line = sbusfbinfo(p->fb_info)->var.xres_virtual; 228 p->next_plane = 0; 229} 230 231static void cg6_clear(struct vc_data *conp, struct display *p, int sy, int sx, 232 int height, int width) 233{ 234 struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info; 235 register struct cg6_fbc *fbc = fb->s.cg6.fbc; 236 unsigned long flags; 237 int x, y, w, h; 238 int i; 239 240 spin_lock_irqsave(&fb->lock, flags); 241 do { 242 i = sbus_readl(&fbc->s); 243 } while (i & 0x10000000); 244 sbus_writel(attr_bgcol_ec(p,conp), &fbc->fg); 245 sbus_writel(attr_bgcol_ec(p,conp), &fbc->bg); 246 sbus_writel(~0, &fbc->pixelm); 247 sbus_writel(0xea80ff00, &fbc->alu); 248 sbus_writel(0, &fbc->s); 249 sbus_writel(0, &fbc->clip); 250 sbus_writel(~0, &fbc->pm); 251 252 if (fontheightlog(p)) { 253 y = sy << fontheightlog(p); h = height << fontheightlog(p); 254 } else { 255 y = sy * fontheight(p); h = height * fontheight(p); 256 } 257 if (fontwidthlog(p)) { 258 x = sx << fontwidthlog(p); w = width << fontwidthlog(p); 259 } else { 260 x = sx * fontwidth(p); w = width * fontwidth(p); 261 } 262 sbus_writel(y + fb->y_margin, &fbc->arecty); 263 sbus_writel(x + fb->x_margin, &fbc->arectx); 264 sbus_writel(y + fb->y_margin + h, &fbc->arecty); 265 sbus_writel(x + fb->x_margin + w, &fbc->arectx); 266 do { 267 i = sbus_readl(&fbc->draw); 268 } while (i < 0 && (i & 0x20000000)); 269 spin_unlock_irqrestore(&fb->lock, flags); 270} 271 272static void cg6_fill(struct fb_info_sbusfb *fb, struct display *p, int s, 273 int count, unsigned short *boxes) 274{ 275 int i; 276 register struct cg6_fbc *fbc = fb->s.cg6.fbc; 277 unsigned long flags; 278 279 spin_lock_irqsave(&fb->lock, flags); 280 do { 281 i = sbus_readl(&fbc->s); 282 } while (i & 0x10000000); 283 sbus_writel(attr_bgcol(p,s), &fbc->fg); 284 sbus_writel(attr_bgcol(p,s), &fbc->bg); 285 sbus_writel(~0, &fbc->pixelm); 286 sbus_writel(0xea80ff00, &fbc->alu); 287 sbus_writel(0, &fbc->s); 288 sbus_writel(0, &fbc->clip); 289 sbus_writel(~0, &fbc->pm); 290 while (count-- > 0) { 291 sbus_writel(boxes[1], &fbc->arecty); 292 sbus_writel(boxes[0], &fbc->arectx); 293 sbus_writel(boxes[3], &fbc->arecty); 294 sbus_writel(boxes[2], &fbc->arectx); 295 boxes += 4; 296 do { 297 i = sbus_readl(&fbc->draw); 298 } while (i < 0 && (i & 0x20000000)); 299 } 300 spin_unlock_irqrestore(&fb->lock, flags); 301} 302 303static void cg6_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx) 304{ 305 struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info; 306 register struct cg6_fbc *fbc = fb->s.cg6.fbc; 307 unsigned long flags; 308 int i, x, y; 309 u8 *fd; 310 311 spin_lock_irqsave(&fb->lock, flags); 312 if (fontheightlog(p)) { 313 y = fb->y_margin + (yy << fontheightlog(p)); 314 i = ((c & p->charmask) << fontheightlog(p)); 315 } else { 316 y = fb->y_margin + (yy * fontheight(p)); 317 i = (c & p->charmask) * fontheight(p); 318 } 319 if (fontwidth(p) <= 8) 320 fd = p->fontdata + i; 321 else 322 fd = p->fontdata + (i << 1); 323 if (fontwidthlog(p)) 324 x = fb->x_margin + (xx << fontwidthlog(p)); 325 else 326 x = fb->x_margin + (xx * fontwidth(p)); 327 do { 328 i = sbus_readl(&fbc->s); 329 } while (i & 0x10000000); 330 sbus_writel(attr_fgcol(p,c), &fbc->fg); 331 sbus_writel(attr_bgcol(p,c), &fbc->bg); 332 sbus_writel(0x140000, &fbc->mode); 333 sbus_writel(0xe880fc30, &fbc->alu); 334 sbus_writel(~0, &fbc->pixelm); 335 sbus_writel(0, &fbc->s); 336 sbus_writel(0, &fbc->clip); 337 sbus_writel(0xff, &fbc->pm); 338 sbus_writel(0, &fbc->incx); 339 sbus_writel(1, &fbc->incy); 340 sbus_writel(x, &fbc->x0); 341 sbus_writel(x + fontwidth(p) - 1, &fbc->x1); 342 sbus_writel(y, &fbc->y0); 343 if (fontwidth(p) <= 8) { 344 for (i = 0; i < fontheight(p); i++) { 345 u32 val = *fd++ << 24; 346 sbus_writel(val, &fbc->font); 347 } 348 } else { 349 for (i = 0; i < fontheight(p); i++) { 350 u32 val = *(u16 *)fd << 16; 351 352 sbus_writel(val, &fbc->font); 353 fd += 2; 354 } 355 } 356 spin_unlock_irqrestore(&fb->lock, flags); 357} 358 359static void cg6_putcs(struct vc_data *conp, struct display *p, const unsigned short *s, 360 int count, int yy, int xx) 361{ 362 struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info; 363 register struct cg6_fbc *fbc = fb->s.cg6.fbc; 364 unsigned long flags; 365 int i, x, y; 366 u8 *fd1, *fd2, *fd3, *fd4; 367 u16 c; 368 369 spin_lock_irqsave(&fb->lock, flags); 370 do { 371 i = sbus_readl(&fbc->s); 372 } while (i & 0x10000000); 373 c = scr_readw(s); 374 sbus_writel(attr_fgcol(p, c), &fbc->fg); 375 sbus_writel(attr_bgcol(p, c), &fbc->bg); 376 sbus_writel(0x140000, &fbc->mode); 377 sbus_writel(0xe880fc30, &fbc->alu); 378 sbus_writel(~0, &fbc->pixelm); 379 sbus_writel(0, &fbc->s); 380 sbus_writel(0, &fbc->clip); 381 sbus_writel(0xff, &fbc->pm); 382 x = fb->x_margin; 383 y = fb->y_margin; 384 if (fontwidthlog(p)) 385 x += (xx << fontwidthlog(p)); 386 else 387 x += xx * fontwidth(p); 388 if (fontheightlog(p)) 389 y += (yy << fontheightlog(p)); 390 else 391 y += (yy * fontheight(p)); 392 if (fontwidth(p) <= 8) { 393 while (count >= 4) { 394 count -= 4; 395 sbus_writel(0, &fbc->incx); 396 sbus_writel(1, &fbc->incy); 397 sbus_writel(x, &fbc->x0); 398 sbus_writel((x += 4 * fontwidth(p)) - 1, &fbc->x1); 399 sbus_writel(y, &fbc->y0); 400 if (fontheightlog(p)) { 401 fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p)); 402 fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p)); 403 fd3 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p)); 404 fd4 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p)); 405 } else { 406 fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p)); 407 fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p)); 408 fd3 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p)); 409 fd4 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p)); 410 } 411 if (fontwidth(p) == 8) { 412 for (i = 0; i < fontheight(p); i++) { 413 u32 val = ((u32)*fd4++) | 414 ((((u32)*fd3++) | 415 ((((u32)*fd2++) | 416 (((u32)*fd1++) 417 << 8)) << 8)) << 8); 418 sbus_writel(val, &fbc->font); 419 } 420 } else { 421 for (i = 0; i < fontheight(p); i++) { 422 u32 val = (((u32)*fd4++) | 423 ((((u32)*fd3++) | 424 ((((u32)*fd2++) | 425 (((u32)*fd1++) 426 << fontwidth(p))) << 427 fontwidth(p))) << 428 fontwidth(p))) << 429 (24 - 3 * fontwidth(p)); 430 sbus_writel(val, &fbc->font); 431 } 432 } 433 } 434 } else { 435 while (count >= 2) { 436 count -= 2; 437 sbus_writel(0, &fbc->incx); 438 sbus_writel(1, &fbc->incy); 439 sbus_writel(x, &fbc->x0); 440 sbus_writel((x += 2 * fontwidth(p)) - 1, &fbc->x1); 441 sbus_writel(y, &fbc->y0); 442 if (fontheightlog(p)) { 443 fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) << (fontheightlog(p) + 1)); 444 fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) << (fontheightlog(p) + 1)); 445 } else { 446 fd1 = p->fontdata + (((scr_readw(s++) & p->charmask) * fontheight(p)) << 1); 447 fd2 = p->fontdata + (((scr_readw(s++) & p->charmask) * fontheight(p)) << 1); 448 } 449 for (i = 0; i < fontheight(p); i++) { 450 u32 val = ((((u32)*(u16 *)fd1) << fontwidth(p)) | 451 ((u32)*(u16 *)fd2)) << (16 - fontwidth(p)); 452 sbus_writel(val, &fbc->font); 453 fd1 += 2; fd2 += 2; 454 } 455 } 456 } 457 while (count) { 458 count--; 459 sbus_writel(0, &fbc->incx); 460 sbus_writel(1, &fbc->incy); 461 sbus_writel(x, &fbc->x0); 462 sbus_writel((x += fontwidth(p)) - 1, &fbc->x1); 463 sbus_writel(y, &fbc->y0); 464 if (fontheightlog(p)) 465 i = ((scr_readw(s++) & p->charmask) << fontheightlog(p)); 466 else 467 i = ((scr_readw(s++) & p->charmask) * fontheight(p)); 468 if (fontwidth(p) <= 8) { 469 fd1 = p->fontdata + i; 470 for (i = 0; i < fontheight(p); i++) { 471 u32 val = *fd1++ << 24; 472 sbus_writel(val, &fbc->font); 473 } 474 } else { 475 fd1 = p->fontdata + (i << 1); 476 for (i = 0; i < fontheight(p); i++) { 477 u32 val = *(u16 *)fd1 << 16; 478 sbus_writel(val, &fbc->font); 479 fd1 += 2; 480 } 481 } 482 } 483 spin_unlock_irqrestore(&fb->lock, flags); 484} 485 486static void cg6_revc(struct display *p, int xx, int yy) 487{ 488 /* Not used if hw cursor */ 489} 490 491static void cg6_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count) 492{ 493 struct bt_regs *bt = fb->s.cg6.bt; 494 unsigned long flags; 495 int i; 496 497 spin_lock_irqsave(&fb->lock, flags); 498 sbus_writel(index << 24, &bt->addr); 499 for (i = index; count--; i++){ 500 sbus_writel(fb->color_map CM(i,0) << 24, 501 &bt->color_map); 502 sbus_writel(fb->color_map CM(i,1) << 24, 503 &bt->color_map); 504 sbus_writel(fb->color_map CM(i,2) << 24, 505 &bt->color_map); 506 } 507 spin_unlock_irqrestore(&fb->lock, flags); 508} 509 510static void cg6_restore_palette (struct fb_info_sbusfb *fb) 511{ 512 struct bt_regs *bt = fb->s.cg6.bt; 513 unsigned long flags; 514 515 spin_lock_irqsave(&fb->lock, flags); 516 sbus_writel(0, &bt->addr); 517 sbus_writel(0xffffffff, &bt->color_map); 518 sbus_writel(0xffffffff, &bt->color_map); 519 sbus_writel(0xffffffff, &bt->color_map); 520 spin_unlock_irqrestore(&fb->lock, flags); 521} 522 523static struct display_switch cg6_dispsw __initdata = { 524 setup: cg6_setup, 525 bmove: fbcon_redraw_bmove, 526 clear: cg6_clear, 527 putc: cg6_putc, 528 putcs: cg6_putcs, 529 revc: cg6_revc, 530 fontwidthmask: FONTWIDTHRANGE(1,16) /* Allow fontwidths up to 16 */ 531}; 532 533static void cg6_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue) 534{ 535 struct bt_regs *bt = fb->s.cg6.bt; 536 unsigned long flags; 537 538 spin_lock_irqsave(&fb->lock, flags); 539 sbus_writel(1 << 24, &bt->addr); 540 sbus_writel(red[0] << 24, &bt->cursor); 541 sbus_writel(green[0] << 24, &bt->cursor); 542 sbus_writel(blue[0] << 24, &bt->cursor); 543 sbus_writel(3 << 24, &bt->addr); 544 sbus_writel(red[1] << 24, &bt->cursor); 545 sbus_writel(green[1] << 24, &bt->cursor); 546 sbus_writel(blue[1] << 24, &bt->cursor); 547 spin_unlock_irqrestore(&fb->lock, flags); 548} 549 550/* Set cursor shape */ 551static void cg6_setcurshape (struct fb_info_sbusfb *fb) 552{ 553 struct cg6_thc *thc = fb->s.cg6.thc; 554 unsigned long flags; 555 int i; 556 557 spin_lock_irqsave(&fb->lock, flags); 558 for (i = 0; i < 32; i++) { 559 sbus_writel(fb->cursor.bits[0][i], 560 &thc->thc_cursmask [i]); 561 sbus_writel(fb->cursor.bits[1][i], 562 &thc->thc_cursbits [i]); 563 } 564 spin_unlock_irqrestore(&fb->lock, flags); 565} 566 567/* Load cursor information */ 568static void cg6_setcursor (struct fb_info_sbusfb *fb) 569{ 570 unsigned int v; 571 unsigned long flags; 572 struct cg_cursor *c = &fb->cursor; 573 574 spin_lock_irqsave(&fb->lock, flags); 575 if (c->enable) 576 v = ((c->cpos.fbx - c->chot.fbx) << 16) 577 |((c->cpos.fby - c->chot.fby) & 0xffff); 578 else 579 /* Magic constant to turn off the cursor */ 580 v = ((65536-32) << 16) | (65536-32); 581 sbus_writel(v, &fb->s.cg6.thc->thc_cursxy); 582 spin_unlock_irqrestore(&fb->lock, flags); 583} 584 585static void cg6_blank (struct fb_info_sbusfb *fb) 586{ 587 unsigned long flags; 588 u32 tmp; 589 590 spin_lock_irqsave(&fb->lock, flags); 591 tmp = sbus_readl(&fb->s.cg6.thc->thc_misc); 592 tmp &= ~CG6_THC_MISC_VIDEO; 593 sbus_writel(tmp, &fb->s.cg6.thc->thc_misc); 594 spin_unlock_irqrestore(&fb->lock, flags); 595} 596 597static void cg6_unblank (struct fb_info_sbusfb *fb) 598{ 599 unsigned long flags; 600 u32 tmp; 601 602 spin_lock_irqsave(&fb->lock, flags); 603 tmp = sbus_readl(&fb->s.cg6.thc->thc_misc); 604 tmp |= CG6_THC_MISC_VIDEO; 605 sbus_writel(tmp, &fb->s.cg6.thc->thc_misc); 606 spin_unlock_irqrestore(&fb->lock, flags); 607} 608 609static void cg6_reset (struct fb_info_sbusfb *fb) 610{ 611 unsigned int rev, conf; 612 struct cg6_tec *tec = fb->s.cg6.tec; 613 struct cg6_fbc *fbc = fb->s.cg6.fbc; 614 unsigned long flags; 615 u32 mode, tmp; 616 int i; 617 618 spin_lock_irqsave(&fb->lock, flags); 619 620 /* Turn off stuff in the Transform Engine. */ 621 sbus_writel(0, &tec->tec_matrix); 622 sbus_writel(0, &tec->tec_clip); 623 sbus_writel(0, &tec->tec_vdc); 624 625 /* Take care of bugs in old revisions. */ 626 rev = (sbus_readl(fb->s.cg6.fhc) >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK; 627 if (rev < 5) { 628 conf = (sbus_readl(fb->s.cg6.fhc) & CG6_FHC_RES_MASK) | 629 CG6_FHC_CPU_68020 | CG6_FHC_TEST | 630 (11 << CG6_FHC_TEST_X_SHIFT) | 631 (11 << CG6_FHC_TEST_Y_SHIFT); 632 if (rev < 2) 633 conf |= CG6_FHC_DST_DISABLE; 634 sbus_writel(conf, fb->s.cg6.fhc); 635 } 636 637 /* Set things in the FBC. Bad things appear to happen if we do 638 * back to back store/loads on the mode register, so copy it 639 * out instead. */ 640 mode = sbus_readl(&fbc->mode); 641 do { 642 i = sbus_readl(&fbc->s); 643 } while (i & 0x10000000); 644 mode &= ~(CG6_FBC_BLIT_MASK | CG6_FBC_MODE_MASK | 645 CG6_FBC_DRAW_MASK | CG6_FBC_BWRITE0_MASK | 646 CG6_FBC_BWRITE1_MASK | CG6_FBC_BREAD_MASK | 647 CG6_FBC_BDISP_MASK); 648 mode |= (CG6_FBC_BLIT_SRC | CG6_FBC_MODE_COLOR8 | 649 CG6_FBC_DRAW_RENDER | CG6_FBC_BWRITE0_ENABLE | 650 CG6_FBC_BWRITE1_DISABLE | CG6_FBC_BREAD_0 | 651 CG6_FBC_BDISP_0); 652 sbus_writel(mode, &fbc->mode); 653 654 sbus_writel(0, &fbc->clip); 655 sbus_writel(0, &fbc->offx); 656 sbus_writel(0, &fbc->offy); 657 sbus_writel(0, &fbc->clipminx); 658 sbus_writel(0, &fbc->clipminy); 659 sbus_writel(fb->type.fb_width - 1, &fbc->clipmaxx); 660 sbus_writel(fb->type.fb_height - 1, &fbc->clipmaxy); 661 662 /* Enable cursor in Brooktree DAC. */ 663 sbus_writel(0x06 << 24, &fb->s.cg6.bt->addr); 664 tmp = sbus_readl(&fb->s.cg6.bt->control); 665 tmp |= 0x03 << 24; 666 sbus_writel(tmp, &fb->s.cg6.bt->control); 667 668 spin_unlock_irqrestore(&fb->lock, flags); 669} 670 671static void cg6_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin) 672{ 673 p->screen_base += (y_margin - fb->y_margin) * 674 p->line_length + (x_margin - fb->x_margin); 675} 676 677static int __init cg6_rasterimg (struct fb_info *info, int start) 678{ 679 struct fb_info_sbusfb *fb = sbusfbinfo(info); 680 register struct cg6_fbc *fbc = fb->s.cg6.fbc; 681 int i; 682 683 do { 684 i = sbus_readl(&fbc->s); 685 } while (i & 0x10000000); 686 return 0; 687} 688 689static char idstring[70] __initdata = { 0 }; 690 691char __init *cgsixfb_init(struct fb_info_sbusfb *fb) 692{ 693 struct fb_fix_screeninfo *fix = &fb->fix; 694 struct fb_var_screeninfo *var = &fb->var; 695 struct display *disp = &fb->disp; 696 struct fbtype *type = &fb->type; 697 struct sbus_dev *sdev = fb->sbdp; 698 unsigned long phys = sdev->reg_addrs[0].phys_addr; 699 u32 conf; 700 char *p; 701 char *cardtype; 702 struct bt_regs *bt; 703 struct fb_ops *fbops; 704 705 fbops = kmalloc(sizeof(*fbops), GFP_KERNEL); 706 if (fbops == NULL) 707 return NULL; 708 709 *fbops = *fb->info.fbops; 710 fbops->fb_rasterimg = cg6_rasterimg; 711 fb->info.fbops = fbops; 712 713 if (prom_getbool (fb->prom_node, "dblbuf")) { 714 type->fb_size *= 4; 715 fix->smem_len *= 4; 716 } 717 718 fix->line_length = fb->var.xres_virtual; 719 fix->accel = FB_ACCEL_SUN_CGSIX; 720 721 var->accel_flags = FB_ACCELF_TEXT; 722 723 disp->scrollmode = SCROLL_YREDRAW; 724 if (!disp->screen_base) { 725 disp->screen_base = (char *) 726 sbus_ioremap(&sdev->resource[0], CG6_RAM_OFFSET, 727 type->fb_size, "cgsix ram"); 728 } 729 disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin; 730 fb->s.cg6.fbc = (struct cg6_fbc *) 731 sbus_ioremap(&sdev->resource[0], CG6_FBC_OFFSET, 732 4096, "cgsix fbc"); 733 fb->s.cg6.tec = (struct cg6_tec *) 734 sbus_ioremap(&sdev->resource[0], CG6_TEC_OFFSET, 735 sizeof(struct cg6_tec), "cgsix tec"); 736 fb->s.cg6.thc = (struct cg6_thc *) 737 sbus_ioremap(&sdev->resource[0], CG6_THC_OFFSET, 738 sizeof(struct cg6_thc), "cgsix thc"); 739 fb->s.cg6.bt = bt = (struct bt_regs *) 740 sbus_ioremap(&sdev->resource[0], CG6_BROOKTREE_OFFSET, 741 sizeof(struct bt_regs), "cgsix dac"); 742 fb->s.cg6.fhc = (u32 *) 743 sbus_ioremap(&sdev->resource[0], CG6_FHC_OFFSET, 744 sizeof(u32), "cgsix fhc"); 745 fb->dispsw = cg6_dispsw; 746 747 fb->margins = cg6_margins; 748 fb->loadcmap = cg6_loadcmap; 749 fb->setcursor = cg6_setcursor; 750 fb->setcursormap = cg6_setcursormap; 751 fb->setcurshape = cg6_setcurshape; 752 fb->restore_palette = cg6_restore_palette; 753 fb->fill = cg6_fill; 754 fb->blank = cg6_blank; 755 fb->unblank = cg6_unblank; 756 fb->reset = cg6_reset; 757 758 fb->physbase = phys; 759 fb->mmap_map = cg6_mmap_map; 760 761 /* Initialize Brooktree DAC */ 762 sbus_writel(0x04 << 24, &bt->addr); /* color planes */ 763 sbus_writel(0xff << 24, &bt->control); 764 sbus_writel(0x05 << 24, &bt->addr); 765 sbus_writel(0x00 << 24, &bt->control); 766 sbus_writel(0x06 << 24, &bt->addr); /* overlay plane */ 767 sbus_writel(0x73 << 24, &bt->control); 768 sbus_writel(0x07 << 24, &bt->addr); 769 sbus_writel(0x00 << 24, &bt->control); 770 771 conf = sbus_readl(fb->s.cg6.fhc); 772 switch(conf & CG6_FHC_CPU_MASK) { 773 case CG6_FHC_CPU_SPARC: p = "sparc"; break; 774 case CG6_FHC_CPU_68020: p = "68020"; break; 775 default: p = "i386"; break; 776 } 777 778 if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) { 779 if (fix->smem_len <= 0x100000) { 780 cardtype = "TGX"; 781 } else { 782 cardtype = "TGX+"; 783 } 784 } else { 785 if (fix->smem_len <= 0x100000) { 786 cardtype = "GX"; 787 } else { 788 cardtype = "GX+"; 789 } 790 } 791 792 sprintf(idstring, 793#ifdef __sparc_v9__ 794 "cgsix at %016lx TEC Rev %x CPU %s Rev %x [%s]", phys, 795#else 796 "cgsix at %x.%08lx TEC Rev %x CPU %s Rev %x [%s]", 797 fb->iospace, phys, 798#endif 799 ((sbus_readl(&fb->s.cg6.thc->thc_misc) >> CG6_THC_MISC_REV_SHIFT) & 800 CG6_THC_MISC_REV_MASK), 801 p, (conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK, cardtype); 802 803 sprintf(fb->info.modename, "CGsix [%s]", cardtype); 804 sprintf(fix->id, "CGsix [%s]", cardtype); 805 806 cg6_reset(fb); 807 808 return idstring; 809} 810