1 /* $NetBSD: aica_arm.c,v 1.4 2005/12/24 20:06:59 perry Exp $ */
4 * Copyright (c) 2003 SHIMIZU Ryo <ryo@misakimix.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 typedef unsigned char uint8_t;
32 typedef unsigned short uint16_t;
33 typedef unsigned long uint32_t;
35 #include <arch/dreamcast/dev/g2/aicavar.h>
37 #define DC_REG_ADDR 0x00800000
39 #define REG_READ_1(off) \
40 (*(volatile uint8_t *)(DC_REG_ADDR + (off)))
41 #define REG_READ_2(off) \
42 (*(volatile uint16_t *)(DC_REG_ADDR + (off)))
43 #define REG_READ_4(off) \
44 (*(volatile uint32_t *)(DC_REG_ADDR + (off)))
45 #define REG_WRITE_1(off,val) \
46 ((*(volatile uint8_t *)(DC_REG_ADDR + (off))) = (val))
47 #define REG_WRITE_2(off,val) \
48 ((*(volatile uint16_t *)(DC_REG_ADDR + (off))) = (val))
49 #define REG_WRITE_4(off,val) \
50 ((*(volatile uint32_t *)((DC_REG_ADDR)+(off))) = (val))
52 #define CH_READ_1(ch,off) REG_READ_1(((ch) << 7) + (off))
53 #define CH_READ_2(ch,off) REG_READ_2(((ch) << 7) + (off))
54 #define CH_READ_4(ch,off) REG_READ_4(((ch) << 7) + (off))
55 #define CH_WRITE_1(ch,off,val) REG_WRITE_1(((ch) << 7) + (off), val)
56 #define CH_WRITE_2(ch,off,val) REG_WRITE_2(((ch) << 7) + (off), val)
57 #define CH_WRITE_4(ch,off,val) REG_WRITE_4(((ch) << 7) + (off), val)
60 inline int in_first_half(unsigned int);
61 inline int in_second_half(unsigned int);
62 void bzero_4(void *, unsigned int);
63 void bzero(void *, unsigned int);
64 uint32_t rate2reg(unsigned int);
73 /* Initialize AICA channels */
74 REG_WRITE_4(0x2800, 0x0000); /* Master volume: Min */
76 for (ch
= 0; ch
< 64; ch
++) {
77 CH_WRITE_4(ch
, 0x00, 0x8000); /* Key off */
78 CH_WRITE_4(ch
, 0x04, 0x0000); /* DataAddress (low) */
79 CH_WRITE_4(ch
, 0x08, 0x0000); /* LoopStartPosition */
80 CH_WRITE_4(ch
, 0x0c, 0x0000); /* LoopEndPosition */
81 CH_WRITE_4(ch
, 0x10, 0x001f); /* AR = 0x1f = 0 msec */
82 CH_WRITE_4(ch
, 0x14, 0x001f); /* RR = 0x1f = 0 msec */
83 CH_WRITE_4(ch
, 0x18, 0x0000); /* Pitch */
84 CH_WRITE_4(ch
, 0x1c, 0x0000); /* LFO Control */
85 CH_WRITE_4(ch
, 0x20, 0x0000); /* DSP Channel to send */
86 CH_WRITE_4(ch
, 0x24, 0x0000); /* Pan & Volume */
87 CH_WRITE_4(ch
, 0x28, 0x0024); /* Volume & LowPassFilter */
88 CH_WRITE_4(ch
, 0x2c, 0x0000); /* LowPassFilter for Attack */
89 CH_WRITE_4(ch
, 0x30, 0x0000); /* LowPassFilter for Decay */
90 CH_WRITE_4(ch
, 0x34, 0x0000); /* LowPassFilter for Sustain */
91 CH_WRITE_4(ch
, 0x38, 0x0000); /* LowPassFilter for Keyoff */
92 CH_WRITE_4(ch
, 0x3c, 0x0000); /* LowPassFilter for Release */
93 CH_WRITE_4(ch
, 0x40, 0x0000); /* LowPassFilter transition
95 CH_WRITE_4(ch
, 0x44, 0x0000); /* LowPassFilter transition
98 for (off
= 0x48; off
< 0x80; off
+=4) {
99 CH_WRITE_4(ch
, off
, 0x0000); /* other = 0 */
103 REG_WRITE_4(0x2800, 0x000f); /* Master volume: Max */
108 in_first_half(unsigned int loophalf
)
111 REG_WRITE_1(0x280d, 0); /* select channel 0 */
112 return REG_READ_4(0x2814) < loophalf
;
116 in_second_half(unsigned int loophalf
)
119 REG_WRITE_1(0x280d, 0); /* select channel 0 */
120 return REG_READ_4(0x2814) >= loophalf
;
125 bzero_4(void *b
, unsigned int len
)
130 len
= (len
+ 3) & ~3;
131 for (; len
!= 0; len
-= 4)
136 bzero(void *b
,unsigned int len
)
141 for (; len
!= 0; len
--)
147 rate2reg(unsigned int rate
)
153 for (oct
= 7; oct
>= -8 && rate
< base
; oct
--)
157 return (oct
<< 11) & 0xf800;
162 /* (base / 2) : round off */
163 fns
= (rate
* 1024 + (base
/ 2)) / base
;
165 /* avoid using udivsi3() */
167 uint32_t tmp
= (rate
* 1024 + (base
/ 2));
168 for (fns
= 0; tmp
>= base
; tmp
-= base
, fns
++)
178 if ((rate
> base
* fns
/ 1024) &&
180 (rate
== base
* (fns
+ 1) / 1024)) {
182 } else if ((rate
< base
* fns
/ 1024) &&
184 (rate
== base
* (fns
- 1)/ 1024)) {
189 return ((oct
<< 11) & 0xf800) + fns
;
198 CH_WRITE_4(0, 0x00, 0x8000);
199 CH_WRITE_4(1, 0x00, 0x8000);
200 bzero_4((void *)AICA_DMABUF_LEFT
, AICA_DMABUF_SIZE
);
201 bzero_4((void *)AICA_DMABUF_RIGHT
, AICA_DMABUF_SIZE
);
207 volatile aica_cmd_t
*aicacmd
= (volatile aica_cmd_t
*)AICA_ARM_CMD
;
209 unsigned int loopend
= 0,loophalf
= 0;
210 unsigned int blksize
= 0, ratepitch
;
211 uint32_t cmd
, serial
;
215 REG_WRITE_4(0x28b4, 0x0020); /* INT Enable to SH4 */
217 bzero_4((void *)AICA_DMABUF_LEFT
, AICA_DMABUF_SIZE
);
218 bzero_4((void *)AICA_DMABUF_RIGHT
, AICA_DMABUF_SIZE
);
221 serial
= aicacmd
->serial
= 0;
224 if (serial
!= aicacmd
->serial
) {
225 serial
= aicacmd
->serial
;
226 cmd
= aicacmd
->command
;
227 aicacmd
->command
= AICA_COMMAND_NOP
;
229 cmd
= AICA_COMMAND_NOP
;
233 case AICA_COMMAND_NOP
:
235 * AICA_COMMAND_NOP - Idle process
237 switch (play_state
) {
238 case 0: /* not playing */
240 case 1: /* first half */
241 if (in_second_half(loophalf
)) {
242 /* Send INT to SH4 */
243 REG_WRITE_4(0x28b8, 0x0020);
247 case 2: /* second half */
248 if (in_first_half(loophalf
)) {
249 /* Send INT to SH4 */
250 REG_WRITE_4(0x28b8, 0x0020);
255 if (in_second_half(loophalf
)) {
261 if (in_first_half(loophalf
)) {
269 case AICA_COMMAND_PLAY
:
270 blksize
= aicacmd
->blocksize
;
272 CH_WRITE_4(0, 0x00, 0x8000);
273 CH_WRITE_4(1, 0x00, 0x8000);
275 switch (aicacmd
->precision
) {
280 loopend
= blksize
* 2;
283 loopend
= blksize
* 4;
286 loophalf
= loopend
/ 2;
288 ratepitch
= rate2reg(aicacmd
->rate
);
291 CH_WRITE_4(0, 0x08, 0); /* loop start */
292 CH_WRITE_4(0, 0x0c, loopend
); /* loop end */
293 CH_WRITE_4(0, 0x18, ratepitch
); /* SamplingRate */
294 CH_WRITE_4(0, 0x24, 0x0f1f); /* volume MAX,
298 CH_WRITE_4(1, 0x08,0); /* loop start */
299 CH_WRITE_4(1, 0x0c, loopend
); /* loop end */
300 CH_WRITE_4(1, 0x18, ratepitch
); /* SamplingRate */
301 CH_WRITE_4(1, 0x24, 0x0f0f); /* volume MAX,
305 uint32_t mode
, lparam
, rparam
;
307 if (aicacmd
->precision
== 4)
308 mode
= 3 << 7; /* 4bit ADPCM */
309 else if (aicacmd
->precision
== 8)
310 mode
= 1 << 7; /* 8bit */
312 mode
= 0; /* 16bit */
314 switch (aicacmd
->channel
) {
317 AICA_DMABUF_LEFT
& 0xffff);
319 AICA_DMABUF_RIGHT
& 0xffff);
320 lparam
= 0xc000 /*PLAY*/ |
321 0x0200 /*LOOP*/ | mode
|
322 (AICA_DMABUF_LEFT
>> 16);
323 rparam
= 0xc000 /*PLAY*/ |
324 0x0200 /*LOOP*/ | mode
|
325 (AICA_DMABUF_RIGHT
>> 16);
326 CH_WRITE_4(0, 0x00, lparam
);
327 CH_WRITE_4(1, 0x00, rparam
);
330 CH_WRITE_1(0, 0x24, 0); /* middle
333 AICA_DMABUF_LEFT
& 0xffff);
334 CH_WRITE_4(0, 0x00, 0xc000 /*PLAY*/ |
335 0x0200 /*LOOP*/ | mode
|
336 (AICA_DMABUF_LEFT
>> 16));
343 case AICA_COMMAND_STOP
:
344 switch (play_state
) {
346 bzero_4((void *)(AICA_DMABUF_LEFT
+ blksize
),
348 bzero_4((void *)(AICA_DMABUF_RIGHT
+ blksize
),
353 bzero_4((void *)AICA_DMABUF_LEFT
, blksize
);
354 bzero_4((void *)AICA_DMABUF_RIGHT
, blksize
);
364 case AICA_COMMAND_INIT
:
369 case AICA_COMMAND_MVOL
:
370 REG_WRITE_4(0x2800, L256TO16(aicacmd
->l_param
));
373 case AICA_COMMAND_VOL
:
374 CH_WRITE_1(0, 0x29, 0xff - (aicacmd
->l_param
& 0xff));
375 CH_WRITE_1(1, 0x29, 0xff - (aicacmd
->r_param
& 0xff));