1 #include "grf.h" 2 #if NGRF > 0 3 4 /* Graphics routines for the AMIGA native custom chip set. */ 5 6 #include "sys/param.h" 7 #include "sys/errno.h" 8 9 #include "grfioctl.h" 10 #include "grfvar.h" 11 #include "grf_ccreg.h" 12 13 #include "../include/cpu.h" 14 #include "../amiga/custom.h" 15 16 17 extern caddr_t CHIPMEMADDR; 18 extern caddr_t chipmem_steal (); 19 20 struct ccfb ccfb = { 21 DEF_DISP_WIDTH, DEF_DISP_HEIGHT, DEF_DISP_X, DEF_DISP_Y, DEF_DISP_Z, 22 0, 23 DEF_FB_WIDTH, DEF_FB_HEIGHT, DEF_FB_X, DEF_FB_Y, DEF_FB_Z, 24 DEF_COL0, DEF_COL1 25 }; 26 27 /* Initialize one copper list. We'll need two to make a nice interlaced display. */ 28 29 /* maximum size needed for a copper list (4 planes hires laced) */ 30 #define COPENTS (4 * (2 * 4 + 2 + 1 + 2 + 2 + 2 + 16 * 1 + 1)) 31 32 /* copper instructions */ 33 #define MOVE(cl, reg, val) \ 34 do { *cl++ = CUSTOM_OFS(reg); *cl++ = val; } while (0) 35 #define WAIT(cl, vp, hp, bfd, ve, he) \ 36 do { *cl++ = ((vp & 0xff)<<8)|(hp & 0xfe)|1; \ 37 *cl++ = (bfd<<15)|((ve & 0x7f)<<8)|(hp & 0xfe)|1; } while (0) 38 #define STOP(cl) \ 39 do { *cl++ = 0xffff; *cl++ = 0xffff; } while (0) 40 41 static void 42 initcop (cop, othercop, shf, fb) 43 u_short *cop; 44 u_short *othercop; 45 int shf; 46 struct ccfb *fb; 47 { 48 long scrmem; 49 int i; 50 u_short c, strt, stop, *orig_cop = cop; 51 52 /* get PA of display area */ 53 scrmem = (long) fb->fb - (long) CHIPMEMADDR; 54 55 othercop = (u_short *) ((long)othercop - (long) CHIPMEMADDR); 56 57 /* account for possible interlaced half-frame */ 58 if (shf) 59 scrmem += fb->fb_width >> 3; 60 61 /* account for oversized framebuffers */ 62 scrmem += (fb->fb_x >> 3) + (fb->fb_y * (fb->fb_width >> 3)); 63 64 /* initialize bitplane pointers for all planes */ 65 /* remember offset in copperlist to patch later */ 66 if (! fb->bplstart_off) 67 fb->bplstart_off = cop - orig_cop; 68 for (i = 0; i < fb->disp_z; i++) 69 { 70 MOVE (cop, bplpth(i), scrmem >> 16); 71 MOVE (cop, bplptl(i), scrmem & 0xffff); 72 scrmem += (fb->fb_width >> 3) * fb->fb_height; 73 } 74 75 /* modulo is one line for interlaced displays, plus difference between 76 virtual and effective framebuffer size */ 77 MOVE (cop, bpl1mod, (fb->fb_width + (fb->fb_width - fb->disp_width)) >> 3); 78 MOVE (cop, bpl2mod, (fb->fb_width + (fb->fb_width - fb->disp_width)) >> 3); 79 80 c = 0x8000 /* HIRES */ 81 | ((fb->disp_z & 7) << 12) /* bitplane use */ 82 | 0x0200 /* composite COLOR enable (whatever this is..) */ 83 | 0x0004; /* LACE */ 84 85 MOVE (cop, bplcon0, c); 86 87 /* these use pre-ECS register interpretation. Might want to go ECS ? */ 88 strt = (((fb->disp_y >> 1) & 0xff)<<8) | ((fb->disp_x >> 1) & 0xff); 89 MOVE (cop, diwstrt, strt); 90 stop = (((((fb->disp_y + fb->disp_height + 1-shf)>>1) & 0xff)<<8) 91 | (((fb->disp_x + fb->disp_width)>>1) & 0xff)); 92 MOVE (cop, diwstop, stop); 93 /* NOTE: default values for strt: 0x2c81, stop: 0xf4c1 */ 94 95 /* these are from from HW-manual.. */ 96 strt = ((strt & 0xff) - 9) >> 1; 97 MOVE (cop, ddfstrt, strt); 98 stop = strt + (((fb->disp_width >> 4) - 2) << 2); 99 MOVE (cop, ddfstop, stop); 100 101 /* setup interlaced display by constantly toggling between two copperlists */ 102 MOVE (cop, cop1lch, (long)othercop >> 16); 103 MOVE (cop, cop1lcl, (long)othercop & 0xffff); 104 105 for (i = 0; i < (1 << fb->disp_z); i++) 106 MOVE (cop, color[i], fb->col[i]); 107 108 /* wait forever */ 109 STOP (cop); 110 } 111 112 113 #ifdef DEBUG 114 void 115 dump_copperlist (cl) 116 u_int *cl; 117 { 118 while (*cl != 0xffffffff) 119 { 120 if (!(*cl & 0x00010000)) 121 printf ("MOVE (%x, %x)\t", *cl & 0xffff, *cl >> 16); 122 else 123 printf ("WAIT (%d, %d, %d, %d, %d)\t", *cl >> 24, (*cl & 0x00fe0000)>>16, 124 (*cl & 0x8000)>> 15, (*cl & 0x7f00)>>8, (*cl & 0xfe)); 125 cl++; 126 } 127 printf ("STOP ()\n"); 128 129 } 130 #endif 131 132 133 /* 134 * Initialize hardware. 135 * Must point g_display at a grfinfo structure describing the hardware. 136 * Returns 0 if hardware not present, non-zero ow. 137 */ 138 cc_init(gp, ad) 139 struct grf_softc *gp; 140 struct amiga_device *ad; 141 { 142 register struct ccfb *fb = &ccfb; 143 struct grfinfo *gi = &gp->g_display; 144 u_char *fbp, save; 145 int fboff, fbsize; 146 int s; 147 148 /* if already initialized, fail */ 149 if (fb->fb) 150 return 0; 151 152 /* testing for the result is really redundant because chipmem_steal 153 panics if it runs out of memory.. */ 154 fbsize = (fb->fb_width >> 3) * fb->fb_height * fb->fb_z; 155 if (! (fb->fb = (u_char *) chipmem_steal (fbsize)) 156 || !(fb->cop1 = (u_short *) chipmem_steal (COPENTS)) 157 || !(fb->cop2 = (u_short *) chipmem_steal (COPENTS))) 158 return 0; 159 160 /* clear the display. bzero only likes regions up to 64k, so call multiple times */ 161 for (fboff = 0; fboff < fbsize; fboff += 64*1024) 162 bzero (fb->fb + fboff, fbsize - fboff > 64*1024 ? 64*1024 : fbsize - fboff); 163 164 initcop (fb->cop1, fb->cop2, 0, fb); 165 initcop (fb->cop2, fb->cop1, 1, fb); 166 167 /* Make sure no ex-sprites are streaking down the screen */ 168 { 169 int i; 170 for(i = 0;i < 8;i++) 171 { 172 custom.spr[i].data = 0; 173 custom.spr[i].datb = 0; 174 } 175 } 176 177 /* start the new display */ 178 /* disable these */ 179 custom.dmacon = (DMAF_BLTDONE | DMAF_BLTNZERO | DMAF_BLITHOG | DMAF_BLITTER 180 | DMAF_SPRITE | DMAF_DISK 181 | DMAF_AUD3 | DMAF_AUD2 | DMAF_AUD1 | DMAF_AUD0); 182 /* enable these */ 183 custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER; 184 185 #if 0 186 /* ok, this is a bit rough.. */ 187 s = splhigh (); 188 /* load a first guess copperlist, verify later whether we got the right 189 one */ 190 custom.cop1lc = (void *) ((long)fb->cop1 - (long) CHIPMEMADDR); 191 custom.copjmp1 = 0; 192 /* reset VBL */ 193 custom.intreq = INTF_VERTB; 194 /* wait for VBL */ 195 while (! (custom.intreqr & INTF_VERTB)) ; 196 /* reset VBL */ 197 custom.intreq = INTF_VERTB; 198 /* now, in a safe location, set correct copperlist based on longframe/shortframe 199 bit */ 200 if (custom.vposr & 0x8000) 201 { 202 custom.cop1lc = (void *) ((long)fb->cop1 - (long) CHIPMEMADDR); 203 custom.copjmp1 = 0; /* strobe it */ 204 } 205 else 206 { 207 custom.cop1lc = (void *) ((long)fb->cop2 - (long) CHIPMEMADDR); 208 custom.copjmp1 = 0; 209 } 210 /* wait for another VBL and reset int, then go back to previous int level */ 211 while (! (custom.intreqr & INTF_VERTB)) ; 212 custom.intreq = INTF_VERTB; 213 splx (s); 214 #else 215 s = splhigh(); 216 /* set up copper */ 217 custom.cop1lc = (void *) ((long)fb->cop1 - (long) CHIPMEMADDR); 218 custom.copjmp1 = 0; 219 220 /* reset vertical blank interrupt */ 221 custom.intreq = INTF_VERTB; 222 223 /* wait for vertical blank interrupt */ 224 while ((custom.intreqr & INTF_VERTB) != INTF_VERTB) 225 ; 226 227 /* set bitplane pointers based on LOF/SHF bit */ 228 if (custom.vposr & 0x8000) 229 { 230 custom.cop1lc = (void *) ((long)fb->cop1 - (long) CHIPMEMADDR); 231 custom.copjmp1 = 0; 232 } 233 else 234 { 235 custom.cop1lc = (void *) ((long)fb->cop2 - (long) CHIPMEMADDR); 236 custom.copjmp1 = 0; 237 } 238 splx (s); 239 #endif 240 241 /* tame the blitter. Copying one word onto itself should put it into 242 a consistent state. This is black magic... */ 243 custom.bltapt = 244 custom.bltbpt = 245 custom.bltcpt = 246 custom.bltdpt = 0; 247 custom.bltamod = 248 custom.bltbmod = 249 custom.bltcmod = 250 custom.bltdmod = 0; 251 custom.bltafwm = 252 custom.bltalwn = 0xffff; 253 custom.bltcon0 = 0x09f0; 254 custom.bltcon1 = 0; 255 custom.bltsize = 1; 256 257 /* enable VBR interrupts. This is also done in the serial driver, but it 258 really belongs here.. */ 259 custom.intena = INTF_SETCLR | INTF_VERTB; 260 261 #if 0 262 #ifdef DEBUG 263 /* prove the display is up.. */ 264 for (fboff = 0; fboff < fbsize; fboff++) 265 { 266 fb->fb[fboff] = 0xff; 267 DELAY(10); 268 } 269 for (fboff = 0; fboff < fbsize; fboff++) 270 { 271 fb->fb[fboff] = 0; 272 DELAY(10); 273 } 274 #endif 275 #endif 276 277 gi->gd_regaddr = (caddr_t) fb; /* XXX */ 278 gi->gd_regsize = 0; 279 280 gi->gd_fbaddr = fb->fb - (u_char *) CHIPMEMADDR; 281 gi->gd_fbsize = fbsize; 282 283 gi->gd_colors = 1 << fb->fb_z; 284 gi->gd_planes = fb->fb_z; 285 286 gi->gd_fbwidth = fb->fb_width; 287 gi->gd_fbheight = fb->fb_height; 288 gi->gd_fbx = fb->fb_x; 289 gi->gd_fby = fb->fb_y; 290 gi->gd_dwidth = fb->disp_width; 291 gi->gd_dheight = fb->disp_height; 292 gi->gd_dx = fb->disp_x; 293 gi->gd_dy = fb->disp_y; 294 295 gp->g_regkva = 0; /* builtin */ 296 gp->g_fbkva = fb->fb; 297 298 return(1); 299 } 300 301 cc_config(gp, di) 302 register struct grf_softc *gp; 303 struct grfdyninfo *di; 304 { 305 register struct ccfb *fb = &ccfb; 306 struct grfinfo *gi = &gp->g_display; 307 u_char *fbp, save; 308 int fboff, fbsize; 309 int s; 310 311 /* bottom missing... */ 312 313 } 314 315 /* 316 * Change the mode of the display. 317 * Right now all we can do is grfon/grfoff. 318 * Return a UNIX error number or 0 for success. 319 */ 320 cc_mode(gp, cmd, arg) 321 register struct grf_softc *gp; 322 int cmd; 323 void *arg; 324 { 325 switch (cmd) 326 { 327 case GM_GRFON: 328 custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_COPPER; 329 return 0; 330 331 case GM_GRFOFF: 332 custom.dmacon = DMAF_RASTER | DMAF_COPPER; 333 return 0; 334 335 case GM_GRFCONFIG: 336 return cc_config (gp, (struct grfdyninfo *) arg); 337 338 default: 339 break; 340 } 341 342 return EINVAL; 343 } 344 345 #endif 346