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