1/* $Id: tcxfb.c,v 1.1.1.1 2008/10/15 03:27:05 james26_jang Exp $ 2 * tcxfb.c: TCX 24/8bit 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#include <asm/sbus.h> 27 28#include <video/fbcon-cfb8.h> 29 30/* THC definitions */ 31#define TCX_THC_MISC_REV_SHIFT 16 32#define TCX_THC_MISC_REV_MASK 15 33#define TCX_THC_MISC_VSYNC_DIS (1 << 25) 34#define TCX_THC_MISC_HSYNC_DIS (1 << 24) 35#define TCX_THC_MISC_RESET (1 << 12) 36#define TCX_THC_MISC_VIDEO (1 << 10) 37#define TCX_THC_MISC_SYNC (1 << 9) 38#define TCX_THC_MISC_VSYNC (1 << 8) 39#define TCX_THC_MISC_SYNC_ENAB (1 << 7) 40#define TCX_THC_MISC_CURS_RES (1 << 6) 41#define TCX_THC_MISC_INT_ENAB (1 << 5) 42#define TCX_THC_MISC_INT (1 << 4) 43#define TCX_THC_MISC_INIT 0x9f 44#define TCX_THC_REV_REV_SHIFT 20 45#define TCX_THC_REV_REV_MASK 15 46#define TCX_THC_REV_MINREV_SHIFT 28 47#define TCX_THC_REV_MINREV_MASK 15 48 49/* The contents are unknown */ 50struct tcx_tec { 51 volatile u32 tec_matrix; 52 volatile u32 tec_clip; 53 volatile u32 tec_vdc; 54}; 55 56struct tcx_thc { 57 volatile u32 thc_rev; 58 u32 thc_pad0[511]; 59 volatile u32 thc_hs; /* hsync timing */ 60 volatile u32 thc_hsdvs; 61 volatile u32 thc_hd; 62 volatile u32 thc_vs; /* vsync timing */ 63 volatile u32 thc_vd; 64 volatile u32 thc_refresh; 65 volatile u32 thc_misc; 66 u32 thc_pad1[56]; 67 volatile u32 thc_cursxy; /* cursor x,y position (16 bits each) */ 68 volatile u32 thc_cursmask[32]; /* cursor mask bits */ 69 volatile u32 thc_cursbits[32]; /* what to show where mask enabled */ 70}; 71 72static struct sbus_mmap_map tcx_mmap_map[] = { 73 { TCX_RAM8BIT, 0, SBUS_MMAP_FBSIZE(1) }, 74 { TCX_RAM24BIT, 0, SBUS_MMAP_FBSIZE(4) }, 75 { TCX_UNK3, 0, SBUS_MMAP_FBSIZE(8) }, 76 { TCX_UNK4, 0, SBUS_MMAP_FBSIZE(8) }, 77 { TCX_CONTROLPLANE, 0, SBUS_MMAP_FBSIZE(4) }, 78 { TCX_UNK6, 0, SBUS_MMAP_FBSIZE(8) }, 79 { TCX_UNK7, 0, SBUS_MMAP_FBSIZE(8) }, 80 { TCX_TEC, 0, PAGE_SIZE }, 81 { TCX_BTREGS, 0, PAGE_SIZE }, 82 { TCX_THC, 0, PAGE_SIZE }, 83 { TCX_DHC, 0, PAGE_SIZE }, 84 { TCX_ALT, 0, PAGE_SIZE }, 85 { TCX_UNK2, 0, 0x20000 }, 86 { 0, 0, 0 } 87}; 88 89static void __tcx_set_control_plane (struct fb_info_sbusfb *fb) 90{ 91 u32 *p, *pend; 92 93 p = fb->s.tcx.cplane; 94 if (p == NULL) 95 return; 96 for (pend = p + fb->type.fb_size; p < pend; p++) { 97 u32 tmp = sbus_readl(p); 98 99 tmp &= 0xffffff; 100 sbus_writel(tmp, p); 101 } 102} 103 104static void tcx_switch_from_graph (struct fb_info_sbusfb *fb) 105{ 106 unsigned long flags; 107 108 spin_lock_irqsave(&fb->lock, flags); 109 110 /* Reset control plane to 8bit mode if necessary */ 111 if (fb->open && fb->mmaped) 112 __tcx_set_control_plane (fb); 113 114 spin_unlock_irqrestore(&fb->lock, flags); 115} 116 117static void tcx_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count) 118{ 119 struct bt_regs *bt = fb->s.tcx.bt; 120 unsigned long flags; 121 int i; 122 123 spin_lock_irqsave(&fb->lock, flags); 124 sbus_writel(index << 24, &bt->addr); 125 for (i = index; count--; i++){ 126 sbus_writel(fb->color_map CM(i,0) << 24, &bt->color_map); 127 sbus_writel(fb->color_map CM(i,1) << 24, &bt->color_map); 128 sbus_writel(fb->color_map CM(i,2) << 24, &bt->color_map); 129 } 130 sbus_writel(0, &bt->addr); 131 spin_unlock_irqrestore(&fb->lock, flags); 132} 133 134static void tcx_restore_palette (struct fb_info_sbusfb *fb) 135{ 136 struct bt_regs *bt = fb->s.tcx.bt; 137 unsigned long flags; 138 139 spin_lock_irqsave(&fb->lock, flags); 140 sbus_writel(0, &bt->addr); 141 sbus_writel(0xffffffff, &bt->color_map); 142 sbus_writel(0xffffffff, &bt->color_map); 143 sbus_writel(0xffffffff, &bt->color_map); 144 spin_unlock_irqrestore(&fb->lock, flags); 145} 146 147static void tcx_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue) 148{ 149 struct bt_regs *bt = fb->s.tcx.bt; 150 unsigned long flags; 151 152 spin_lock_irqsave(&fb->lock, flags); 153 154 /* Note the 2 << 24 is different from cg6's 1 << 24 */ 155 sbus_writel(2 << 24, &bt->addr); 156 sbus_writel(red[0] << 24, &bt->cursor); 157 sbus_writel(green[0] << 24, &bt->cursor); 158 sbus_writel(blue[0] << 24, &bt->cursor); 159 sbus_writel(3 << 24, &bt->addr); 160 sbus_writel(red[1] << 24, &bt->cursor); 161 sbus_writel(green[1] << 24, &bt->cursor); 162 sbus_writel(blue[1] << 24, &bt->cursor); 163 sbus_writel(0, &bt->addr); 164 165 spin_unlock_irqrestore(&fb->lock, flags); 166} 167 168/* Set cursor shape */ 169static void tcx_setcurshape (struct fb_info_sbusfb *fb) 170{ 171 struct tcx_thc *thc = fb->s.tcx.thc; 172 unsigned long flags; 173 int i; 174 175 spin_lock_irqsave(&fb->lock, flags); 176 for (i = 0; i < 32; i++){ 177 sbus_writel(fb->cursor.bits[0][i], &thc->thc_cursmask[i]); 178 sbus_writel(fb->cursor.bits[1][i], &thc->thc_cursbits[i]); 179 } 180 spin_unlock_irqrestore(&fb->lock, flags); 181} 182 183/* Load cursor information */ 184static void tcx_setcursor (struct fb_info_sbusfb *fb) 185{ 186 struct cg_cursor *c = &fb->cursor; 187 unsigned long flags; 188 unsigned int v; 189 190 spin_lock_irqsave(&fb->lock, flags); 191 if (c->enable) 192 v = ((c->cpos.fbx - c->chot.fbx) << 16) 193 |((c->cpos.fby - c->chot.fby) & 0xffff); 194 else 195 /* Magic constant to turn off the cursor */ 196 v = ((65536-32) << 16) | (65536-32); 197 sbus_writel(v, &fb->s.tcx.thc->thc_cursxy); 198 spin_unlock_irqrestore(&fb->lock, flags); 199} 200 201static void tcx_blank (struct fb_info_sbusfb *fb) 202{ 203 unsigned long flags; 204 u32 tmp; 205 206 spin_lock_irqsave(&fb->lock, flags); 207 tmp = sbus_readl(&fb->s.tcx.thc->thc_misc); 208 tmp &= ~TCX_THC_MISC_VIDEO; 209 /* This should put us in power-save */ 210 tmp |= TCX_THC_MISC_VSYNC_DIS; 211 tmp |= TCX_THC_MISC_HSYNC_DIS; 212 sbus_writel(tmp, &fb->s.tcx.thc->thc_misc); 213 spin_unlock_irqrestore(&fb->lock, flags); 214} 215 216static void tcx_unblank (struct fb_info_sbusfb *fb) 217{ 218 unsigned long flags; 219 u32 tmp; 220 221 spin_lock_irqsave(&fb->lock, flags); 222 tmp = sbus_readl(&fb->s.tcx.thc->thc_misc); 223 tmp &= ~TCX_THC_MISC_VSYNC_DIS; 224 tmp &= ~TCX_THC_MISC_HSYNC_DIS; 225 tmp |= TCX_THC_MISC_VIDEO; 226 sbus_writel(tmp, &fb->s.tcx.thc->thc_misc); 227 spin_unlock_irqrestore(&fb->lock, flags); 228} 229 230static void tcx_reset (struct fb_info_sbusfb *fb) 231{ 232 unsigned long flags; 233 u32 tmp; 234 235 spin_lock_irqsave(&fb->lock, flags); 236 if (fb->open && fb->mmaped) 237 __tcx_set_control_plane(fb); 238 239 /* Turn off stuff in the Transform Engine. */ 240 sbus_writel(0, &fb->s.tcx.tec->tec_matrix); 241 sbus_writel(0, &fb->s.tcx.tec->tec_clip); 242 sbus_writel(0, &fb->s.tcx.tec->tec_vdc); 243 244 /* Enable cursor in Brooktree DAC. */ 245 sbus_writel(0x06 << 24, &fb->s.tcx.bt->addr); 246 tmp = sbus_readl(&fb->s.tcx.bt->control); 247 tmp |= 0x03 << 24; 248 sbus_writel(tmp, &fb->s.tcx.bt->control); 249 spin_unlock_irqrestore(&fb->lock, flags); 250} 251 252static void tcx_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin) 253{ 254 p->screen_base += (y_margin - fb->y_margin) * p->line_length + (x_margin - fb->x_margin); 255} 256 257static char idstring[60] __initdata = { 0 }; 258 259char __init *tcxfb_init(struct fb_info_sbusfb *fb) 260{ 261 struct fb_fix_screeninfo *fix = &fb->fix; 262 struct display *disp = &fb->disp; 263 struct fbtype *type = &fb->type; 264 struct sbus_dev *sdev = fb->sbdp; 265 unsigned long phys = sdev->reg_addrs[0].phys_addr; 266 int lowdepth, i, j; 267 268#ifndef FBCON_HAS_CFB8 269 return NULL; 270#endif 271 272 lowdepth = prom_getbool (fb->prom_node, "tcx-8-bit"); 273 274 if (lowdepth) { 275 strcpy(fb->info.modename, "TCX8"); 276 strcpy(fix->id, "TCX8"); 277 } else { 278 strcpy(fb->info.modename, "TCX24"); 279 strcpy(fix->id, "TCX24"); 280 } 281 fix->line_length = fb->var.xres_virtual; 282 fix->accel = FB_ACCEL_SUN_TCX; 283 284 disp->scrollmode = SCROLL_YREDRAW; 285 if (!disp->screen_base) { 286 disp->screen_base = (char *) 287 sbus_ioremap(&sdev->resource[0], 0, 288 type->fb_size, "tcx ram"); 289 } 290 disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin; 291 fb->s.tcx.tec = (struct tcx_tec *) 292 sbus_ioremap(&sdev->resource[7], 0, 293 sizeof(struct tcx_tec), "tcx tec"); 294 fb->s.tcx.thc = (struct tcx_thc *) 295 sbus_ioremap(&sdev->resource[9], 0, 296 sizeof(struct tcx_thc), "tcx thc"); 297 fb->s.tcx.bt = (struct bt_regs *) 298 sbus_ioremap(&sdev->resource[8], 0, 299 sizeof(struct bt_regs), "tcx dac"); 300 if (!lowdepth) { 301 fb->s.tcx.cplane = (u32 *) 302 sbus_ioremap(&sdev->resource[4], 0, 303 type->fb_size * sizeof(u32), "tcx cplane"); 304 type->fb_depth = 24; 305 fb->switch_from_graph = tcx_switch_from_graph; 306 } else { 307 /* As there can be one tcx in a machine only, we can write directly into 308 tcx_mmap_map */ 309 tcx_mmap_map[1].size = SBUS_MMAP_EMPTY; 310 tcx_mmap_map[4].size = SBUS_MMAP_EMPTY; 311 tcx_mmap_map[5].size = SBUS_MMAP_EMPTY; 312 tcx_mmap_map[6].size = SBUS_MMAP_EMPTY; 313 } 314 fb->dispsw = fbcon_cfb8; 315 316 fb->margins = tcx_margins; 317 fb->loadcmap = tcx_loadcmap; 318 if (prom_getbool (fb->prom_node, "hw-cursor")) { 319 fb->setcursor = tcx_setcursor; 320 fb->setcursormap = tcx_setcursormap; 321 fb->setcurshape = tcx_setcurshape; 322 } 323 fb->restore_palette = tcx_restore_palette; 324 fb->blank = tcx_blank; 325 fb->unblank = tcx_unblank; 326 fb->reset = tcx_reset; 327 328 fb->physbase = 0; 329 for (i = 0; i < 13; i++) { 330 /* tcx_mmap_map has to be sorted by voff, while 331 order of phys registers from PROM differs a little 332 bit. Here is the correction */ 333 switch (i) { 334 case 10: j = 12; break; 335 case 11: 336 case 12: j = i - 1; break; 337 default: j = i; break; 338 } 339 tcx_mmap_map[i].poff = fb->sbdp->reg_addrs[j].phys_addr; 340 } 341 fb->mmap_map = tcx_mmap_map; 342 343 /* Initialize Brooktree DAC */ 344 sbus_writel(0x04 << 24, &fb->s.tcx.bt->addr); /* color planes */ 345 sbus_writel(0xff << 24, &fb->s.tcx.bt->control); 346 sbus_writel(0x05 << 24, &fb->s.tcx.bt->addr); 347 sbus_writel(0x00 << 24, &fb->s.tcx.bt->control); 348 sbus_writel(0x06 << 24, &fb->s.tcx.bt->addr); /* overlay plane */ 349 sbus_writel(0x73 << 24, &fb->s.tcx.bt->control); 350 sbus_writel(0x07 << 24, &fb->s.tcx.bt->addr); 351 sbus_writel(0x00 << 24, &fb->s.tcx.bt->control); 352 353 sprintf(idstring, "tcx at %x.%08lx Rev %d.%d %s", 354 fb->iospace, phys, 355 ((sbus_readl(&fb->s.tcx.thc->thc_rev) >> TCX_THC_REV_REV_SHIFT) & 356 TCX_THC_REV_REV_MASK), 357 ((sbus_readl(&fb->s.tcx.thc->thc_rev) >> TCX_THC_REV_MINREV_SHIFT) & 358 TCX_THC_REV_MINREV_MASK), 359 lowdepth ? "8-bit only" : "24-bit depth"); 360 361 tcx_reset(fb); 362 363 return idstring; 364} 365 366MODULE_LICENSE("GPL"); 367