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