]> Git Repo - qemu.git/blob - hw/audio/milkymist-ac97.c
Include hw/irq.h a lot less
[qemu.git] / hw / audio / 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://milkymist.walle.cc/socdoc/ac97.pdf
22  */
23
24 #include "qemu/osdep.h"
25 #include "hw/hw.h"
26 #include "hw/irq.h"
27 #include "hw/sysbus.h"
28 #include "trace.h"
29 #include "audio/audio.h"
30 #include "qemu/error-report.h"
31 #include "qemu/module.h"
32
33 enum {
34     R_AC97_CTRL = 0,
35     R_AC97_ADDR,
36     R_AC97_DATAOUT,
37     R_AC97_DATAIN,
38     R_D_CTRL,
39     R_D_ADDR,
40     R_D_REMAINING,
41     R_RESERVED,
42     R_U_CTRL,
43     R_U_ADDR,
44     R_U_REMAINING,
45     R_MAX
46 };
47
48 enum {
49     AC97_CTRL_RQEN  = (1<<0),
50     AC97_CTRL_WRITE = (1<<1),
51 };
52
53 enum {
54     CTRL_EN = (1<<0),
55 };
56
57 #define TYPE_MILKYMIST_AC97 "milkymist-ac97"
58 #define MILKYMIST_AC97(obj) \
59     OBJECT_CHECK(MilkymistAC97State, (obj), TYPE_MILKYMIST_AC97)
60
61 struct MilkymistAC97State {
62     SysBusDevice parent_obj;
63
64     MemoryRegion regs_region;
65
66     QEMUSoundCard card;
67     SWVoiceIn *voice_in;
68     SWVoiceOut *voice_out;
69
70     uint32_t regs[R_MAX];
71
72     qemu_irq crrequest_irq;
73     qemu_irq crreply_irq;
74     qemu_irq dmar_irq;
75     qemu_irq dmaw_irq;
76 };
77 typedef struct MilkymistAC97State MilkymistAC97State;
78
79 static void update_voices(MilkymistAC97State *s)
80 {
81     if (s->regs[R_D_CTRL] & CTRL_EN) {
82         AUD_set_active_out(s->voice_out, 1);
83     } else {
84         AUD_set_active_out(s->voice_out, 0);
85     }
86
87     if (s->regs[R_U_CTRL] & CTRL_EN) {
88         AUD_set_active_in(s->voice_in, 1);
89     } else {
90         AUD_set_active_in(s->voice_in, 0);
91     }
92 }
93
94 static uint64_t ac97_read(void *opaque, hwaddr addr,
95                           unsigned size)
96 {
97     MilkymistAC97State *s = opaque;
98     uint32_t r = 0;
99
100     addr >>= 2;
101     switch (addr) {
102     case R_AC97_CTRL:
103     case R_AC97_ADDR:
104     case R_AC97_DATAOUT:
105     case R_AC97_DATAIN:
106     case R_D_CTRL:
107     case R_D_ADDR:
108     case R_D_REMAINING:
109     case R_U_CTRL:
110     case R_U_ADDR:
111     case R_U_REMAINING:
112         r = s->regs[addr];
113         break;
114
115     default:
116         error_report("milkymist_ac97: read access to unknown register 0x"
117                 TARGET_FMT_plx, addr << 2);
118         break;
119     }
120
121     trace_milkymist_ac97_memory_read(addr << 2, r);
122
123     return r;
124 }
125
126 static void ac97_write(void *opaque, hwaddr addr, uint64_t value,
127                        unsigned size)
128 {
129     MilkymistAC97State *s = opaque;
130
131     trace_milkymist_ac97_memory_write(addr, value);
132
133     addr >>= 2;
134     switch (addr) {
135     case R_AC97_CTRL:
136         /* always raise an IRQ according to the direction */
137         if (value & AC97_CTRL_RQEN) {
138             if (value & AC97_CTRL_WRITE) {
139                 trace_milkymist_ac97_pulse_irq_crrequest();
140                 qemu_irq_pulse(s->crrequest_irq);
141             } else {
142                 trace_milkymist_ac97_pulse_irq_crreply();
143                 qemu_irq_pulse(s->crreply_irq);
144             }
145         }
146
147         /* RQEN is self clearing */
148         s->regs[addr] = value & ~AC97_CTRL_RQEN;
149         break;
150     case R_D_CTRL:
151     case R_U_CTRL:
152         s->regs[addr] = value;
153         update_voices(s);
154         break;
155     case R_AC97_ADDR:
156     case R_AC97_DATAOUT:
157     case R_AC97_DATAIN:
158     case R_D_ADDR:
159     case R_D_REMAINING:
160     case R_U_ADDR:
161     case R_U_REMAINING:
162         s->regs[addr] = value;
163         break;
164
165     default:
166         error_report("milkymist_ac97: write access to unknown register 0x"
167                 TARGET_FMT_plx, addr);
168         break;
169     }
170
171 }
172
173 static const MemoryRegionOps ac97_mmio_ops = {
174     .read = ac97_read,
175     .write = ac97_write,
176     .valid = {
177         .min_access_size = 4,
178         .max_access_size = 4,
179     },
180     .endianness = DEVICE_NATIVE_ENDIAN,
181 };
182
183 static void ac97_in_cb(void *opaque, int avail_b)
184 {
185     MilkymistAC97State *s = opaque;
186     uint8_t buf[4096];
187     uint32_t remaining = s->regs[R_U_REMAINING];
188     int temp = audio_MIN(remaining, avail_b);
189     uint32_t addr = s->regs[R_U_ADDR];
190     int transferred = 0;
191
192     trace_milkymist_ac97_in_cb(avail_b, remaining);
193
194     /* prevent from raising an IRQ */
195     if (temp == 0) {
196         return;
197     }
198
199     while (temp) {
200         int acquired, to_copy;
201
202         to_copy = audio_MIN(temp, sizeof(buf));
203         acquired = AUD_read(s->voice_in, buf, to_copy);
204         if (!acquired) {
205             break;
206         }
207
208         cpu_physical_memory_write(addr, buf, acquired);
209
210         temp -= acquired;
211         addr += acquired;
212         transferred += acquired;
213     }
214
215     trace_milkymist_ac97_in_cb_transferred(transferred);
216
217     s->regs[R_U_ADDR] = addr;
218     s->regs[R_U_REMAINING] -= transferred;
219
220     if ((s->regs[R_U_CTRL] & CTRL_EN) && (s->regs[R_U_REMAINING] == 0)) {
221         trace_milkymist_ac97_pulse_irq_dmaw();
222         qemu_irq_pulse(s->dmaw_irq);
223     }
224 }
225
226 static void ac97_out_cb(void *opaque, int free_b)
227 {
228     MilkymistAC97State *s = opaque;
229     uint8_t buf[4096];
230     uint32_t remaining = s->regs[R_D_REMAINING];
231     int temp = audio_MIN(remaining, free_b);
232     uint32_t addr = s->regs[R_D_ADDR];
233     int transferred = 0;
234
235     trace_milkymist_ac97_out_cb(free_b, remaining);
236
237     /* prevent from raising an IRQ */
238     if (temp == 0) {
239         return;
240     }
241
242     while (temp) {
243         int copied, to_copy;
244
245         to_copy = audio_MIN(temp, sizeof(buf));
246         cpu_physical_memory_read(addr, buf, to_copy);
247         copied = AUD_write(s->voice_out, buf, to_copy);
248         if (!copied) {
249             break;
250         }
251         temp -= copied;
252         addr += copied;
253         transferred += copied;
254     }
255
256     trace_milkymist_ac97_out_cb_transferred(transferred);
257
258     s->regs[R_D_ADDR] = addr;
259     s->regs[R_D_REMAINING] -= transferred;
260
261     if ((s->regs[R_D_CTRL] & CTRL_EN) && (s->regs[R_D_REMAINING] == 0)) {
262         trace_milkymist_ac97_pulse_irq_dmar();
263         qemu_irq_pulse(s->dmar_irq);
264     }
265 }
266
267 static void milkymist_ac97_reset(DeviceState *d)
268 {
269     MilkymistAC97State *s = MILKYMIST_AC97(d);
270     int i;
271
272     for (i = 0; i < R_MAX; i++) {
273         s->regs[i] = 0;
274     }
275
276     AUD_set_active_in(s->voice_in, 0);
277     AUD_set_active_out(s->voice_out, 0);
278 }
279
280 static int ac97_post_load(void *opaque, int version_id)
281 {
282     MilkymistAC97State *s = opaque;
283
284     update_voices(s);
285
286     return 0;
287 }
288
289 static void milkymist_ac97_init(Object *obj)
290 {
291     MilkymistAC97State *s = MILKYMIST_AC97(obj);
292     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
293
294     sysbus_init_irq(dev, &s->crrequest_irq);
295     sysbus_init_irq(dev, &s->crreply_irq);
296     sysbus_init_irq(dev, &s->dmar_irq);
297     sysbus_init_irq(dev, &s->dmaw_irq);
298
299     memory_region_init_io(&s->regs_region, obj, &ac97_mmio_ops, s,
300             "milkymist-ac97", R_MAX * 4);
301     sysbus_init_mmio(dev, &s->regs_region);
302 }
303
304 static void milkymist_ac97_realize(DeviceState *dev, Error **errp)
305 {
306     MilkymistAC97State *s = MILKYMIST_AC97(dev);
307     struct audsettings as;
308
309     AUD_register_card("Milkymist AC'97", &s->card);
310
311     as.freq = 48000;
312     as.nchannels = 2;
313     as.fmt = AUDIO_FORMAT_S16;
314     as.endianness = 1;
315
316     s->voice_in = AUD_open_in(&s->card, s->voice_in,
317             "mm_ac97.in", s, ac97_in_cb, &as);
318     s->voice_out = AUD_open_out(&s->card, s->voice_out,
319             "mm_ac97.out", s, ac97_out_cb, &as);
320 }
321
322 static const VMStateDescription vmstate_milkymist_ac97 = {
323     .name = "milkymist-ac97",
324     .version_id = 1,
325     .minimum_version_id = 1,
326     .post_load = ac97_post_load,
327     .fields = (VMStateField[]) {
328         VMSTATE_UINT32_ARRAY(regs, MilkymistAC97State, R_MAX),
329         VMSTATE_END_OF_LIST()
330     }
331 };
332
333 static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
334 {
335     DeviceClass *dc = DEVICE_CLASS(klass);
336
337     dc->realize = milkymist_ac97_realize;
338     dc->reset = milkymist_ac97_reset;
339     dc->vmsd = &vmstate_milkymist_ac97;
340 }
341
342 static const TypeInfo milkymist_ac97_info = {
343     .name          = TYPE_MILKYMIST_AC97,
344     .parent        = TYPE_SYS_BUS_DEVICE,
345     .instance_size = sizeof(MilkymistAC97State),
346     .instance_init = milkymist_ac97_init,
347     .class_init    = milkymist_ac97_class_init,
348 };
349
350 static void milkymist_ac97_register_types(void)
351 {
352     type_register_static(&milkymist_ac97_info);
353 }
354
355 type_init(milkymist_ac97_register_types)
This page took 0.042281 seconds and 4 git commands to generate.