]> Git Repo - qemu.git/blob - hw/milkymist-ac97.c
Spell "unkown" correctly in error_report() arguments
[qemu.git] / hw / milkymist-ac97.c
1 /*
2  *  QEMU model of the Milkymist System Controller.
3  *
4  *  Copyright (c) 2010 Michael Walle <[email protected]>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  *
20  * Specification available at:
21  *   http://www.milkymist.org/socdoc/ac97.pdf
22  */
23
24 #include "hw.h"
25 #include "sysbus.h"
26 #include "trace.h"
27 #include "audio/audio.h"
28 #include "qemu-error.h"
29
30 enum {
31     R_AC97_CTRL = 0,
32     R_AC97_ADDR,
33     R_AC97_DATAOUT,
34     R_AC97_DATAIN,
35     R_D_CTRL,
36     R_D_ADDR,
37     R_D_REMAINING,
38     R_RESERVED,
39     R_U_CTRL,
40     R_U_ADDR,
41     R_U_REMAINING,
42     R_MAX
43 };
44
45 enum {
46     AC97_CTRL_RQEN  = (1<<0),
47     AC97_CTRL_WRITE = (1<<1),
48 };
49
50 enum {
51     CTRL_EN = (1<<0),
52 };
53
54 struct MilkymistAC97State {
55     SysBusDevice busdev;
56
57     QEMUSoundCard card;
58     SWVoiceIn *voice_in;
59     SWVoiceOut *voice_out;
60
61     uint32_t regs[R_MAX];
62
63     qemu_irq crrequest_irq;
64     qemu_irq crreply_irq;
65     qemu_irq dmar_irq;
66     qemu_irq dmaw_irq;
67 };
68 typedef struct MilkymistAC97State MilkymistAC97State;
69
70 static void update_voices(MilkymistAC97State *s)
71 {
72     if (s->regs[R_D_CTRL] & CTRL_EN) {
73         AUD_set_active_out(s->voice_out, 1);
74     } else {
75         AUD_set_active_out(s->voice_out, 0);
76     }
77
78     if (s->regs[R_U_CTRL] & CTRL_EN) {
79         AUD_set_active_in(s->voice_in, 1);
80     } else {
81         AUD_set_active_in(s->voice_in, 0);
82     }
83 }
84
85 static uint32_t ac97_read(void *opaque, target_phys_addr_t addr)
86 {
87     MilkymistAC97State *s = opaque;
88     uint32_t r = 0;
89
90     addr >>= 2;
91     switch (addr) {
92     case R_AC97_CTRL:
93     case R_AC97_ADDR:
94     case R_AC97_DATAOUT:
95     case R_AC97_DATAIN:
96     case R_D_CTRL:
97     case R_D_ADDR:
98     case R_D_REMAINING:
99     case R_U_CTRL:
100     case R_U_ADDR:
101     case R_U_REMAINING:
102         r = s->regs[addr];
103         break;
104
105     default:
106         error_report("milkymist_ac97: read access to unknown register 0x"
107                 TARGET_FMT_plx, addr << 2);
108         break;
109     }
110
111     trace_milkymist_ac97_memory_read(addr << 2, r);
112
113     return r;
114 }
115
116 static void ac97_write(void *opaque, target_phys_addr_t addr, uint32_t value)
117 {
118     MilkymistAC97State *s = opaque;
119
120     trace_milkymist_ac97_memory_write(addr, value);
121
122     addr >>= 2;
123     switch (addr) {
124     case R_AC97_CTRL:
125         /* always raise an IRQ according to the direction */
126         if (value & AC97_CTRL_RQEN) {
127             if (value & AC97_CTRL_WRITE) {
128                 trace_milkymist_ac97_pulse_irq_crrequest();
129                 qemu_irq_pulse(s->crrequest_irq);
130             } else {
131                 trace_milkymist_ac97_pulse_irq_crreply();
132                 qemu_irq_pulse(s->crreply_irq);
133             }
134         }
135
136         /* RQEN is self clearing */
137         s->regs[addr] = value & ~AC97_CTRL_RQEN;
138         break;
139     case R_D_CTRL:
140     case R_U_CTRL:
141         s->regs[addr] = value;
142         update_voices(s);
143         break;
144     case R_AC97_ADDR:
145     case R_AC97_DATAOUT:
146     case R_AC97_DATAIN:
147     case R_D_ADDR:
148     case R_D_REMAINING:
149     case R_U_ADDR:
150     case R_U_REMAINING:
151         s->regs[addr] = value;
152         break;
153
154     default:
155         error_report("milkymist_ac97: write access to unknown register 0x"
156                 TARGET_FMT_plx, addr);
157         break;
158     }
159
160 }
161
162 static CPUReadMemoryFunc * const ac97_read_fn[] = {
163     NULL,
164     NULL,
165     &ac97_read,
166 };
167
168 static CPUWriteMemoryFunc * const ac97_write_fn[] = {
169     NULL,
170     NULL,
171     &ac97_write,
172 };
173
174 static void ac97_in_cb(void *opaque, int avail_b)
175 {
176     MilkymistAC97State *s = opaque;
177     uint8_t buf[4096];
178     uint32_t remaining = s->regs[R_U_REMAINING];
179     int temp = audio_MIN(remaining, avail_b);
180     uint32_t addr = s->regs[R_U_ADDR];
181     int transferred = 0;
182
183     trace_milkymist_ac97_in_cb(avail_b, remaining);
184
185     /* prevent from raising an IRQ */
186     if (temp == 0) {
187         return;
188     }
189
190     while (temp) {
191         int acquired, to_copy;
192
193         to_copy = audio_MIN(temp, sizeof(buf));
194         acquired = AUD_read(s->voice_in, buf, to_copy);
195         if (!acquired) {
196             break;
197         }
198
199         cpu_physical_memory_write(addr, buf, acquired);
200
201         temp -= acquired;
202         addr += acquired;
203         transferred += acquired;
204     }
205
206     trace_milkymist_ac97_in_cb_transferred(transferred);
207
208     s->regs[R_U_ADDR] = addr;
209     s->regs[R_U_REMAINING] -= transferred;
210
211     if ((s->regs[R_U_CTRL] & CTRL_EN) && (s->regs[R_U_REMAINING] == 0)) {
212         trace_milkymist_ac97_pulse_irq_dmaw();
213         qemu_irq_pulse(s->dmaw_irq);
214     }
215 }
216
217 static void ac97_out_cb(void *opaque, int free_b)
218 {
219     MilkymistAC97State *s = opaque;
220     uint8_t buf[4096];
221     uint32_t remaining = s->regs[R_D_REMAINING];
222     int temp = audio_MIN(remaining, free_b);
223     uint32_t addr = s->regs[R_D_ADDR];
224     int transferred = 0;
225
226     trace_milkymist_ac97_out_cb(free_b, remaining);
227
228     /* prevent from raising an IRQ */
229     if (temp == 0) {
230         return;
231     }
232
233     while (temp) {
234         int copied, to_copy;
235
236         to_copy = audio_MIN(temp, sizeof(buf));
237         cpu_physical_memory_read(addr, buf, to_copy);
238         copied = AUD_write(s->voice_out, buf, to_copy);
239         if (!copied) {
240             break;
241         }
242         temp -= copied;
243         addr += copied;
244         transferred += copied;
245     }
246
247     trace_milkymist_ac97_out_cb_transferred(transferred);
248
249     s->regs[R_D_ADDR] = addr;
250     s->regs[R_D_REMAINING] -= transferred;
251
252     if ((s->regs[R_D_CTRL] & CTRL_EN) && (s->regs[R_D_REMAINING] == 0)) {
253         trace_milkymist_ac97_pulse_irq_dmar();
254         qemu_irq_pulse(s->dmar_irq);
255     }
256 }
257
258 static void milkymist_ac97_reset(DeviceState *d)
259 {
260     MilkymistAC97State *s = container_of(d, MilkymistAC97State, busdev.qdev);
261     int i;
262
263     for (i = 0; i < R_MAX; i++) {
264         s->regs[i] = 0;
265     }
266
267     AUD_set_active_in(s->voice_in, 0);
268     AUD_set_active_out(s->voice_out, 0);
269 }
270
271 static int ac97_post_load(void *opaque, int version_id)
272 {
273     MilkymistAC97State *s = opaque;
274
275     update_voices(s);
276
277     return 0;
278 }
279
280 static int milkymist_ac97_init(SysBusDevice *dev)
281 {
282     MilkymistAC97State *s = FROM_SYSBUS(typeof(*s), dev);
283     int ac97_regs;
284
285     struct audsettings as;
286     sysbus_init_irq(dev, &s->crrequest_irq);
287     sysbus_init_irq(dev, &s->crreply_irq);
288     sysbus_init_irq(dev, &s->dmar_irq);
289     sysbus_init_irq(dev, &s->dmaw_irq);
290
291     AUD_register_card("Milkymist AC'97", &s->card);
292
293     as.freq = 48000;
294     as.nchannels = 2;
295     as.fmt = AUD_FMT_S16;
296     as.endianness = 1;
297
298     s->voice_in = AUD_open_in(&s->card, s->voice_in,
299             "mm_ac97.in", s, ac97_in_cb, &as);
300     s->voice_out = AUD_open_out(&s->card, s->voice_out,
301             "mm_ac97.out", s, ac97_out_cb, &as);
302
303     ac97_regs = cpu_register_io_memory(ac97_read_fn, ac97_write_fn, s,
304             DEVICE_NATIVE_ENDIAN);
305     sysbus_init_mmio(dev, R_MAX * 4, ac97_regs);
306
307     return 0;
308 }
309
310 static const VMStateDescription vmstate_milkymist_ac97 = {
311     .name = "milkymist-ac97",
312     .version_id = 1,
313     .minimum_version_id = 1,
314     .minimum_version_id_old = 1,
315     .post_load = ac97_post_load,
316     .fields      = (VMStateField[]) {
317         VMSTATE_UINT32_ARRAY(regs, MilkymistAC97State, R_MAX),
318         VMSTATE_END_OF_LIST()
319     }
320 };
321
322 static SysBusDeviceInfo milkymist_ac97_info = {
323     .init = milkymist_ac97_init,
324     .qdev.name  = "milkymist-ac97",
325     .qdev.size  = sizeof(MilkymistAC97State),
326     .qdev.vmsd  = &vmstate_milkymist_ac97,
327     .qdev.reset = milkymist_ac97_reset,
328 };
329
330 static void milkymist_ac97_register(void)
331 {
332     sysbus_register_withprop(&milkymist_ac97_info);
333 }
334
335 device_init(milkymist_ac97_register)
This page took 0.042867 seconds and 4 git commands to generate.