blob: d482e04ac981a7e09dee6965ecad9defc32b9844 [file] [log] [blame]
malccc53d262008-06-13 10:48:22 +00001/*
2 * QEMU Crystal CS4231 audio chip emulation
3 *
4 * Copyright (c) 2006 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include "hw.h"
25#include "audiodev.h"
26#include "audio/audio.h"
27#include "isa.h"
28#include "qemu-timer.h"
29
30/*
31 Missing features:
32 ADC
33 Loopback
34 Timer
35 ADPCM
36 More...
37*/
38
39/* #define DEBUG */
malc77599a12008-06-21 17:15:00 +000040/* #define DEBUG_XLAW */
malccc53d262008-06-13 10:48:22 +000041
42static struct {
43 int irq;
44 int dma;
45 int port;
46 int aci_counter;
47} conf = {9, 3, 0x534, 1};
48
49#ifdef DEBUG
50#define dolog(...) AUD_log ("cs4231a", __VA_ARGS__)
51#else
52#define dolog(...)
53#endif
54
55#define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__)
56#define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__)
57
58#define CS_REGS 16
59#define CS_DREGS 32
60
61typedef struct CSState {
62 QEMUSoundCard card;
Jes Sorensen3a38d432009-08-14 11:36:15 +020063 qemu_irq pic;
malccc53d262008-06-13 10:48:22 +000064 uint32_t regs[CS_REGS];
65 uint8_t dregs[CS_DREGS];
malccc53d262008-06-13 10:48:22 +000066 int dma;
67 int port;
68 int shift;
69 int dma_running;
70 int audio_free;
71 int transferred;
72 int aci_counter;
73 SWVoiceOut *voice;
74 int16_t *tab;
75} CSState;
76
77#define IO_READ_PROTO(name) \
78 static uint32_t name (void *opaque, uint32_t addr)
79
80#define IO_WRITE_PROTO(name) \
81 static void name (void *opaque, uint32_t addr, uint32_t val)
82
83#define GET_SADDR(addr) (addr & 3)
84
85#define MODE2 (1 << 6)
86#define MCE (1 << 6)
87#define PMCE (1 << 4)
88#define CMCE (1 << 5)
89#define TE (1 << 6)
90#define PEN (1 << 0)
91#define INT (1 << 0)
92#define IEN (1 << 1)
93#define PPIO (1 << 6)
94#define PI (1 << 4)
95#define CI (1 << 5)
96#define TI (1 << 6)
97
98enum {
99 Index_Address,
100 Index_Data,
101 Status,
102 PIO_Data
103};
104
105enum {
106 Left_ADC_Input_Control,
107 Right_ADC_Input_Control,
108 Left_AUX1_Input_Control,
109 Right_AUX1_Input_Control,
110 Left_AUX2_Input_Control,
111 Right_AUX2_Input_Control,
112 Left_DAC_Output_Control,
113 Right_DAC_Output_Control,
114 FS_And_Playback_Data_Format,
115 Interface_Configuration,
116 Pin_Control,
117 Error_Status_And_Initialization,
118 MODE_And_ID,
119 Loopback_Control,
120 Playback_Upper_Base_Count,
121 Playback_Lower_Base_Count,
122 Alternate_Feature_Enable_I,
123 Alternate_Feature_Enable_II,
124 Left_Line_Input_Control,
125 Right_Line_Input_Control,
126 Timer_Low_Base,
127 Timer_High_Base,
128 RESERVED,
129 Alternate_Feature_Enable_III,
130 Alternate_Feature_Status,
131 Version_Chip_ID,
132 Mono_Input_And_Output_Control,
133 RESERVED_2,
134 Capture_Data_Format,
135 RESERVED_3,
136 Capture_Upper_Base_Count,
137 Capture_Lower_Base_Count
138};
139
140static int freqs[2][8] = {
141 { 8000, 16000, 27420, 32000, -1, -1, 48000, 9000 },
142 { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
143};
144
145/* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
146static int16_t MuLawDecompressTable[256] =
147{
148 -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
149 -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
150 -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
151 -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
152 -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
153 -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
154 -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
155 -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
156 -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
157 -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
158 -876, -844, -812, -780, -748, -716, -684, -652,
159 -620, -588, -556, -524, -492, -460, -428, -396,
160 -372, -356, -340, -324, -308, -292, -276, -260,
161 -244, -228, -212, -196, -180, -164, -148, -132,
162 -120, -112, -104, -96, -88, -80, -72, -64,
163 -56, -48, -40, -32, -24, -16, -8, 0,
164 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
165 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
166 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
167 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
168 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
169 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
170 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
171 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
172 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
173 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
174 876, 844, 812, 780, 748, 716, 684, 652,
175 620, 588, 556, 524, 492, 460, 428, 396,
176 372, 356, 340, 324, 308, 292, 276, 260,
177 244, 228, 212, 196, 180, 164, 148, 132,
178 120, 112, 104, 96, 88, 80, 72, 64,
179 56, 48, 40, 32, 24, 16, 8, 0
180};
181
182static int16_t ALawDecompressTable[256] =
183{
184 -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
185 -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
186 -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
187 -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
188 -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
189 -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
190 -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
191 -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
192 -344, -328, -376, -360, -280, -264, -312, -296,
193 -472, -456, -504, -488, -408, -392, -440, -424,
194 -88, -72, -120, -104, -24, -8, -56, -40,
195 -216, -200, -248, -232, -152, -136, -184, -168,
196 -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
197 -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
198 -688, -656, -752, -720, -560, -528, -624, -592,
199 -944, -912, -1008, -976, -816, -784, -880, -848,
200 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
201 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
202 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
203 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
204 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
205 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
206 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
207 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
208 344, 328, 376, 360, 280, 264, 312, 296,
209 472, 456, 504, 488, 408, 392, 440, 424,
210 88, 72, 120, 104, 24, 8, 56, 40,
211 216, 200, 248, 232, 152, 136, 184, 168,
212 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
213 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
214 688, 656, 752, 720, 560, 528, 624, 592,
215 944, 912, 1008, 976, 816, 784, 880, 848
216};
217
malcd999f7e2009-06-20 05:13:29 +0400218static void cs_reset (void *opaque)
malccc53d262008-06-13 10:48:22 +0000219{
220 CSState *s = opaque;
221
222 s->regs[Index_Address] = 0x40;
223 s->regs[Index_Data] = 0x00;
224 s->regs[Status] = 0x00;
225 s->regs[PIO_Data] = 0x00;
226
227 s->dregs[Left_ADC_Input_Control] = 0x00;
228 s->dregs[Right_ADC_Input_Control] = 0x00;
229 s->dregs[Left_AUX1_Input_Control] = 0x88;
230 s->dregs[Right_AUX1_Input_Control] = 0x88;
231 s->dregs[Left_AUX2_Input_Control] = 0x88;
232 s->dregs[Right_AUX2_Input_Control] = 0x88;
233 s->dregs[Left_DAC_Output_Control] = 0x80;
234 s->dregs[Right_DAC_Output_Control] = 0x80;
235 s->dregs[FS_And_Playback_Data_Format] = 0x00;
236 s->dregs[Interface_Configuration] = 0x08;
237 s->dregs[Pin_Control] = 0x00;
238 s->dregs[Error_Status_And_Initialization] = 0x00;
239 s->dregs[MODE_And_ID] = 0x8a;
240 s->dregs[Loopback_Control] = 0x00;
241 s->dregs[Playback_Upper_Base_Count] = 0x00;
242 s->dregs[Playback_Lower_Base_Count] = 0x00;
243 s->dregs[Alternate_Feature_Enable_I] = 0x00;
244 s->dregs[Alternate_Feature_Enable_II] = 0x00;
245 s->dregs[Left_Line_Input_Control] = 0x88;
246 s->dregs[Right_Line_Input_Control] = 0x88;
247 s->dregs[Timer_Low_Base] = 0x00;
248 s->dregs[Timer_High_Base] = 0x00;
249 s->dregs[RESERVED] = 0x00;
250 s->dregs[Alternate_Feature_Enable_III] = 0x00;
251 s->dregs[Alternate_Feature_Status] = 0x00;
252 s->dregs[Version_Chip_ID] = 0xa0;
253 s->dregs[Mono_Input_And_Output_Control] = 0xa0;
254 s->dregs[RESERVED_2] = 0x00;
255 s->dregs[Capture_Data_Format] = 0x00;
256 s->dregs[RESERVED_3] = 0x00;
257 s->dregs[Capture_Upper_Base_Count] = 0x00;
258 s->dregs[Capture_Lower_Base_Count] = 0x00;
259}
260
261static void cs_audio_callback (void *opaque, int free)
262{
263 CSState *s = opaque;
264 s->audio_free = free;
265}
266
267static void cs_reset_voices (CSState *s, uint32_t val)
268{
269 int xtal;
malc1ea879e2008-12-03 22:48:44 +0000270 struct audsettings as;
malccc53d262008-06-13 10:48:22 +0000271
272#ifdef DEBUG_XLAW
273 if (val == 0 || val == 32)
274 val = (1 << 4) | (1 << 5);
275#endif
276
277 xtal = val & 1;
278 as.freq = freqs[xtal][(val >> 1) & 7];
279
280 if (as.freq == -1) {
281 lerr ("unsupported frequency (val=%#x)\n", val);
282 goto error;
283 }
284
285 as.nchannels = (val & (1 << 4)) ? 2 : 1;
286 as.endianness = 0;
287 s->tab = NULL;
288
289 switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
290 case 0:
291 as.fmt = AUD_FMT_U8;
292 s->shift = as.nchannels == 2;
293 break;
294
295 case 1:
296 s->tab = MuLawDecompressTable;
297 goto x_law;
298 case 3:
299 s->tab = ALawDecompressTable;
300 x_law:
301 as.fmt = AUD_FMT_S16;
302 as.endianness = AUDIO_HOST_ENDIANNESS;
303 s->shift = as.nchannels == 2;
304 break;
305
306 case 6:
307 as.endianness = 1;
308 case 2:
309 as.fmt = AUD_FMT_S16;
310 s->shift = as.nchannels;
311 break;
312
313 case 7:
314 case 4:
315 lerr ("attempt to use reserved format value (%#x)\n", val);
316 goto error;
317
318 case 5:
319 lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
320 goto error;
321 }
322
323 s->voice = AUD_open_out (
324 &s->card,
325 s->voice,
326 "cs4231a",
327 s,
328 cs_audio_callback,
329 &as
330 );
331
332 if (s->dregs[Interface_Configuration] & PEN) {
333 if (!s->dma_running) {
334 DMA_hold_DREQ (s->dma);
335 AUD_set_active_out (s->voice, 1);
336 s->transferred = 0;
337 }
338 s->dma_running = 1;
339 }
340 else {
341 if (s->dma_running) {
342 DMA_release_DREQ (s->dma);
343 AUD_set_active_out (s->voice, 0);
344 }
345 s->dma_running = 0;
346 }
347 return;
348
349 error:
350 if (s->dma_running) {
351 DMA_release_DREQ (s->dma);
352 AUD_set_active_out (s->voice, 0);
353 }
354}
355
356IO_READ_PROTO (cs_read)
357{
358 CSState *s = opaque;
359 uint32_t saddr, iaddr, ret;
360
361 saddr = GET_SADDR (addr);
362 iaddr = ~0U;
363
364 switch (saddr) {
365 case Index_Address:
366 ret = s->regs[saddr] & ~0x80;
367 break;
368
369 case Index_Data:
370 if (!(s->dregs[MODE_And_ID] & MODE2))
371 iaddr = s->regs[Index_Address] & 0x0f;
372 else
373 iaddr = s->regs[Index_Address] & 0x1f;
374
375 ret = s->dregs[iaddr];
376 if (iaddr == Error_Status_And_Initialization) {
377 /* keep SEAL happy */
378 if (s->aci_counter) {
379 ret |= 1 << 5;
380 s->aci_counter -= 1;
381 }
382 }
383 break;
384
385 default:
386 ret = s->regs[saddr];
387 break;
388 }
389 dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
390 return ret;
391}
392
393IO_WRITE_PROTO (cs_write)
394{
395 CSState *s = opaque;
396 uint32_t saddr, iaddr;
397
398 saddr = GET_SADDR (addr);
399
400 switch (saddr) {
401 case Index_Address:
402 if (!(s->regs[Index_Address] & MCE) && (val & MCE)
403 && (s->dregs[Interface_Configuration] & (3 << 3)))
404 s->aci_counter = conf.aci_counter;
405
406 s->regs[Index_Address] = val & ~(1 << 7);
407 break;
408
409 case Index_Data:
410 if (!(s->dregs[MODE_And_ID] & MODE2))
411 iaddr = s->regs[Index_Address] & 0x0f;
412 else
413 iaddr = s->regs[Index_Address] & 0x1f;
414
415 switch (iaddr) {
416 case RESERVED:
417 case RESERVED_2:
418 case RESERVED_3:
419 lwarn ("attempt to write %#x to reserved indirect register %d\n",
420 val, iaddr);
421 break;
422
423 case FS_And_Playback_Data_Format:
424 if (s->regs[Index_Address] & MCE) {
425 cs_reset_voices (s, val);
426 }
427 else {
428 if (s->dregs[Alternate_Feature_Status] & PMCE) {
429 val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
430 cs_reset_voices (s, val);
431 }
432 else {
433 lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
434 s->regs[Index_Address],
435 s->dregs[Alternate_Feature_Status],
436 val);
437 break;
438 }
439 }
440 s->dregs[iaddr] = val;
441 break;
442
443 case Interface_Configuration:
444 val &= ~(1 << 5); /* D5 is reserved */
445 s->dregs[iaddr] = val;
446 if (val & PPIO) {
447 lwarn ("PIO is not supported (%#x)\n", val);
448 break;
449 }
450 if (val & PEN) {
451 if (!s->dma_running) {
452 cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
453 }
454 }
455 else {
456 if (s->dma_running) {
457 DMA_release_DREQ (s->dma);
458 AUD_set_active_out (s->voice, 0);
459 s->dma_running = 0;
460 }
461 }
462 break;
463
464 case Error_Status_And_Initialization:
465 lwarn ("attempt to write to read only register %d\n", iaddr);
466 break;
467
468 case MODE_And_ID:
469 dolog ("val=%#x\n", val);
470 if (val & MODE2)
471 s->dregs[iaddr] |= MODE2;
472 else
473 s->dregs[iaddr] &= ~MODE2;
474 break;
475
476 case Alternate_Feature_Enable_I:
477 if (val & TE)
478 lerr ("timer is not yet supported\n");
479 s->dregs[iaddr] = val;
480 break;
481
482 case Alternate_Feature_Status:
483 if ((s->dregs[iaddr] & PI) && !(val & PI)) {
484 /* XXX: TI CI */
Jes Sorensen3a38d432009-08-14 11:36:15 +0200485 qemu_irq_lower (s->pic);
malccc53d262008-06-13 10:48:22 +0000486 s->regs[Status] &= ~INT;
487 }
488 s->dregs[iaddr] = val;
489 break;
490
491 case Version_Chip_ID:
492 lwarn ("write to Version_Chip_ID register %#x\n", val);
493 s->dregs[iaddr] = val;
494 break;
495
496 default:
497 s->dregs[iaddr] = val;
498 break;
499 }
500 dolog ("written value %#x to indirect register %d\n", val, iaddr);
501 break;
502
503 case Status:
504 if (s->regs[Status] & INT) {
Jes Sorensen3a38d432009-08-14 11:36:15 +0200505 qemu_irq_lower (s->pic);
malccc53d262008-06-13 10:48:22 +0000506 }
507 s->regs[Status] &= ~INT;
508 s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
509 break;
510
511 case PIO_Data:
512 lwarn ("attempt to write value %#x to PIO register\n", val);
513 break;
514 }
515}
516
517static int cs_write_audio (CSState *s, int nchan, int dma_pos,
518 int dma_len, int len)
519{
520 int temp, net;
521 uint8_t tmpbuf[4096];
522
523 temp = len;
524 net = 0;
525
526 while (temp) {
527 int left = dma_len - dma_pos;
528 int copied;
529 size_t to_copy;
530
531 to_copy = audio_MIN (temp, left);
532 if (to_copy > sizeof (tmpbuf)) {
533 to_copy = sizeof (tmpbuf);
534 }
535
536 copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
537 if (s->tab) {
538 int i;
539 int16_t linbuf[4096];
540
541 for (i = 0; i < copied; ++i)
542 linbuf[i] = s->tab[tmpbuf[i]];
543 copied = AUD_write (s->voice, linbuf, copied << 1);
544 copied >>= 1;
545 }
546 else {
547 copied = AUD_write (s->voice, tmpbuf, copied);
548 }
549
550 temp -= copied;
551 dma_pos = (dma_pos + copied) % dma_len;
552 net += copied;
553
554 if (!copied) {
555 break;
556 }
557 }
558
559 return net;
560}
561
562static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
563{
564 CSState *s = opaque;
565 int copy, written;
566 int till = -1;
567
568 copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
569
570 if (s->dregs[Pin_Control] & IEN) {
571 till = (s->dregs[Playback_Lower_Base_Count]
572 | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
573 till -= s->transferred;
574 copy = audio_MIN (till, copy);
575 }
576
577 if ((copy <= 0) || (dma_len <= 0)) {
578 return dma_pos;
579 }
580
581 written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
582
583 dma_pos = (dma_pos + written) % dma_len;
584 s->audio_free -= (written << (s->tab != NULL));
585
586 if (written == till) {
587 s->regs[Status] |= INT;
588 s->dregs[Alternate_Feature_Status] |= PI;
589 s->transferred = 0;
Jes Sorensen3a38d432009-08-14 11:36:15 +0200590 qemu_irq_raise (s->pic);
malccc53d262008-06-13 10:48:22 +0000591 }
592 else {
593 s->transferred += written;
594 }
595
596 return dma_pos;
597}
598
malcd999f7e2009-06-20 05:13:29 +0400599static void cs_save (QEMUFile *f, void *opaque)
malccc53d262008-06-13 10:48:22 +0000600{
601 CSState *s = opaque;
602 unsigned int i;
malc4143f3e2008-06-13 18:16:59 +0000603 uint32_t val;
malccc53d262008-06-13 10:48:22 +0000604
605 for (i = 0; i < CS_REGS; i++)
malcd999f7e2009-06-20 05:13:29 +0400606 qemu_put_be32s (f, &s->regs[i]);
malccc53d262008-06-13 10:48:22 +0000607
malcd999f7e2009-06-20 05:13:29 +0400608 qemu_put_buffer (f, s->dregs, CS_DREGS);
609 val = s->dma_running; qemu_put_be32s (f, &val);
610 val = s->audio_free; qemu_put_be32s (f, &val);
611 val = s->transferred; qemu_put_be32s (f, &val);
612 val = s->aci_counter; qemu_put_be32s (f, &val);
malccc53d262008-06-13 10:48:22 +0000613}
614
malcd999f7e2009-06-20 05:13:29 +0400615static int cs_load (QEMUFile *f, void *opaque, int version_id)
malccc53d262008-06-13 10:48:22 +0000616{
617 CSState *s = opaque;
618 unsigned int i;
malc4143f3e2008-06-13 18:16:59 +0000619 uint32_t val, dma_running;
malccc53d262008-06-13 10:48:22 +0000620
621 if (version_id > 1)
622 return -EINVAL;
623
624 for (i = 0; i < CS_REGS; i++)
malcd999f7e2009-06-20 05:13:29 +0400625 qemu_get_be32s (f, &s->regs[i]);
malccc53d262008-06-13 10:48:22 +0000626
malcd999f7e2009-06-20 05:13:29 +0400627 qemu_get_buffer (f, s->dregs, CS_DREGS);
malc4143f3e2008-06-13 18:16:59 +0000628
malcd999f7e2009-06-20 05:13:29 +0400629 qemu_get_be32s (f, &dma_running);
630 qemu_get_be32s (f, &val); s->audio_free = val;
631 qemu_get_be32s (f, &val); s->transferred = val;
632 qemu_get_be32s (f, &val); s->aci_counter = val;
malc4143f3e2008-06-13 18:16:59 +0000633 if (dma_running && (s->dregs[Interface_Configuration] & PEN))
634 cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
malccc53d262008-06-13 10:48:22 +0000635 return 0;
636}
637
Paul Brook22d83b12009-05-12 12:33:04 +0100638int cs4231a_init (qemu_irq *pic)
malccc53d262008-06-13 10:48:22 +0000639{
640 int i;
641 CSState *s;
642
malccc53d262008-06-13 10:48:22 +0000643 s = qemu_mallocz (sizeof (*s));
malccc53d262008-06-13 10:48:22 +0000644
Jes Sorensen3a38d432009-08-14 11:36:15 +0200645 s->pic = isa_reserve_irq(conf.irq);
malccc53d262008-06-13 10:48:22 +0000646 s->dma = conf.dma;
647 s->port = conf.port;
648
649 for (i = 0; i < 4; i++) {
650 register_ioport_write (s->port + i, 1, 1, cs_write, s);
651 register_ioport_read (s->port + i, 1, 1, cs_read, s);
652 }
653
654 DMA_register_channel (s->dma, cs_dma_read, s);
655
656 register_savevm ("cs4231a", 0, 1, cs_save, cs_load, s);
Jan Kiszkaa08d4362009-06-27 09:25:07 +0200657 qemu_register_reset (cs_reset, s);
malccc53d262008-06-13 10:48:22 +0000658 cs_reset (s);
659
malc1a7dafc2009-05-14 03:11:35 +0400660 AUD_register_card ("cs4231a", &s->card);
malccc53d262008-06-13 10:48:22 +0000661 return 0;
662}