1 /* $NetBSD: cc.c,v 1.28 2021/08/12 20:13:54 andvar Exp $ */
2
3 /*
4 * Copyright (c) 1994 Christian E. Hopps
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Christian E. Hopps.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: cc.c,v 1.28 2021/08/12 20:13:54 andvar Exp $");
35
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/queue.h>
39
40 #include <amiga/amiga/custom.h>
41 #include <amiga/amiga/cc.h>
42 #include "audio.h"
43
44 vaddr_t CUSTOMADDR, CUSTOMbase;
45
46 #if defined (__GNUC__)
47 #define INLINE inline
48 #else
49 #define INLINE
50 #endif
51
52 /* init all the "custom chips" */
53 void
custom_chips_init(void)54 custom_chips_init(void)
55 {
56 cc_init_chipmem();
57 cc_init_vbl();
58 cc_init_audio();
59 cc_init_blitter();
60 cc_init_copper();
61 }
62
63 /*
64 * Vertical blank interrupt server chains.
65 */
66 LIST_HEAD(vbllist, vbl_node) vbl_list;
67
68 void
turn_vbl_function_off(struct vbl_node * n)69 turn_vbl_function_off(struct vbl_node *n)
70 {
71 if (n->flags & VBLNF_OFF)
72 return;
73
74 n->flags |= VBLNF_TURNOFF;
75 while ((n->flags & VBLNF_OFF) == 0)
76 ;
77 }
78
79 /* allow function to be called on next vbl interrupt. */
80 void
turn_vbl_function_on(struct vbl_node * n)81 turn_vbl_function_on(struct vbl_node *n)
82 {
83 n->flags &= (short) ~(VBLNF_OFF);
84 }
85
86 void
add_vbl_function(struct vbl_node * add,short priority,void * data)87 add_vbl_function(struct vbl_node *add, short priority, void *data)
88 {
89 int s;
90 struct vbl_node *n, *prev;
91
92 s = spl3();
93 prev = NULL;
94 LIST_FOREACH(n, &vbl_list, link) {
95 if (add->priority > n->priority) {
96 /* insert add_node before. */
97 if (prev == NULL) {
98 LIST_INSERT_HEAD(&vbl_list, add, link);
99 } else {
100 LIST_INSERT_AFTER(prev, add, link);
101 }
102 add = NULL;
103 break;
104 }
105 prev = n;
106 }
107 if (add != NULL) {
108 if (prev == NULL) {
109 LIST_INSERT_HEAD(&vbl_list, add, link);
110 } else {
111 LIST_INSERT_AFTER(prev, add, link);
112 }
113 }
114 splx(s);
115 }
116
117 void
remove_vbl_function(struct vbl_node * n)118 remove_vbl_function(struct vbl_node *n)
119 {
120 int s;
121
122 s = spl3();
123 LIST_REMOVE(n, link);
124 splx(s);
125 }
126
127 /* Level 3 hardware interrupt */
128 void
vbl_handler(void)129 vbl_handler(void)
130 {
131 struct vbl_node *n;
132
133 /* handle all vbl functions */
134 LIST_FOREACH(n, &vbl_list, link) {
135 if (n->flags & VBLNF_TURNOFF) {
136 n->flags |= VBLNF_OFF;
137 n->flags &= ~(VBLNF_TURNOFF);
138 } else {
139 if (n != NULL)
140 n->function(n->data);
141 }
142 }
143 custom.intreq = INTF_VERTB;
144 }
145
146 void
cc_init_vbl(void)147 cc_init_vbl(void)
148 {
149 LIST_INIT(&vbl_list);
150 /*
151 * enable vertical blank interrupts
152 */
153 custom.intena = INTF_SETCLR | INTF_VERTB;
154 }
155
156
157 /*
158 * Blitter stuff.
159 */
160
161 void
cc_init_blitter(void)162 cc_init_blitter(void)
163 {
164 }
165
166 /* test twice to cover blitter bugs if BLTDONE (BUSY) is set it is not done. */
167 int
is_blitter_busy(void)168 is_blitter_busy(void)
169 {
170 u_short bb;
171
172 bb = (custom.dmaconr & DMAF_BLTDONE);
173 if ((custom.dmaconr & DMAF_BLTDONE) || bb)
174 return (1);
175 return (0);
176 }
177
178 void
wait_blit(void)179 wait_blit(void)
180 {
181 /*
182 * V40 state this covers all blitter bugs.
183 */
184 while (is_blitter_busy())
185 ;
186 }
187
188 void
blitter_handler(void)189 blitter_handler(void)
190 {
191 custom.intreq = INTF_BLIT;
192 }
193
194
195 void
do_blit(u_short size)196 do_blit(u_short size)
197 {
198 custom.bltsize = size;
199 }
200
201 void
set_blitter_control(u_short con0,u_short con1)202 set_blitter_control(u_short con0, u_short con1)
203 {
204 custom.bltcon0 = con0;
205 custom.bltcon1 = con1;
206 }
207
208 void
set_blitter_mods(u_short a,u_short b,u_short c,u_short d)209 set_blitter_mods(u_short a, u_short b, u_short c, u_short d)
210 {
211 custom.bltamod = a;
212 custom.bltbmod = b;
213 custom.bltcmod = c;
214 custom.bltdmod = d;
215 }
216
217 void
set_blitter_masks(u_short fm,u_short lm)218 set_blitter_masks(u_short fm, u_short lm)
219 {
220 custom.bltafwm = fm;
221 custom.bltalwm = lm;
222 }
223
224 void
set_blitter_data(u_short da,u_short db,u_short dc)225 set_blitter_data(u_short da, u_short db, u_short dc)
226 {
227 custom.bltadat = da;
228 custom.bltbdat = db;
229 custom.bltcdat = dc;
230 }
231
232 void
set_blitter_pointers(void * a,void * b,void * c,void * d)233 set_blitter_pointers(void *a, void *b, void *c, void *d)
234 {
235 custom.bltapt = a;
236 custom.bltbpt = b;
237 custom.bltcpt = c;
238 custom.bltdpt = d;
239 }
240
241 /*
242 * Copper Stuff.
243 */
244
245
246 /*
247 * Wait till end of frame. We should probably better use the
248 * sleep/wakeup system newly introduced in the vbl manager
249 */
250 void
wait_tof(void)251 wait_tof(void)
252 {
253 /*
254 * wait until bottom of frame.
255 */
256 while ((custom.vposr & 0x0007) == 0)
257 ;
258
259 /*
260 * wait until until top of frame.
261 */
262 while (custom.vposr & 0x0007)
263 ;
264
265 if (custom.vposr & 0x8000)
266 return;
267 /*
268 * we are on short frame.
269 * wait for long frame bit set
270 */
271 while ((custom.vposr & 0x8000) == 0)
272 ;
273 }
274
275 cop_t *
find_copper_inst(cop_t * l,u_short inst)276 find_copper_inst(cop_t *l, u_short inst)
277 {
278 cop_t *r = NULL;
279 while ((l->cp.data & 0xff01ff01) != 0xff01ff00) {
280 if (l->cp.inst.opcode == inst) {
281 r = l;
282 break;
283 }
284 l++;
285 }
286 return (r);
287 }
288
289 void
install_copper_list(cop_t * l)290 install_copper_list(cop_t *l)
291 {
292 wait_tof();
293 wait_tof();
294 custom.cop1lc = l;
295 }
296
297
298 void
cc_init_copper(void)299 cc_init_copper(void)
300 {
301 }
302
303 /*
304 * level 3 interrupt
305 */
306 void
copper_handler(void)307 copper_handler(void)
308 {
309 custom.intreq = INTF_COPER;
310 }
311
312 /*
313 * Audio stuff.
314 */
315
316
317 /* - channel[4] */
318 /* the data for each audio channel and what to do with it. */
319 struct audio_channel channel[4];
320
321 /* audio vbl node for vbl function */
322 struct vbl_node audio_vbl_node;
323
324 void
cc_init_audio(void)325 cc_init_audio(void)
326 {
327 int i;
328
329 /*
330 * disable all audio interrupts
331 */
332 custom.intena = INTF_AUD0|INTF_AUD1|INTF_AUD2|INTF_AUD3;
333
334 /*
335 * initialize audio channels to off.
336 */
337 for (i = 0; i < 4; i++) {
338 channel[i].play_count = 0;
339 channel[i].isaudio = 0;
340 channel[i].handler = NULL;
341 }
342 }
343
344
345 /*
346 * Audio Interrupt Handler
347 */
348 void
audio_handler(void)349 audio_handler(void)
350 {
351 u_short audio_dma, flag, ir;
352 int i;
353
354 audio_dma = custom.dmaconr;
355
356 /*
357 * only check channels who have DMA enabled.
358 */
359 audio_dma &= (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3);
360
361 /*
362 * disable all audio interrupts with DMA set
363 */
364 custom.intena = (audio_dma << INTB_AUD0) & AUCC_ALLINTF;
365
366 /*
367 * if no audio DMA enabled then exit quick.
368 */
369 if (!audio_dma) {
370 /*
371 * clear all interrupts.
372 */
373 custom.intreq = AUCC_ALLINTF;
374 goto out;
375 }
376 for (i = 0; i < AUCC_MAXINT; i++) {
377 flag = (1 << i);
378 ir = custom.intreqr;
379 /*
380 * is this channel's interrupt is set?
381 */
382 if ((ir & (flag << INTB_AUD0)) == 0)
383 continue;
384 #if NAUDIO>0
385 custom.intreq=(flag<<INTB_AUD0);
386 /* call audio handler with channel number */
387 if (channel[i].isaudio==1)
388 if (channel[i].handler)
389 (*channel[i].handler)(i);
390 #endif
391
392 if (channel[i].play_count)
393 channel[i].play_count--;
394 else {
395 /*
396 * disable DMA to this channel and
397 * disable interrupts to this channel
398 */
399 custom.dmacon = flag;
400 custom.intena = (flag << INTB_AUD0);
401 if (channel[i].isaudio==-1)
402 channel[i].isaudio=0;
403 }
404 /*
405 * clear this channels interrupt.
406 */
407 custom.intreq = (flag << INTB_AUD0);
408 }
409
410 out:
411 /*
412 * enable audio interrupts with DMA still set.
413 */
414 audio_dma = custom.dmaconr;
415 audio_dma &= (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3);
416 custom.intena = INTF_SETCLR | (audio_dma << INTB_AUD0);
417 }
418
419 void
play_sample(u_short len,u_short * data,u_short period,u_short volume,u_short channels,u_long count)420 play_sample(u_short len, u_short *data, u_short period, u_short volume, u_short channels, u_long count)
421 {
422 u_short dmabits, ch;
423 register int i;
424
425 dmabits = channels & 0xf;
426
427 /* check to see, whether all channels are free */
428 for (i=0;i<4;i++) {
429 if ((1<<i) & dmabits) {
430 if (channel[i].isaudio)
431 return; /* allocated */
432 else
433 channel[i].isaudio=-1; /* allocate */
434 }
435 }
436
437 custom.dmacon = dmabits; /* turn off the correct channels */
438
439 /* load the channels */
440 for (ch = 0; ch < 4; ch++) {
441 if ((dmabits & (ch << ch)) == 0)
442 continue;
443 custom.aud[ch].len = len;
444 custom.aud[ch].lc = data;
445 custom.aud[ch].per = period;
446 custom.aud[ch].vol = volume;
447 channel[ch].play_count = count;
448 }
449 /*
450 * turn on interrupts and enable DMA for channels and
451 */
452 custom.intena = INTF_SETCLR | (dmabits << INTB_AUD0);
453 custom.dmacon = DMAF_SETCLR | DMAF_MASTER |dmabits;
454 }
455
456 /*
457 * Chipmem allocator.
458 */
459
460 static TAILQ_HEAD(chiplist, mem_node) chip_list;
461 static TAILQ_HEAD(freelist, mem_node) free_list;
462 static u_long chip_total; /* total free. */
463 static u_long chip_size; /* size of it all. */
464
465 void
cc_init_chipmem(void)466 cc_init_chipmem(void)
467 {
468 int s = splhigh ();
469 struct mem_node *mem;
470
471 chip_size = chipmem_end - (chipmem_start + PAGE_SIZE);
472 chip_total = chip_size - sizeof(*mem);
473
474 mem = (struct mem_node *)chipmem_steal(chip_size);
475 mem->size = chip_total;
476
477 TAILQ_INIT(&chip_list);
478 TAILQ_INIT(&free_list);
479
480 TAILQ_INSERT_HEAD(&chip_list, mem, link);
481 TAILQ_INSERT_HEAD(&free_list, mem, free_link);
482 splx(s);
483 }
484
485 void *
alloc_chipmem(u_long size)486 alloc_chipmem(u_long size)
487 {
488 int s;
489 struct mem_node *mn, *new;
490
491 if (size == 0)
492 return NULL;
493
494 s = splhigh();
495
496 if (size & ~(CM_BLOCKMASK))
497 size = (size & CM_BLOCKMASK) + CM_BLOCKSIZE;
498
499 /*
500 * walk list of available nodes.
501 */
502 TAILQ_FOREACH(mn, &free_list, free_link)
503 if (size <= mn->size)
504 break;
505
506 if (mn == NULL) {
507 splx(s);
508 return NULL;
509 }
510
511 if ((mn->size - size) <= sizeof (*mn)) {
512 /*
513 * our allocation would not leave room
514 * for a new node in between.
515 */
516 TAILQ_REMOVE(&free_list, mn, free_link);
517 mn->type = MNODE_USED;
518 size = mn->size; /* increase size. (or same) */
519 chip_total -= mn->size;
520 splx(s);
521 return ((void *)&mn[1]);
522 }
523
524 /*
525 * split the node's memory.
526 */
527 new = mn;
528 new->size -= size + sizeof(struct mem_node);
529 mn = (struct mem_node *)(MNODES_MEM(new) + new->size);
530 mn->size = size;
531
532 /*
533 * add split node to node list
534 * and mark as not on free list
535 */
536 TAILQ_INSERT_AFTER(&chip_list, new, mn, link);
537 mn->type = MNODE_USED;
538
539 chip_total -= size + sizeof(struct mem_node);
540 splx(s);
541 return ((void *)&mn[1]);
542 }
543
544 void
free_chipmem(void * mem)545 free_chipmem(void *mem)
546 {
547 struct mem_node *mn, *next, *prev;
548 int s;
549
550 if (mem == NULL)
551 return;
552
553 s = splhigh();
554 mn = (struct mem_node *)mem - 1;
555 next = TAILQ_NEXT(mn, link);
556 prev = TAILQ_PREV(mn, chiplist, link);
557
558 /*
559 * check ahead of us.
560 */
561 if (next->type == MNODE_FREE) {
562 /*
563 * if next is: a valid node and a free node. ==> merge
564 */
565 TAILQ_INSERT_BEFORE(next, mn, free_link);
566 mn->type = MNODE_FREE;
567 TAILQ_REMOVE(&chip_list, next, link);
568 TAILQ_REMOVE(&free_list, next, free_link);
569 chip_total += mn->size + sizeof(struct mem_node);
570 mn->size += next->size + sizeof(struct mem_node);
571 }
572 if (prev->type == MNODE_FREE) {
573 /*
574 * if prev is: a valid node and a free node. ==> merge
575 */
576 if (mn->type != MNODE_FREE)
577 chip_total += mn->size + sizeof(struct mem_node);
578 else {
579 /* already on free list */
580 TAILQ_REMOVE(&free_list, mn, free_link);
581 mn->type = MNODE_USED;
582 chip_total += sizeof(struct mem_node);
583 }
584 TAILQ_REMOVE(&chip_list, mn, link);
585 prev->size += mn->size + sizeof(struct mem_node);
586 } else if (mn->type != MNODE_FREE) {
587 /*
588 * we still are not on free list and we need to be.
589 * <-- | -->
590 */
591 while (next != NULL && prev != NULL) {
592 if (next->type == MNODE_FREE) {
593 TAILQ_INSERT_BEFORE(next, mn, free_link);
594 mn->type = MNODE_FREE;
595 break;
596 }
597 if (prev->type == MNODE_FREE) {
598 TAILQ_INSERT_AFTER(&free_list, prev, mn,
599 free_link);
600 mn->type = MNODE_FREE;
601 break;
602 }
603 prev = TAILQ_PREV(prev, chiplist, link);
604 next = TAILQ_NEXT(next, link);
605 }
606 if (mn->type != MNODE_FREE) {
607 if (next == NULL) {
608 /*
609 * we are not on list so we can add
610 * ourselves to the tail. (we walked to it.)
611 */
612 TAILQ_INSERT_TAIL(&free_list,mn,free_link);
613 } else {
614 TAILQ_INSERT_HEAD(&free_list,mn,free_link);
615 }
616 mn->type = MNODE_FREE;
617 }
618 chip_total += mn->size; /* add our helpings to the pool. */
619 }
620 splx(s);
621 }
622
623 u_long
sizeof_chipmem(void * mem)624 sizeof_chipmem(void *mem)
625 {
626 struct mem_node *mn;
627
628 if (mem == NULL)
629 return (0);
630 mn = mem;
631 mn--;
632 return (mn->size);
633 }
634
635 u_long
avail_chipmem(int largest)636 avail_chipmem(int largest)
637 {
638 struct mem_node *mn;
639 u_long val;
640 int s;
641
642 val = 0;
643 if (largest == 0)
644 val = chip_total;
645 else {
646 s = splhigh();
647 TAILQ_FOREACH(mn, &free_list, free_link) {
648 if (mn->size > val)
649 val = mn->size;
650 }
651 splx(s);
652 }
653 return (val);
654 }
655