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