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