Add aica firmware
[librecmc/linux-libre-firmware.git] / aica / arm / main.c
1 /* KallistiOS ##version##
2
3    main.c
4    (c)2000-2002 Dan Potter
5
6    Generic sound driver with streaming capabilities
7
8    This slightly more complicated version allows for sound effect channels,
9    and full sampling rate, panning, and volume control for each.
10
11 */
12
13 #include "aica_cmd_iface.h"
14 #include "aica.h"
15
16 /****************** Timer *******************************************/
17
18 #define timer (*((volatile uint32 *)AICA_MEM_CLOCK))
19
20 void timer_wait(uint32 jiffies) {
21     uint32 fin = timer + jiffies;
22
23     while(timer <= fin)
24         ;
25 }
26
27 /****************** Tiny Libc ***************************************/
28
29 #include <stddef.h>
30
31 void * memcpy(void *dest, const void *src, size_t count) {
32     unsigned char *tmp = (unsigned char *) dest;
33     unsigned char *s = (unsigned char *) src;
34
35     while(count--)
36         *tmp++ = *s++;
37
38     return dest;
39 }
40
41 /****************** Main Program ************************************/
42
43 /* Our SH-4 interface (statically placed memory structures) */
44 volatile aica_queue_t   *q_cmd = (volatile aica_queue_t *)AICA_MEM_CMD_QUEUE;
45 volatile aica_queue_t   *q_resp = (volatile aica_queue_t *)AICA_MEM_RESP_QUEUE;
46 volatile aica_channel_t *chans = (volatile aica_channel_t *)AICA_MEM_CHANNELS;
47
48 /* Process a CHAN command */
49 void process_chn(uint32 chn, aica_channel_t *chndat) {
50     switch(chndat->cmd & AICA_CH_CMD_MASK) {
51         case AICA_CH_CMD_NONE:
52             break;
53         case AICA_CH_CMD_START:
54
55             if(chndat->cmd & AICA_CH_START_SYNC) {
56                 aica_sync_play(chn);
57             }
58             else {
59                 memcpy((void*)(chans + chn), chndat, sizeof(aica_channel_t));
60                 chans[chn].pos = 0;
61                 aica_play(chn, chndat->cmd & AICA_CH_START_DELAY);
62             }
63
64             break;
65         case AICA_CH_CMD_STOP:
66             aica_stop(chn);
67             break;
68         case AICA_CH_CMD_UPDATE:
69
70             if(chndat->cmd & AICA_CH_UPDATE_SET_FREQ) {
71                 chans[chn].freq = chndat->freq;
72                 aica_freq(chn);
73             }
74
75             if(chndat->cmd & AICA_CH_UPDATE_SET_VOL) {
76                 chans[chn].vol = chndat->vol;
77                 aica_vol(chn);
78             }
79
80             if(chndat->cmd & AICA_CH_UPDATE_SET_PAN) {
81                 chans[chn].pan = chndat->pan;
82                 aica_pan(chn);
83             }
84
85             break;
86         default:
87             /* error */
88             break;
89     }
90 }
91
92 /* Process one packet of queue data */
93 uint32 process_one(uint32 tail) {
94     uint32      pktdata[AICA_CMD_MAX_SIZE], *pdptr, size, i;
95     volatile uint32 * src;
96     aica_cmd_t  * pkt;
97
98     src = (volatile uint32 *)(q_cmd->data + tail);
99     pkt = (aica_cmd_t *)pktdata;
100     pdptr = pktdata;
101
102     /* Get the size field */
103     size = *src;
104
105     if(size > AICA_CMD_MAX_SIZE)
106         size = AICA_CMD_MAX_SIZE;
107
108     /* Copy out the packet data */
109     for(i = 0; i < size; i++) {
110         *pdptr++ = *src++;
111
112         if((uint32)src >= (q_cmd->data + q_cmd->size))
113             src = (volatile uint32 *)q_cmd->data;
114     }
115
116     /* Figure out what type of packet it is */
117     switch(pkt->cmd) {
118         case AICA_CMD_NONE:
119             break;
120         case AICA_CMD_PING:
121             /* Not implemented yet */
122             break;
123         case AICA_CMD_CHAN:
124             process_chn(pkt->cmd_id, (aica_channel_t *)pkt->cmd_data);
125             break;
126         case AICA_CMD_SYNC_CLOCK:
127             /* Reset our timer clock to zero */
128             timer = 0;
129             break;
130         default:
131             /* error */
132             break;
133     }
134
135     return size;
136 }
137
138 /* Look for an available request in the command queue; if one is there
139    then process it and move the tail pointer. */
140 void process_cmd_queue() {
141     uint32      head, tail, tsloc, ts;
142
143     /* Grab these values up front in case SH-4 changes head */
144     head = q_cmd->head;
145     tail = q_cmd->tail;
146
147     /* Do we have anything to process? */
148     while(head != tail) {
149         /* Look at the next packet. If our clock isn't there yet, then
150            we won't process anything yet either. */
151         tsloc = tail + offsetof(aica_cmd_t, timestamp);
152
153         if(tsloc >= q_cmd->size)
154             tsloc -= q_cmd->size;
155
156         ts = *((volatile uint32*)(q_cmd->data + tsloc));
157
158         if(ts > 0 && ts >= timer)
159             return;
160
161         /* Process it */
162         ts = process_one(tail);
163
164         /* Ok, skip over the packet */
165         tail += ts * 4;
166
167         if(tail >= q_cmd->size)
168             tail -= q_cmd->size;
169
170         q_cmd->tail = tail;
171     }
172 }
173
174 int arm_main() {
175     int i;
176
177     /* Setup our queues */
178     q_cmd->head = q_cmd->tail = 0;
179     q_cmd->data = AICA_MEM_CMD_QUEUE + sizeof(aica_queue_t);
180     q_cmd->size = AICA_MEM_RESP_QUEUE - q_cmd->data;
181     q_cmd->process_ok = 1;
182     q_cmd->valid = 1;
183
184     q_resp->head = q_resp->tail = 0;
185     q_resp->data = AICA_MEM_RESP_QUEUE + sizeof(aica_queue_t);
186     q_resp->size = AICA_MEM_CHANNELS - q_resp->data;
187     q_resp->process_ok = 1;
188     q_resp->valid = 1;
189
190     /* Initialize the AICA part of the SPU */
191     aica_init();
192
193     /* Wait for a command */
194     for(; ;) {
195         /* Update channel position counters */
196         for(i = 0; i < 64; i++)
197             aica_get_pos(i);
198
199         /* Check for a command */
200         if(q_cmd->process_ok)
201             process_cmd_queue();
202
203         /* Little delay to prevent memory lock */
204         timer_wait(10);
205     }
206 }