Home | History | Annotate | Line # | Download | only in amiga
cc.c revision 1.26
      1 /*	$NetBSD: cc.c,v 1.25 2014/01/03 07:14:20 mlelstv 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.25 2014/01/03 07:14:20 mlelstv 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(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 iterrupt sever chains.
     65  */
     66 LIST_HEAD(vbllist, vbl_node) vbl_list;
     67 
     68 void
     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
     81 turn_vbl_function_on(struct vbl_node *n)
     82 {
     83 	n->flags &= (short) ~(VBLNF_OFF);
     84 }
     85 
     86 void
     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
    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
    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
    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
    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
    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
    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
    189 blitter_handler(void)
    190 {
    191 	custom.intreq = INTF_BLIT;
    192 }
    193 
    194 
    195 void
    196 do_blit(u_short size)
    197 {
    198 	custom.bltsize = size;
    199 }
    200 
    201 void
    202 set_blitter_control(u_short con0, u_short con1)
    203 {
    204 	custom.bltcon0 = con0;
    205 	custom.bltcon1 = con1;
    206 }
    207 
    208 void
    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
    218 set_blitter_masks(u_short fm, u_short lm)
    219 {
    220 	custom.bltafwm = fm;
    221 	custom.bltalwm = lm;
    222 }
    223 
    224 void
    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
    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
    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 *
    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
    290 install_copper_list(cop_t *l)
    291 {
    292 	wait_tof();
    293 	wait_tof();
    294 	custom.cop1lc = l;
    295 }
    296 
    297 
    298 void
    299 cc_init_copper(void)
    300 {
    301 }
    302 
    303 /*
    304  * level 3 interrupt
    305  */
    306 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
    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
    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
    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
    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 *
    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 		return NULL;
    508 
    509 	if ((mn->size - size) <= sizeof (*mn)) {
    510 		/*
    511 		 * our allocation would not leave room
    512 		 * for a new node in between.
    513 		 */
    514 		TAILQ_REMOVE(&free_list, mn, free_link);
    515 		mn->type = MNODE_USED;
    516 		size = mn->size;	 /* increase size. (or same) */
    517 		chip_total -= mn->size;
    518 		splx(s);
    519 		return ((void *)&mn[1]);
    520 	}
    521 
    522 	/*
    523 	 * split the node's memory.
    524 	 */
    525 	new = mn;
    526 	new->size -= size + sizeof(struct mem_node);
    527 	mn = (struct mem_node *)(MNODES_MEM(new) + new->size);
    528 	mn->size = size;
    529 
    530 	/*
    531 	 * add split node to node list
    532 	 * and mark as not on free list
    533 	 */
    534 	TAILQ_INSERT_AFTER(&chip_list, new, mn, link);
    535 	mn->type = MNODE_USED;
    536 
    537 	chip_total -= size + sizeof(struct mem_node);
    538 	splx(s);
    539 	return ((void *)&mn[1]);
    540 }
    541 
    542 void
    543 free_chipmem(void *mem)
    544 {
    545 	struct mem_node *mn, *next, *prev;
    546 	int s;
    547 
    548 	if (mem == NULL)
    549 		return;
    550 
    551 	s = splhigh();
    552 	mn = (struct mem_node *)mem - 1;
    553 	next = TAILQ_NEXT(mn, link);
    554 	prev = TAILQ_PREV(mn, chiplist, link);
    555 
    556 	/*
    557 	 * check ahead of us.
    558 	 */
    559 	if (next->type == MNODE_FREE) {
    560 		/*
    561 		 * if next is: a valid node and a free node. ==> merge
    562 		 */
    563 		TAILQ_INSERT_BEFORE(next, mn, free_link);
    564 		mn->type = MNODE_FREE;
    565 		TAILQ_REMOVE(&chip_list, next, link);
    566 		TAILQ_REMOVE(&free_list, next, free_link);
    567 		chip_total += mn->size + sizeof(struct mem_node);
    568 		mn->size += next->size + sizeof(struct mem_node);
    569 	}
    570 	if (prev->type == MNODE_FREE) {
    571 		/*
    572 		 * if prev is: a valid node and a free node. ==> merge
    573 		 */
    574 		if (mn->type != MNODE_FREE)
    575 			chip_total += mn->size + sizeof(struct mem_node);
    576 		else {
    577 			/* already on free list */
    578 			TAILQ_REMOVE(&free_list, mn, free_link);
    579 			mn->type = MNODE_USED;
    580 			chip_total += sizeof(struct mem_node);
    581 		}
    582 		TAILQ_REMOVE(&chip_list, mn, link);
    583 		prev->size += mn->size + sizeof(struct mem_node);
    584 	} else if (mn->type != MNODE_FREE) {
    585 		/*
    586 		 * we still are not on free list and we need to be.
    587 		 * <-- | -->
    588 		 */
    589 		while (next != NULL && prev != NULL) {
    590 			if (next->type == MNODE_FREE) {
    591 				TAILQ_INSERT_BEFORE(next, mn, free_link);
    592 				mn->type = MNODE_FREE;
    593 				break;
    594 			}
    595 			if (prev->type == MNODE_FREE) {
    596 				TAILQ_INSERT_AFTER(&free_list, prev, mn,
    597 				    free_link);
    598 				mn->type = MNODE_FREE;
    599 				break;
    600 			}
    601 			prev = TAILQ_PREV(prev, chiplist, link);
    602 			next = TAILQ_NEXT(next, link);
    603 		}
    604 		if (mn->type != MNODE_FREE) {
    605 			if (next == NULL) {
    606 				/*
    607 				 * we are not on list so we can add
    608 				 * ourselves to the tail. (we walked to it.)
    609 				 */
    610 				TAILQ_INSERT_TAIL(&free_list,mn,free_link);
    611 			} else {
    612 				TAILQ_INSERT_HEAD(&free_list,mn,free_link);
    613 			}
    614 			mn->type = MNODE_FREE;
    615 		}
    616 		chip_total += mn->size;	/* add our helpings to the pool. */
    617 	}
    618 	splx(s);
    619 }
    620 
    621 u_long
    622 sizeof_chipmem(void *mem)
    623 {
    624 	struct mem_node *mn;
    625 
    626 	if (mem == NULL)
    627 		return (0);
    628 	mn = mem;
    629 	mn--;
    630 	return (mn->size);
    631 }
    632 
    633 u_long
    634 avail_chipmem(int largest)
    635 {
    636 	struct mem_node *mn;
    637 	u_long val;
    638 	int s;
    639 
    640 	val = 0;
    641 	if (largest == 0)
    642 		val = chip_total;
    643 	else {
    644 		s = splhigh();
    645 		TAILQ_FOREACH(mn, &free_list, free_link) {
    646 			if (mn->size > val)
    647 				val = mn->size;
    648 		}
    649 		splx(s);
    650 	}
    651 	return (val);
    652 }
    653