1 /*
2  *  QEMU model of the Milkymist System Controller.
3  *
4  *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
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/irq.h"
26 #include "hw/sysbus.h"
27 #include "migration/vmstate.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 
update_voices(MilkymistAC97State * s)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 
ac97_read(void * opaque,hwaddr addr,unsigned size)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 
ac97_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)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 
ac97_in_cb(void * opaque,int avail_b)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 = 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 = 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 
ac97_out_cb(void * opaque,int free_b)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 = 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 = 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 
milkymist_ac97_reset(DeviceState * d)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 
ac97_post_load(void * opaque,int version_id)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 
milkymist_ac97_init(Object * obj)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 
milkymist_ac97_realize(DeviceState * dev,Error ** errp)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 Property milkymist_ac97_properties[] = {
334     DEFINE_AUDIO_PROPERTIES(MilkymistAC97State, card),
335     DEFINE_PROP_END_OF_LIST(),
336 };
337 
milkymist_ac97_class_init(ObjectClass * klass,void * data)338 static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
339 {
340     DeviceClass *dc = DEVICE_CLASS(klass);
341 
342     dc->realize = milkymist_ac97_realize;
343     dc->reset = milkymist_ac97_reset;
344     dc->vmsd = &vmstate_milkymist_ac97;
345     device_class_set_props(dc, milkymist_ac97_properties);
346 }
347 
348 static const TypeInfo milkymist_ac97_info = {
349     .name          = TYPE_MILKYMIST_AC97,
350     .parent        = TYPE_SYS_BUS_DEVICE,
351     .instance_size = sizeof(MilkymistAC97State),
352     .instance_init = milkymist_ac97_init,
353     .class_init    = milkymist_ac97_class_init,
354 };
355 
milkymist_ac97_register_types(void)356 static void milkymist_ac97_register_types(void)
357 {
358     type_register_static(&milkymist_ac97_info);
359 }
360 
361 type_init(milkymist_ac97_register_types)
362