xref: /netbsd/sys/arch/amiga/amiga/cc.c (revision bf9ec67e)
1 /*	$NetBSD: cc.c,v 1.15 2002/04/25 09:20:26 aymeric 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.15 2002/04/25 09:20:26 aymeric 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
54 custom_chips_init()
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 iterrupt sever chains.
65  */
66 LIST_HEAD(vbllist, vbl_node) vbl_list;
67 
68 void
69 turn_vbl_function_off(n)
70 	struct vbl_node *n;
71 {
72 	if (n->flags & VBLNF_OFF)
73 		return;
74 
75 	n->flags |= VBLNF_TURNOFF;
76 	while ((n->flags & VBLNF_OFF) == 0)
77 		;
78 }
79 
80 /* allow function to be called on next vbl interrupt. */
81 void
82 turn_vbl_function_on(n)
83 	struct vbl_node *n;
84 {
85 	n->flags &= (short) ~(VBLNF_OFF);
86 }
87 
88 void
89 add_vbl_function(add, priority, data)
90 	struct vbl_node *add;
91 	short priority;
92 	void *data;
93 {
94 	int s;
95 	struct vbl_node *n, *prev;
96 
97 	s = spl3();
98 	prev = NULL;
99 	for (n = vbl_list.lh_first; n != NULL; n = n->link.le_next) {
100 		if (add->priority > n->priority) {
101 			/* insert add_node before. */
102 			if (prev == NULL) {
103 				LIST_INSERT_HEAD(&vbl_list, add, link);
104 			} else {
105 				LIST_INSERT_AFTER(prev, add, link);
106 			}
107 			add = NULL;
108 			break;
109 		}
110 		prev = n;
111 	}
112 	if (add) {
113 		if (prev == NULL) {
114 			LIST_INSERT_HEAD(&vbl_list, add, link);
115 		} else {
116 			LIST_INSERT_AFTER(prev, add, link);
117 		}
118 	}
119 	splx(s);
120 }
121 
122 void
123 remove_vbl_function(n)
124 	struct vbl_node *n;
125 {
126 	int s;
127 
128 	s = spl3();
129 	LIST_REMOVE(n, link);
130 	splx(s);
131 }
132 
133 /* Level 3 hardware interrupt */
134 void
135 vbl_handler()
136 {
137 	struct vbl_node *n;
138 
139 	/* handle all vbl functions */
140 	for (n = vbl_list.lh_first; n != NULL; n = n->link.le_next) {
141 		if (n->flags & VBLNF_TURNOFF) {
142 			n->flags |= VBLNF_OFF;
143 			n->flags &= ~(VBLNF_TURNOFF);
144 		} else {
145 			if (n != NULL)
146 				n->function(n->data);
147 		}
148 	}
149 	custom.intreq = INTF_VERTB;
150 }
151 
152 void
153 cc_init_vbl()
154 {
155 	LIST_INIT(&vbl_list);
156 	/*
157 	 * enable vertical blank interrupts
158 	 */
159 	custom.intena = INTF_SETCLR | INTF_VERTB;
160 }
161 
162 
163 /*
164  * Blitter stuff.
165  */
166 
167 void
168 cc_init_blitter()
169 {
170 }
171 
172 /* test twice to cover blitter bugs if BLTDONE (BUSY) is set it is not done. */
173 int
174 is_blitter_busy()
175 {
176 	u_short bb;
177 
178 	bb = (custom.dmaconr & DMAF_BLTDONE);
179 	if ((custom.dmaconr & DMAF_BLTDONE) || bb)
180 		return (1);
181 	return (0);
182 }
183 
184 void
185 wait_blit()
186 {
187 	/*
188 	 * V40 state this covers all blitter bugs.
189 	 */
190 	while (is_blitter_busy())
191 		;
192 }
193 
194 void
195 blitter_handler()
196 {
197 	custom.intreq = INTF_BLIT;
198 }
199 
200 
201 void
202 do_blit(size)
203 	u_short size;
204 {
205 	custom.bltsize = size;
206 }
207 
208 void
209 set_blitter_control(con0, con1)
210 	u_short con0, con1;
211 {
212 	custom.bltcon0 = con0;
213 	custom.bltcon1 = con1;
214 }
215 
216 void
217 set_blitter_mods(a, b, c, d)
218 	u_short a, b, c, d;
219 {
220 	custom.bltamod = a;
221 	custom.bltbmod = b;
222 	custom.bltcmod = c;
223 	custom.bltdmod = d;
224 }
225 
226 void
227 set_blitter_masks(fm, lm)
228 	u_short fm, lm;
229 {
230 	custom.bltafwm = fm;
231 	custom.bltalwm = lm;
232 }
233 
234 void
235 set_blitter_data(da, db, dc)
236 	u_short da, db, dc;
237 {
238 	custom.bltadat = da;
239 	custom.bltbdat = db;
240 	custom.bltcdat = dc;
241 }
242 
243 void
244 set_blitter_pointers(a, b, c, d)
245 	void *a, *b, *c, *d;
246 {
247 	custom.bltapt = a;
248 	custom.bltbpt = b;
249 	custom.bltcpt = c;
250 	custom.bltdpt = d;
251 }
252 
253 /*
254  * Copper Stuff.
255  */
256 
257 
258 /*
259  * Wait till end of frame. We should probably better use the
260  * sleep/wakeup system newly introduced in the vbl manager
261  */
262 void
263 wait_tof()
264 {
265 	/*
266 	 * wait until bottom of frame.
267 	 */
268 	while ((custom.vposr & 0x0007) == 0)
269 		;
270 
271 	/*
272 	 * wait until until top of frame.
273 	 */
274 	while (custom.vposr & 0x0007)
275 		;
276 
277 	if (custom.vposr & 0x8000)
278 		return;
279 	/*
280 	 * we are on short frame.
281 	 * wait for long frame bit set
282 	 */
283 	while ((custom.vposr & 0x8000) == 0)
284 		;
285 }
286 
287 cop_t *
288 find_copper_inst(l, inst)
289 	cop_t *l;
290 	u_short inst;
291 {
292 	cop_t *r = NULL;
293 	while ((l->cp.data & 0xff01ff01) != 0xff01ff00) {
294 		if (l->cp.inst.opcode == inst) {
295 			r = l;
296 			break;
297 		}
298 		l++;
299 	}
300 	return (r);
301 }
302 
303 void
304 install_copper_list(l)
305 	cop_t *l;
306 {
307 	wait_tof();
308 	wait_tof();
309 	custom.cop1lc = l;
310 }
311 
312 
313 void
314 cc_init_copper()
315 {
316 }
317 
318 /*
319  * level 3 interrupt
320  */
321 void
322 copper_handler()
323 {
324 	custom.intreq = INTF_COPER;
325 }
326 
327 /*
328  * Audio stuff.
329  */
330 
331 
332 /* - channel[4] */
333 /* the data for each audio channel and what to do with it. */
334 struct audio_channel channel[4];
335 
336 /* audio vbl node for vbl function  */
337 struct vbl_node audio_vbl_node;
338 
339 void
340 cc_init_audio()
341 {
342 	int i;
343 
344 	/*
345 	 * disable all audio interupts
346 	 */
347 	custom.intena = INTF_AUD0|INTF_AUD1|INTF_AUD2|INTF_AUD3;
348 
349 	/*
350 	 * initialize audio channels to off.
351 	 */
352 	for (i=0; i < 4; i++) {
353 		channel[i].play_count = 0;
354 		channel[i].isaudio=0;
355 		channel[i].handler=NULL;
356 	}
357 }
358 
359 
360 /*
361  * Audio Interrupt Handler
362  */
363 void
364 audio_handler()
365 {
366 	u_short audio_dma, disable_dma, flag, ir;
367 	int i;
368 
369 	audio_dma = custom.dmaconr;
370 	disable_dma = 0;
371 
372 	/*
373 	 * only check channels who have DMA enabled.
374 	 */
375 	audio_dma &= (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3);
376 
377 	/*
378 	 * disable all audio interupts with DMA set
379 	 */
380 	custom.intena = (audio_dma << INTB_AUD0) & AUCC_ALLINTF;
381 
382 	/*
383 	 * if no audio dma enabled then exit quick.
384 	 */
385 	if (!audio_dma) {
386 		/*
387 		 * clear all interrupts.
388 		 */
389 		custom.intreq = AUCC_ALLINTF;
390 		goto out;
391 	}
392 	for (i = 0; i < AUCC_MAXINT; i++) {
393 		flag = (1 << i);
394 		ir = custom.intreqr;
395 		/*
396 		 * is this channel's interrupt is set?
397 		 */
398 		if ((ir & (flag << INTB_AUD0)) == 0)
399 			continue;
400 #if NAUDIO>0
401 		custom.intreq=(flag<<INTB_AUD0);
402 		/* call audio handler with channel number */
403 		if (channel[i].isaudio==1)
404 			if (channel[i].handler)
405 				(*channel[i].handler)(i);
406 #endif
407 
408 		if (channel[i].play_count)
409 			channel[i].play_count--;
410 		else {
411 			/*
412 			 * disable DMA to this channel and
413 			 * disable interrupts to this channel
414 			 */
415 			custom.dmacon = flag;
416 			custom.intena = (flag << INTB_AUD0);
417 			if (channel[i].isaudio==-1)
418 				channel[i].isaudio=0;
419 		}
420 		/*
421 		 * clear this channels interrupt.
422 		 */
423 		custom.intreq = (flag << INTB_AUD0);
424 	}
425 
426 out:
427 	/*
428 	 * enable audio interupts with dma still set.
429 	 */
430 	audio_dma = custom.dmaconr;
431 	audio_dma &= (DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3);
432 	custom.intena = INTF_SETCLR | (audio_dma << INTB_AUD0);
433 }
434 
435 void
436 play_sample(len, data, period, volume, channels, count)
437 	u_short len, *data, period, volume, channels;
438 	u_long count;
439 {
440 	u_short dmabits, ch;
441 	register int i;
442 
443 	dmabits = channels & 0xf;
444 
445 	/* check to see, whether all channels are free */
446 	for (i=0;i<4;i++) {
447 		if ((1<<i) & dmabits) {
448 			if (channel[i].isaudio)
449 				return; /* allocated */
450 			else
451 				channel[i].isaudio=-1; /* allocate */
452 		}
453 	}
454 
455 	custom.dmacon = dmabits;	/* turn off the correct channels */
456 
457 	/* load the channels */
458 	for (ch = 0; ch < 4; ch++) {
459 		if ((dmabits & (ch << ch)) == 0)
460 			continue;
461 		custom.aud[ch].len = len;
462 		custom.aud[ch].lc = data;
463 		custom.aud[ch].per = period;
464 		custom.aud[ch].vol = volume;
465 		channel[ch].play_count = count;
466 	}
467 	/*
468 	 * turn on interrupts and enable dma for channels and
469 	 */
470 	custom.intena = INTF_SETCLR | (dmabits << INTB_AUD0);
471 	custom.dmacon = DMAF_SETCLR | DMAF_MASTER |dmabits;
472 }
473 
474 /*
475  * Chipmem allocator.
476  */
477 
478 static CIRCLEQ_HEAD(chiplist, mem_node) chip_list;
479 static CIRCLEQ_HEAD(freelist, mem_node) free_list;
480 static u_long   chip_total;		/* total free. */
481 static u_long   chip_size;		/* size of it all. */
482 
483 void
484 cc_init_chipmem()
485 {
486 	int s = splhigh ();
487 	struct mem_node *mem;
488 
489 	chip_size = chipmem_end - (chipmem_start + NBPG);
490 	chip_total = chip_size - sizeof(*mem);
491 
492 	mem = (struct mem_node *)chipmem_steal(chip_size);
493 	mem->size = chip_total;
494 
495 	CIRCLEQ_INIT(&chip_list);
496 	CIRCLEQ_INIT(&free_list);
497 
498 	CIRCLEQ_INSERT_HEAD(&chip_list, mem, link);
499 	CIRCLEQ_INSERT_HEAD(&free_list, mem, free_link);
500 	splx(s);
501 }
502 
503 void *
504 alloc_chipmem(size)
505 	u_long size;
506 {
507 	int s;
508 	struct mem_node *mn, *new;
509 
510 	if (size == 0)
511 		return NULL;
512 
513 	s = splhigh();
514 
515 	if (size & ~(CM_BLOCKMASK))
516 		size = (size & CM_BLOCKMASK) + CM_BLOCKSIZE;
517 
518 	/*
519 	 * walk list of available nodes.
520 	 */
521 	mn = free_list.cqh_first;
522 	while (size > mn->size && mn != (void *)&free_list)
523 		mn = mn->free_link.cqe_next;
524 
525 	if (mn == (void *)&free_list)
526 		return(NULL);
527 
528 	if ((mn->size - size) <= sizeof (*mn)) {
529 		/*
530 		 * our allocation would not leave room
531 		 * for a new node in between.
532 		 */
533 		CIRCLEQ_REMOVE(&free_list, mn, free_link);
534 		mn->free_link.cqe_next = NULL;
535 		size = mn->size;	 /* increase size. (or same) */
536 		chip_total -= mn->size;
537 		splx(s);
538 		return ((void *)&mn[1]);
539 	}
540 
541 	/*
542 	 * split the node's memory.
543 	 */
544 	new = mn;
545 	new->size -= size + sizeof(struct mem_node);
546 	mn = (struct mem_node *)(MNODES_MEM(new) + new->size);
547 	mn->size = size;
548 
549 	/*
550 	 * add split node to node list
551 	 * and mark as not on free list
552 	 */
553 	CIRCLEQ_INSERT_AFTER(&chip_list, new, mn, link);
554 	mn->free_link.cqe_next = NULL;
555 
556 	chip_total -= size + sizeof(struct mem_node);
557 	splx(s);
558 	return ((void *)&mn[1]);
559 }
560 
561 void
562 free_chipmem(mem)
563 	void *mem;
564 {
565 	struct mem_node *mn, *next, *prev;
566 	int s;
567 
568 	if (mem == NULL)
569 		return;
570 
571 	s = splhigh();
572 	mn = (struct mem_node *)mem - 1;
573 	next = mn->link.cqe_next;
574 	prev = mn->link.cqe_prev;
575 
576 	/*
577 	 * check ahead of us.
578 	 */
579 	if (next->link.cqe_next != (void *)&chip_list &&
580 	    next->free_link.cqe_next) {
581 		/*
582 		 * if next is: a valid node and a free node. ==> merge
583 		 */
584 		CIRCLEQ_INSERT_BEFORE(&free_list, next, mn, free_link);
585 		CIRCLEQ_REMOVE(&chip_list, next, link);
586 		CIRCLEQ_REMOVE(&chip_list, next, free_link);
587 		chip_total += mn->size + sizeof(struct mem_node);
588 		mn->size += next->size + sizeof(struct mem_node);
589 	}
590 	if (prev->link.cqe_prev != (void *)&chip_list &&
591 	    prev->free_link.cqe_prev) {
592 		/*
593 		 * if prev is: a valid node and a free node. ==> merge
594 		 */
595 		if (mn->free_link.cqe_next == NULL)
596 			chip_total += mn->size + sizeof(struct mem_node);
597 		else {
598 			/* already on free list */
599 			CIRCLEQ_REMOVE(&free_list, mn, free_link);
600 			chip_total += sizeof(struct mem_node);
601 		}
602 		CIRCLEQ_REMOVE(&chip_list, mn, link);
603 		prev->size += mn->size + sizeof(struct mem_node);
604 	} else if (mn->free_link.cqe_next == NULL) {
605 		/*
606 		 * we still are not on free list and we need to be.
607 		 * <-- | -->
608 		 */
609 		while (next->link.cqe_next != (void *)&chip_list &&
610 		    prev->link.cqe_prev != (void *)&chip_list) {
611 			if (next->free_link.cqe_next) {
612 				CIRCLEQ_INSERT_BEFORE(&free_list, next, mn,
613 				    free_link);
614 				break;
615 			}
616 			if (prev->free_link.cqe_next) {
617 				CIRCLEQ_INSERT_AFTER(&free_list, prev, mn,
618 				    free_link);
619 				break;
620 			}
621 			prev = prev->link.cqe_prev;
622 			next = next->link.cqe_next;
623 		}
624 		if (mn->free_link.cqe_next == NULL) {
625 			if (next->link.cqe_next == (void *)&chip_list) {
626 				/*
627 				 * we are not on list so we can add
628 				 * ourselves to the tail. (we walked to it.)
629 				 */
630 				CIRCLEQ_INSERT_TAIL(&free_list,mn,free_link);
631 			} else {
632 				CIRCLEQ_INSERT_HEAD(&free_list,mn,free_link);
633 			}
634 		}
635 		chip_total += mn->size;	/* add our helpings to the pool. */
636 	}
637 	splx(s);
638 }
639 
640 u_long
641 sizeof_chipmem(mem)
642 	void *mem;
643 {
644 	struct mem_node *mn;
645 
646 	if (mem == NULL)
647 		return (0);
648 	mn = mem;
649 	mn--;
650 	return (mn->size);
651 }
652 
653 u_long
654 avail_chipmem(largest)
655 	int largest;
656 {
657 	struct mem_node *mn;
658 	u_long val;
659 	int s;
660 
661 	val = 0;
662 	if (largest == 0)
663 		val = chip_total;
664 	else {
665 		s = splhigh();
666 		for (mn = free_list.cqh_first; mn != (void *)&free_list;
667 		     mn = mn->free_link.cqe_next) {
668 			if (mn->size > val)
669 				val = mn->size;
670 		}
671 		splx(s);
672 	}
673 	return (val);
674 }
675