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