/* $NetBSD: aica_arm.c,v 1.6.2.1 2023/04/20 14:08:07 martin Exp $ */ /* * Copyright (c) 2003 Ryo Shimizu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #include #define DC_REG_ADDR 0x00800000 #define REG_READ_1(off) \ (*(volatile uint8_t *)(DC_REG_ADDR + (off))) #define REG_READ_2(off) \ (*(volatile uint16_t *)(DC_REG_ADDR + (off))) #define REG_READ_4(off) \ (*(volatile uint32_t *)(DC_REG_ADDR + (off))) #define REG_WRITE_1(off,val) \ ((*(volatile uint8_t *)(DC_REG_ADDR + (off))) = (val)) #define REG_WRITE_2(off,val) \ ((*(volatile uint16_t *)(DC_REG_ADDR + (off))) = (val)) #define REG_WRITE_4(off,val) \ ((*(volatile uint32_t *)((DC_REG_ADDR)+(off))) = (val)) #define CH_READ_1(ch,off) REG_READ_1(((ch) << 7) + (off)) #define CH_READ_2(ch,off) REG_READ_2(((ch) << 7) + (off)) #define CH_READ_4(ch,off) REG_READ_4(((ch) << 7) + (off)) #define CH_WRITE_1(ch,off,val) REG_WRITE_1(((ch) << 7) + (off), val) #define CH_WRITE_2(ch,off,val) REG_WRITE_2(((ch) << 7) + (off), val) #define CH_WRITE_4(ch,off,val) REG_WRITE_4(((ch) << 7) + (off), val) void *memset(void *, int, unsigned long); void aica_init(void); inline int in_first_half(unsigned int); inline int in_second_half(unsigned int); uint32_t rate2reg(unsigned int); void aica_stop(void); void aica_main(void); void aica_init(void) { int ch, off; /* Initialize AICA channels */ REG_WRITE_4(0x2800, 0x0000); /* Master volume: Min */ for (ch = 0; ch < 64; ch++) { CH_WRITE_4(ch, 0x00, 0x8000); /* Key off */ CH_WRITE_4(ch, 0x04, 0x0000); /* DataAddress (low) */ CH_WRITE_4(ch, 0x08, 0x0000); /* LoopStartPosition */ CH_WRITE_4(ch, 0x0c, 0x0000); /* LoopEndPosition */ CH_WRITE_4(ch, 0x10, 0x001f); /* AR = 0x1f = 0 msec */ CH_WRITE_4(ch, 0x14, 0x001f); /* RR = 0x1f = 0 msec */ CH_WRITE_4(ch, 0x18, 0x0000); /* Pitch */ CH_WRITE_4(ch, 0x1c, 0x0000); /* LFO Control */ CH_WRITE_4(ch, 0x20, 0x0000); /* DSP Channel to send */ CH_WRITE_4(ch, 0x24, 0x0000); /* Pan & Volume */ CH_WRITE_4(ch, 0x28, 0x0024); /* Volume & LowPassFilter */ CH_WRITE_4(ch, 0x2c, 0x0000); /* LowPassFilter for Attack */ CH_WRITE_4(ch, 0x30, 0x0000); /* LowPassFilter for Decay */ CH_WRITE_4(ch, 0x34, 0x0000); /* LowPassFilter for Sustain */ CH_WRITE_4(ch, 0x38, 0x0000); /* LowPassFilter for Keyoff */ CH_WRITE_4(ch, 0x3c, 0x0000); /* LowPassFilter for Release */ CH_WRITE_4(ch, 0x40, 0x0000); /* LowPassFilter transition for Attack, Decay */ CH_WRITE_4(ch, 0x44, 0x0000); /* LowPassFilter transition for Decay, Release */ for (off = 0x48; off < 0x80; off+=4) { CH_WRITE_4(ch, off, 0x0000); /* other = 0 */ } } REG_WRITE_4(0x2800, 0x000f); /* Master volume: Max */ } inline int in_first_half(unsigned int loophalf) { REG_WRITE_1(0x280d, 0); /* select channel 0 */ return REG_READ_4(0x2814) < loophalf; } inline int in_second_half(unsigned int loophalf) { REG_WRITE_1(0x280d, 0); /* select channel 0 */ return REG_READ_4(0x2814) >= loophalf; } uint32_t rate2reg(unsigned int rate) { uint32_t base, fns; int oct; base = 44100 << 7; for (oct = 7; oct >= -8 && rate < base; oct--) base >>= 1; if (rate < base) return (oct << 11) & 0xf800; rate -= base; #if 0 /* (base / 2) : round off */ fns = (rate * 1024 + (base / 2)) / base; #else /* avoid using udivsi3() */ { uint32_t tmp = (rate * 1024 + (base / 2)); for (fns = 0; tmp >= base; tmp -= base, fns++) ; } #endif /* adjustment */ if (fns == 1024) { oct++; fns = 0; } else { if ((rate > base * fns / 1024) && (fns < 1023) && (rate == base * (fns + 1) / 1024)) { fns++; } else if ((rate < base * fns / 1024) && (fns > 0) && (rate == base * (fns - 1)/ 1024)) { fns--; } } return ((oct << 11) & 0xf800) + fns; } void aica_stop(void) { CH_WRITE_4(0, 0x00, 0x8000); CH_WRITE_4(1, 0x00, 0x8000); memset((void *)AICA_DMABUF_LEFT, 0, AICA_DMABUF_SIZE); memset((void *)AICA_DMABUF_RIGHT, 0, AICA_DMABUF_SIZE); } void aica_main(void) { volatile aica_cmd_t *aicacmd = (volatile aica_cmd_t *)AICA_ARM_CMD; int play_state; unsigned int loopend = 0,loophalf = 0; unsigned int blksize = 0, ratepitch; uint32_t cmd, serial; aica_init(); REG_WRITE_4(0x28b4, 0x0020); /* INT Enable to SH4 */ memset((void *)AICA_DMABUF_LEFT, 0, AICA_DMABUF_SIZE); memset((void *)AICA_DMABUF_RIGHT, 0, AICA_DMABUF_SIZE); play_state = 0; serial = aicacmd->serial = 0; for (;;) { if (serial != aicacmd->serial) { serial = aicacmd->serial; cmd = aicacmd->command; aicacmd->command = AICA_COMMAND_NOP; } else { cmd = AICA_COMMAND_NOP; } switch (cmd) { case AICA_COMMAND_NOP: /* * AICA_COMMAND_NOP - Idle process */ switch (play_state) { case 0: /* not playing */ break; case 1: /* first half */ if (in_second_half(loophalf)) { /* Send INT to SH4 */ REG_WRITE_4(0x28b8, 0x0020); play_state = 2; } break; case 2: /* second half */ if (in_first_half(loophalf)) { /* Send INT to SH4 */ REG_WRITE_4(0x28b8, 0x0020); play_state = 1; } break; case 3: if (in_second_half(loophalf)) { aica_stop(); play_state = 0; } break; case 4: if (in_first_half(loophalf)) { aica_stop(); play_state = 0; } break; } break; case AICA_COMMAND_PLAY: aica_stop(); play_state = 0; blksize = aicacmd->blocksize; REG_WRITE_4(0x28b4, 0x0020); /* INT Enable to SH4 */ CH_WRITE_4(0, 0x00, 0x8000); CH_WRITE_4(1, 0x00, 0x8000); switch (aicacmd->precision) { case 16: loopend = blksize; break; case 8: loopend = blksize * 2; break; case 4: loopend = blksize * 4; break; } loophalf = loopend / 2; ratepitch = rate2reg(aicacmd->rate); /* setup left */ CH_WRITE_4(0, 0x08, 0); /* loop start */ CH_WRITE_4(0, 0x0c, loopend); /* loop end */ CH_WRITE_4(0, 0x18, ratepitch); /* SamplingRate */ CH_WRITE_4(0, 0x24, 0x0f1f); /* volume MAX, right PAN */ /* setup right */ CH_WRITE_4(1, 0x08,0); /* loop start */ CH_WRITE_4(1, 0x0c, loopend); /* loop end */ CH_WRITE_4(1, 0x18, ratepitch); /* SamplingRate */ CH_WRITE_4(1, 0x24, 0x0f0f); /* volume MAX, right PAN */ { uint32_t mode, lparam, rparam; if (aicacmd->precision == 4) mode = 3 << 7; /* 4bit ADPCM */ else if (aicacmd->precision == 8) mode = 1 << 7; /* 8bit */ else mode = 0; /* 16bit */ switch (aicacmd->channel) { case 2: CH_WRITE_4(0, 0x04, AICA_DMABUF_LEFT & 0xffff); CH_WRITE_4(1, 0x04, AICA_DMABUF_RIGHT & 0xffff); lparam = 0xc000 /*PLAY*/ | 0x0200 /*LOOP*/ | mode | (AICA_DMABUF_LEFT >> 16); rparam = 0xc000 /*PLAY*/ | 0x0200 /*LOOP*/ | mode | (AICA_DMABUF_RIGHT >> 16); CH_WRITE_4(0, 0x00, lparam); CH_WRITE_4(1, 0x00, rparam); break; case 1: CH_WRITE_1(0, 0x24, 0); /* middle balance */ CH_WRITE_4(0, 0x04, AICA_DMABUF_LEFT & 0xffff); CH_WRITE_4(0, 0x00, 0xc000 /*PLAY*/ | 0x0200 /*LOOP*/ | mode | (AICA_DMABUF_LEFT >> 16)); break; } } play_state = 1; break; case AICA_COMMAND_STOP: switch (play_state) { case 1: memset((void *)(AICA_DMABUF_LEFT + blksize), 0, blksize); memset((void *)(AICA_DMABUF_RIGHT + blksize), 0, blksize); play_state = 3; break; case 2: memset((void *)AICA_DMABUF_LEFT, 0, blksize); memset((void *)AICA_DMABUF_RIGHT, 0, blksize); play_state = 4; break; default: aica_stop(); play_state = 0; break; } break; case AICA_COMMAND_INIT: aica_stop(); play_state = 0; break; case AICA_COMMAND_MVOL: REG_WRITE_4(0x2800, L256TO16(aicacmd->l_param)); break; case AICA_COMMAND_VOL: CH_WRITE_1(0, 0x29, 0xff - (aicacmd->l_param & 0xff)); CH_WRITE_1(1, 0x29, 0xff - (aicacmd->r_param & 0xff)); break; } } }