1/* $NetBSD: aica_arm.c,v 1.9 2024/02/07 04:20:27 msaitoh Exp $ */ 2 3/* 4 * Copyright (c) 2003 Ryo Shimizu 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 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29typedef unsigned char uint8_t; 30typedef unsigned short uint16_t; 31typedef unsigned long uint32_t; 32 33#include <arch/dreamcast/dev/g2/aicavar.h> 34 35#define DC_REG_ADDR 0x00800000 36 37#define REG_READ_1(off) \ 38 (*(volatile uint8_t *)(DC_REG_ADDR + (off))) 39#define REG_READ_2(off) \ 40 (*(volatile uint16_t *)(DC_REG_ADDR + (off))) 41#define REG_READ_4(off) \ 42 (*(volatile uint32_t *)(DC_REG_ADDR + (off))) 43#define REG_WRITE_1(off,val) \ 44 ((*(volatile uint8_t *)(DC_REG_ADDR + (off))) = (val)) 45#define REG_WRITE_2(off,val) \ 46 ((*(volatile uint16_t *)(DC_REG_ADDR + (off))) = (val)) 47#define REG_WRITE_4(off,val) \ 48 ((*(volatile uint32_t *)((DC_REG_ADDR)+(off))) = (val)) 49 50#define CH_READ_1(ch,off) REG_READ_1(((ch) << 7) + (off)) 51#define CH_READ_2(ch,off) REG_READ_2(((ch) << 7) + (off)) 52#define CH_READ_4(ch,off) REG_READ_4(((ch) << 7) + (off)) 53#define CH_WRITE_1(ch,off,val) REG_WRITE_1(((ch) << 7) + (off), val) 54#define CH_WRITE_2(ch,off,val) REG_WRITE_2(((ch) << 7) + (off), val) 55#define CH_WRITE_4(ch,off,val) REG_WRITE_4(((ch) << 7) + (off), val) 56 57void *memset(void *, int, unsigned long); 58 59void aica_init(void); 60inline int in_first_half(unsigned int); 61inline int in_second_half(unsigned int); 62uint32_t rate2reg(unsigned int); 63void aica_stop(void); 64void aica_main(void); 65 66void 67aica_init(void) 68{ 69 int ch, off; 70 71 /* Initialize AICA channels */ 72 REG_WRITE_4(0x2800, 0x0000); /* Master volume: Min */ 73 74 for (ch = 0; ch < 64; ch++) { 75 CH_WRITE_4(ch, 0x00, 0x8000); /* Key off */ 76 CH_WRITE_4(ch, 0x04, 0x0000); /* DataAddress (low) */ 77 CH_WRITE_4(ch, 0x08, 0x0000); /* LoopStartPosition */ 78 CH_WRITE_4(ch, 0x0c, 0x0000); /* LoopEndPosition */ 79 CH_WRITE_4(ch, 0x10, 0x001f); /* AR = 0x1f = 0 msec */ 80 CH_WRITE_4(ch, 0x14, 0x001f); /* RR = 0x1f = 0 msec */ 81 CH_WRITE_4(ch, 0x18, 0x0000); /* Pitch */ 82 CH_WRITE_4(ch, 0x1c, 0x0000); /* LFO Control */ 83 CH_WRITE_4(ch, 0x20, 0x0000); /* DSP Channel to send */ 84 CH_WRITE_4(ch, 0x24, 0x0000); /* Pan & Volume */ 85 CH_WRITE_4(ch, 0x28, 0x0024); /* Volume & LowPassFilter */ 86 CH_WRITE_4(ch, 0x2c, 0x0000); /* LowPassFilter for Attack */ 87 CH_WRITE_4(ch, 0x30, 0x0000); /* LowPassFilter for Decay */ 88 CH_WRITE_4(ch, 0x34, 0x0000); /* LowPassFilter for Sustain */ 89 CH_WRITE_4(ch, 0x38, 0x0000); /* LowPassFilter for Keyoff */ 90 CH_WRITE_4(ch, 0x3c, 0x0000); /* LowPassFilter for Release */ 91 CH_WRITE_4(ch, 0x40, 0x0000); /* LowPassFilter transition 92 for Attack, Decay */ 93 CH_WRITE_4(ch, 0x44, 0x0000); /* LowPassFilter transition 94 for Decay, Release */ 95 96 for (off = 0x48; off < 0x80; off+=4) { 97 CH_WRITE_4(ch, off, 0x0000); /* other = 0 */ 98 } 99 } 100 101 REG_WRITE_4(0x2800, 0x000f); /* Master volume: Max */ 102} 103 104 105inline int 106in_first_half(unsigned int loophalf) 107{ 108 109 REG_WRITE_1(0x280d, 0); /* select channel 0 */ 110 return REG_READ_4(0x2814) < loophalf; 111} 112 113inline int 114in_second_half(unsigned int loophalf) 115{ 116 117 REG_WRITE_1(0x280d, 0); /* select channel 0 */ 118 return REG_READ_4(0x2814) >= loophalf; 119} 120 121uint32_t 122rate2reg(unsigned int rate) 123{ 124 uint32_t base, fns; 125 int oct; 126 127 base = 44100 << 7; 128 for (oct = 7; oct >= -8 && rate < base; oct--) 129 base >>= 1; 130 131 if (rate < base) 132 return (oct << 11) & 0xf800; 133 134 rate -= base; 135 136#if 0 137 /* (base / 2) : round off */ 138 fns = (rate * 1024 + (base / 2)) / base; 139#else 140 /* avoid using udivsi3() */ 141 { 142 uint32_t tmp = (rate * 1024 + (base / 2)); 143 for (fns = 0; tmp >= base; tmp -= base, fns++) 144 ; 145 } 146#endif 147 148 /* adjustment */ 149 if (fns == 1024) { 150 oct++; 151 fns = 0; 152 } else { 153 if ((rate > base * fns / 1024) && 154 (fns < 1023) && 155 (rate == base * (fns + 1) / 1024)) { 156 fns++; 157 } else if ((rate < base * fns / 1024) && 158 (fns > 0) && 159 (rate == base * (fns - 1)/ 1024)) { 160 fns--; 161 } 162 } 163 164 return ((oct << 11) & 0xf800) + fns; 165} 166 167 168 169void 170aica_stop(void) 171{ 172 173 CH_WRITE_4(0, 0x00, 0x8000); 174 CH_WRITE_4(1, 0x00, 0x8000); 175 memset((void *)AICA_DMABUF_LEFT, 0, AICA_DMABUF_SIZE); 176 memset((void *)AICA_DMABUF_RIGHT, 0, AICA_DMABUF_SIZE); 177} 178 179void 180aica_main(void) 181{ 182 volatile aica_cmd_t *aicacmd = (volatile aica_cmd_t *)AICA_ARM_CMD; 183 int play_state; 184 unsigned int loopend = 0,loophalf = 0; 185 unsigned int blksize = 0, ratepitch; 186 uint32_t cmd, serial; 187 188 aica_init(); 189 190 REG_WRITE_4(0x28b4, 0x0020); /* INT Enable to SH4 */ 191 192 memset((void *)AICA_DMABUF_LEFT, 0, AICA_DMABUF_SIZE); 193 memset((void *)AICA_DMABUF_RIGHT, 0, AICA_DMABUF_SIZE); 194 195 play_state = 0; 196 serial = aicacmd->serial = 0; 197 198 for (;;) { 199 if (serial != aicacmd->serial) { 200 serial = aicacmd->serial; 201 cmd = aicacmd->command; 202 aicacmd->command = AICA_COMMAND_NOP; 203 } else { 204 cmd = AICA_COMMAND_NOP; 205 } 206 207 switch (cmd) { 208 case AICA_COMMAND_NOP: 209 /* 210 * AICA_COMMAND_NOP - Idle process 211 */ 212 switch (play_state) { 213 case 0: /* not playing */ 214 break; 215 case 1: /* first half */ 216 if (in_second_half(loophalf)) { 217 /* Send INT to SH4 */ 218 REG_WRITE_4(0x28b8, 0x0020); 219 play_state = 2; 220 } 221 break; 222 case 2: /* second half */ 223 if (in_first_half(loophalf)) { 224 /* Send INT to SH4 */ 225 REG_WRITE_4(0x28b8, 0x0020); 226 play_state = 1; 227 } 228 break; 229 case 3: 230 if (in_second_half(loophalf)) { 231 aica_stop(); 232 play_state = 0; 233 } 234 break; 235 case 4: 236 if (in_first_half(loophalf)) { 237 aica_stop(); 238 play_state = 0; 239 } 240 break; 241 } 242 break; 243 244 case AICA_COMMAND_PLAY: 245 aica_stop(); 246 play_state = 0; 247 248 blksize = aicacmd->blocksize; 249 250 REG_WRITE_4(0x28b4, 0x0020); /* INT Enable to SH4 */ 251 252 CH_WRITE_4(0, 0x00, 0x8000); 253 CH_WRITE_4(1, 0x00, 0x8000); 254 255 switch (aicacmd->precision) { 256 case 16: 257 loopend = blksize; 258 break; 259 case 8: 260 loopend = blksize * 2; 261 break; 262 case 4: 263 loopend = blksize * 4; 264 break; 265 } 266 loophalf = loopend / 2; 267 268 ratepitch = rate2reg(aicacmd->rate); 269 270 /* setup left */ 271 CH_WRITE_4(0, 0x08, 0); /* loop start */ 272 CH_WRITE_4(0, 0x0c, loopend); /* loop end */ 273 CH_WRITE_4(0, 0x18, ratepitch); /* SamplingRate */ 274 CH_WRITE_4(0, 0x24, 0x0f1f); /* volume MAX, 275 right PAN */ 276 277 /* setup right */ 278 CH_WRITE_4(1, 0x08,0); /* loop start */ 279 CH_WRITE_4(1, 0x0c, loopend); /* loop end */ 280 CH_WRITE_4(1, 0x18, ratepitch); /* SamplingRate */ 281 CH_WRITE_4(1, 0x24, 0x0f0f); /* volume MAX, 282 right PAN */ 283 284 { 285 uint32_t mode, lparam, rparam; 286 287 if (aicacmd->precision == 4) 288 mode = 3 << 7; /* 4bit ADPCM */ 289 else if (aicacmd->precision == 8) 290 mode = 1 << 7; /* 8bit */ 291 else 292 mode = 0; /* 16bit */ 293 294 switch (aicacmd->channel) { 295 case 2: 296 CH_WRITE_4(0, 0x04, 297 AICA_DMABUF_LEFT & 0xffff); 298 CH_WRITE_4(1, 0x04, 299 AICA_DMABUF_RIGHT & 0xffff); 300 lparam = 0xc000 /*PLAY*/ | 301 0x0200 /*LOOP*/ | mode | 302 (AICA_DMABUF_LEFT >> 16); 303 rparam = 0xc000 /*PLAY*/ | 304 0x0200 /*LOOP*/ | mode | 305 (AICA_DMABUF_RIGHT >> 16); 306 CH_WRITE_4(0, 0x00, lparam); 307 CH_WRITE_4(1, 0x00, rparam); 308 break; 309 case 1: 310 CH_WRITE_1(0, 0x24, 0); /* middle 311 balance */ 312 CH_WRITE_4(0, 0x04, 313 AICA_DMABUF_LEFT & 0xffff); 314 CH_WRITE_4(0, 0x00, 0xc000 /*PLAY*/ | 315 0x0200 /*LOOP*/ | mode | 316 (AICA_DMABUF_LEFT >> 16)); 317 break; 318 } 319 } 320 play_state = 1; 321 break; 322 323 case AICA_COMMAND_STOP: 324 switch (play_state) { 325 case 1: 326 memset((void *)(AICA_DMABUF_LEFT + blksize), 0, 327 blksize); 328 memset((void *)(AICA_DMABUF_RIGHT + blksize), 0, 329 blksize); 330 play_state = 3; 331 break; 332 case 2: 333 memset((void *)AICA_DMABUF_LEFT, 0, blksize); 334 memset((void *)AICA_DMABUF_RIGHT, 0, blksize); 335 play_state = 4; 336 break; 337 default: 338 aica_stop(); 339 play_state = 0; 340 break; 341 } 342 break; 343 344 case AICA_COMMAND_INIT: 345 aica_stop(); 346 play_state = 0; 347 break; 348 349 case AICA_COMMAND_MVOL: 350 REG_WRITE_4(0x2800, L256TO16(aicacmd->l_param)); 351 break; 352 353 case AICA_COMMAND_VOL: 354 CH_WRITE_1(0, 0x29, 0xff - (aicacmd->l_param & 0xff)); 355 CH_WRITE_1(1, 0x29, 0xff - (aicacmd->r_param & 0xff)); 356 break; 357 358 } 359 } 360} 361