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 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 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 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 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 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 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