]> git.proxmox.com Git - mirror_qemu.git/blame - hw/audio/cs4231a.c
Include hw/qdev-properties.h less
[mirror_qemu.git] / hw / audio / cs4231a.c
CommitLineData
cc53d26d 1/*
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 */
0b8fa32f 24
6086a565 25#include "qemu/osdep.h"
8a824e4d 26#include "hw/audio/soundhw.h"
cc53d26d 27#include "audio/audio.h"
64552b6b 28#include "hw/irq.h"
0d09e41a 29#include "hw/isa/isa.h"
a27bd6c7 30#include "hw/qdev-properties.h"
d6454270 31#include "migration/vmstate.h"
0b8fa32f 32#include "qemu/module.h"
1de7afc9 33#include "qemu/timer.h"
c9073238 34#include "qapi/error.h"
cc53d26d 35
36/*
37 Missing features:
38 ADC
39 Loopback
40 Timer
41 ADPCM
42 More...
43*/
44
45/* #define DEBUG */
77599a1f 46/* #define DEBUG_XLAW */
cc53d26d 47
48static struct {
cc53d26d 49 int aci_counter;
f8ba7846 50} conf = {1};
cc53d26d 51
52#ifdef DEBUG
53#define dolog(...) AUD_log ("cs4231a", __VA_ARGS__)
54#else
55#define dolog(...)
56#endif
57
58#define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__)
59#define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__)
60
61#define CS_REGS 16
62#define CS_DREGS 32
63
a3dcca56
AF
64#define TYPE_CS4231A "cs4231a"
65#define CS4231A(obj) OBJECT_CHECK (CSState, (obj), TYPE_CS4231A)
66
cc53d26d 67typedef struct CSState {
f8ba7846 68 ISADevice dev;
cc53d26d 69 QEMUSoundCard card;
beae3979 70 MemoryRegion ioports;
3a38d437 71 qemu_irq pic;
cc53d26d 72 uint32_t regs[CS_REGS];
73 uint8_t dregs[CS_DREGS];
f8ba7846
GH
74 uint32_t irq;
75 uint32_t dma;
76 uint32_t port;
2d011091 77 IsaDma *isa_dma;
cc53d26d 78 int shift;
79 int dma_running;
80 int audio_free;
81 int transferred;
82 int aci_counter;
83 SWVoiceOut *voice;
84 int16_t *tab;
85} CSState;
86
cc53d26d 87#define MODE2 (1 << 6)
88#define MCE (1 << 6)
89#define PMCE (1 << 4)
90#define CMCE (1 << 5)
91#define TE (1 << 6)
92#define PEN (1 << 0)
93#define INT (1 << 0)
94#define IEN (1 << 1)
95#define PPIO (1 << 6)
96#define PI (1 << 4)
97#define CI (1 << 5)
98#define TI (1 << 6)
99
100enum {
101 Index_Address,
102 Index_Data,
103 Status,
104 PIO_Data
105};
106
107enum {
108 Left_ADC_Input_Control,
109 Right_ADC_Input_Control,
110 Left_AUX1_Input_Control,
111 Right_AUX1_Input_Control,
112 Left_AUX2_Input_Control,
113 Right_AUX2_Input_Control,
114 Left_DAC_Output_Control,
115 Right_DAC_Output_Control,
116 FS_And_Playback_Data_Format,
117 Interface_Configuration,
118 Pin_Control,
119 Error_Status_And_Initialization,
120 MODE_And_ID,
121 Loopback_Control,
122 Playback_Upper_Base_Count,
123 Playback_Lower_Base_Count,
124 Alternate_Feature_Enable_I,
125 Alternate_Feature_Enable_II,
126 Left_Line_Input_Control,
127 Right_Line_Input_Control,
128 Timer_Low_Base,
129 Timer_High_Base,
130 RESERVED,
131 Alternate_Feature_Enable_III,
132 Alternate_Feature_Status,
133 Version_Chip_ID,
134 Mono_Input_And_Output_Control,
135 RESERVED_2,
136 Capture_Data_Format,
137 RESERVED_3,
138 Capture_Upper_Base_Count,
139 Capture_Lower_Base_Count
140};
141
142static int freqs[2][8] = {
143 { 8000, 16000, 27420, 32000, -1, -1, 48000, 9000 },
144 { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
145};
146
147/* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
148static int16_t MuLawDecompressTable[256] =
149{
150 -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
151 -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
152 -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
153 -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
154 -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
155 -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
156 -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
157 -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
158 -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
159 -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
160 -876, -844, -812, -780, -748, -716, -684, -652,
161 -620, -588, -556, -524, -492, -460, -428, -396,
162 -372, -356, -340, -324, -308, -292, -276, -260,
163 -244, -228, -212, -196, -180, -164, -148, -132,
164 -120, -112, -104, -96, -88, -80, -72, -64,
165 -56, -48, -40, -32, -24, -16, -8, 0,
166 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
167 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
168 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
169 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
170 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
171 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
172 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
173 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
174 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
175 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
176 876, 844, 812, 780, 748, 716, 684, 652,
177 620, 588, 556, 524, 492, 460, 428, 396,
178 372, 356, 340, 324, 308, 292, 276, 260,
179 244, 228, 212, 196, 180, 164, 148, 132,
180 120, 112, 104, 96, 88, 80, 72, 64,
181 56, 48, 40, 32, 24, 16, 8, 0
182};
183
184static int16_t ALawDecompressTable[256] =
185{
186 -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
187 -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
188 -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
189 -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
190 -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
191 -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
192 -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
193 -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
194 -344, -328, -376, -360, -280, -264, -312, -296,
195 -472, -456, -504, -488, -408, -392, -440, -424,
196 -88, -72, -120, -104, -24, -8, -56, -40,
197 -216, -200, -248, -232, -152, -136, -184, -168,
198 -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
199 -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
200 -688, -656, -752, -720, -560, -528, -624, -592,
201 -944, -912, -1008, -976, -816, -784, -880, -848,
202 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
203 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
204 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
205 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
206 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
207 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
208 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
209 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
210 344, 328, 376, 360, 280, 264, 312, 296,
211 472, 456, 504, 488, 408, 392, 440, 424,
212 88, 72, 120, 104, 24, 8, 56, 40,
213 216, 200, 248, 232, 152, 136, 184, 168,
214 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
215 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
216 688, 656, 752, 720, 560, 528, 624, 592,
217 944, 912, 1008, 976, 816, 784, 880, 848
218};
219
a3dcca56 220static void cs4231a_reset (DeviceState *dev)
cc53d26d 221{
a3dcca56 222 CSState *s = CS4231A (dev);
cc53d26d 223
224 s->regs[Index_Address] = 0x40;
225 s->regs[Index_Data] = 0x00;
226 s->regs[Status] = 0x00;
227 s->regs[PIO_Data] = 0x00;
228
229 s->dregs[Left_ADC_Input_Control] = 0x00;
230 s->dregs[Right_ADC_Input_Control] = 0x00;
231 s->dregs[Left_AUX1_Input_Control] = 0x88;
232 s->dregs[Right_AUX1_Input_Control] = 0x88;
233 s->dregs[Left_AUX2_Input_Control] = 0x88;
234 s->dregs[Right_AUX2_Input_Control] = 0x88;
235 s->dregs[Left_DAC_Output_Control] = 0x80;
236 s->dregs[Right_DAC_Output_Control] = 0x80;
237 s->dregs[FS_And_Playback_Data_Format] = 0x00;
238 s->dregs[Interface_Configuration] = 0x08;
239 s->dregs[Pin_Control] = 0x00;
240 s->dregs[Error_Status_And_Initialization] = 0x00;
241 s->dregs[MODE_And_ID] = 0x8a;
242 s->dregs[Loopback_Control] = 0x00;
243 s->dregs[Playback_Upper_Base_Count] = 0x00;
244 s->dregs[Playback_Lower_Base_Count] = 0x00;
245 s->dregs[Alternate_Feature_Enable_I] = 0x00;
246 s->dregs[Alternate_Feature_Enable_II] = 0x00;
247 s->dregs[Left_Line_Input_Control] = 0x88;
248 s->dregs[Right_Line_Input_Control] = 0x88;
249 s->dregs[Timer_Low_Base] = 0x00;
250 s->dregs[Timer_High_Base] = 0x00;
251 s->dregs[RESERVED] = 0x00;
252 s->dregs[Alternate_Feature_Enable_III] = 0x00;
253 s->dregs[Alternate_Feature_Status] = 0x00;
254 s->dregs[Version_Chip_ID] = 0xa0;
255 s->dregs[Mono_Input_And_Output_Control] = 0xa0;
256 s->dregs[RESERVED_2] = 0x00;
257 s->dregs[Capture_Data_Format] = 0x00;
258 s->dregs[RESERVED_3] = 0x00;
259 s->dregs[Capture_Upper_Base_Count] = 0x00;
260 s->dregs[Capture_Lower_Base_Count] = 0x00;
261}
262
263static void cs_audio_callback (void *opaque, int free)
264{
265 CSState *s = opaque;
266 s->audio_free = free;
267}
268
269static void cs_reset_voices (CSState *s, uint32_t val)
270{
271 int xtal;
1ea879e5 272 struct audsettings as;
2d011091 273 IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
cc53d26d 274
275#ifdef DEBUG_XLAW
276 if (val == 0 || val == 32)
277 val = (1 << 4) | (1 << 5);
278#endif
279
280 xtal = val & 1;
281 as.freq = freqs[xtal][(val >> 1) & 7];
282
283 if (as.freq == -1) {
284 lerr ("unsupported frequency (val=%#x)\n", val);
285 goto error;
286 }
287
288 as.nchannels = (val & (1 << 4)) ? 2 : 1;
289 as.endianness = 0;
290 s->tab = NULL;
291
292 switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
293 case 0:
85bc5852 294 as.fmt = AUDIO_FORMAT_U8;
cc53d26d 295 s->shift = as.nchannels == 2;
296 break;
297
298 case 1:
299 s->tab = MuLawDecompressTable;
300 goto x_law;
301 case 3:
302 s->tab = ALawDecompressTable;
303 x_law:
85bc5852 304 as.fmt = AUDIO_FORMAT_S16;
cc53d26d 305 as.endianness = AUDIO_HOST_ENDIANNESS;
306 s->shift = as.nchannels == 2;
307 break;
308
309 case 6:
310 as.endianness = 1;
edd7541b 311 /* fall through */
cc53d26d 312 case 2:
85bc5852 313 as.fmt = AUDIO_FORMAT_S16;
cc53d26d 314 s->shift = as.nchannels;
315 break;
316
317 case 7:
318 case 4:
319 lerr ("attempt to use reserved format value (%#x)\n", val);
320 goto error;
321
322 case 5:
323 lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
324 goto error;
325 }
326
327 s->voice = AUD_open_out (
328 &s->card,
329 s->voice,
330 "cs4231a",
331 s,
332 cs_audio_callback,
333 &as
334 );
335
336 if (s->dregs[Interface_Configuration] & PEN) {
337 if (!s->dma_running) {
2d011091 338 k->hold_DREQ(s->isa_dma, s->dma);
cc53d26d 339 AUD_set_active_out (s->voice, 1);
340 s->transferred = 0;
341 }
342 s->dma_running = 1;
343 }
344 else {
345 if (s->dma_running) {
2d011091 346 k->release_DREQ(s->isa_dma, s->dma);
cc53d26d 347 AUD_set_active_out (s->voice, 0);
348 }
349 s->dma_running = 0;
350 }
351 return;
352
353 error:
354 if (s->dma_running) {
2d011091 355 k->release_DREQ(s->isa_dma, s->dma);
cc53d26d 356 AUD_set_active_out (s->voice, 0);
357 }
358}
359
a8170e5e 360static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size)
cc53d26d 361{
362 CSState *s = opaque;
363 uint32_t saddr, iaddr, ret;
364
beae3979 365 saddr = addr;
cc53d26d 366 iaddr = ~0U;
367
368 switch (saddr) {
369 case Index_Address:
370 ret = s->regs[saddr] & ~0x80;
371 break;
372
373 case Index_Data:
374 if (!(s->dregs[MODE_And_ID] & MODE2))
375 iaddr = s->regs[Index_Address] & 0x0f;
376 else
377 iaddr = s->regs[Index_Address] & 0x1f;
378
379 ret = s->dregs[iaddr];
380 if (iaddr == Error_Status_And_Initialization) {
381 /* keep SEAL happy */
382 if (s->aci_counter) {
383 ret |= 1 << 5;
384 s->aci_counter -= 1;
385 }
386 }
387 break;
388
389 default:
390 ret = s->regs[saddr];
391 break;
392 }
393 dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
394 return ret;
395}
396
a8170e5e 397static void cs_write (void *opaque, hwaddr addr,
8acbc9b2 398 uint64_t val64, unsigned size)
cc53d26d 399{
400 CSState *s = opaque;
beae3979 401 uint32_t saddr, iaddr, val;
cc53d26d 402
beae3979
RH
403 saddr = addr;
404 val = val64;
cc53d26d 405
406 switch (saddr) {
407 case Index_Address:
408 if (!(s->regs[Index_Address] & MCE) && (val & MCE)
409 && (s->dregs[Interface_Configuration] & (3 << 3)))
410 s->aci_counter = conf.aci_counter;
411
412 s->regs[Index_Address] = val & ~(1 << 7);
413 break;
414
415 case Index_Data:
416 if (!(s->dregs[MODE_And_ID] & MODE2))
417 iaddr = s->regs[Index_Address] & 0x0f;
418 else
419 iaddr = s->regs[Index_Address] & 0x1f;
420
421 switch (iaddr) {
422 case RESERVED:
423 case RESERVED_2:
424 case RESERVED_3:
425 lwarn ("attempt to write %#x to reserved indirect register %d\n",
426 val, iaddr);
427 break;
428
429 case FS_And_Playback_Data_Format:
430 if (s->regs[Index_Address] & MCE) {
431 cs_reset_voices (s, val);
432 }
433 else {
434 if (s->dregs[Alternate_Feature_Status] & PMCE) {
435 val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
436 cs_reset_voices (s, val);
437 }
438 else {
439 lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
440 s->regs[Index_Address],
441 s->dregs[Alternate_Feature_Status],
442 val);
443 break;
444 }
445 }
446 s->dregs[iaddr] = val;
447 break;
448
449 case Interface_Configuration:
450 val &= ~(1 << 5); /* D5 is reserved */
451 s->dregs[iaddr] = val;
452 if (val & PPIO) {
453 lwarn ("PIO is not supported (%#x)\n", val);
454 break;
455 }
456 if (val & PEN) {
457 if (!s->dma_running) {
458 cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
459 }
460 }
461 else {
462 if (s->dma_running) {
2d011091
HP
463 IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
464 k->release_DREQ(s->isa_dma, s->dma);
cc53d26d 465 AUD_set_active_out (s->voice, 0);
466 s->dma_running = 0;
467 }
468 }
469 break;
470
471 case Error_Status_And_Initialization:
472 lwarn ("attempt to write to read only register %d\n", iaddr);
473 break;
474
475 case MODE_And_ID:
476 dolog ("val=%#x\n", val);
477 if (val & MODE2)
478 s->dregs[iaddr] |= MODE2;
479 else
480 s->dregs[iaddr] &= ~MODE2;
481 break;
482
483 case Alternate_Feature_Enable_I:
484 if (val & TE)
485 lerr ("timer is not yet supported\n");
486 s->dregs[iaddr] = val;
487 break;
488
489 case Alternate_Feature_Status:
490 if ((s->dregs[iaddr] & PI) && !(val & PI)) {
491 /* XXX: TI CI */
3a38d437 492 qemu_irq_lower (s->pic);
cc53d26d 493 s->regs[Status] &= ~INT;
494 }
495 s->dregs[iaddr] = val;
496 break;
497
498 case Version_Chip_ID:
499 lwarn ("write to Version_Chip_ID register %#x\n", val);
500 s->dregs[iaddr] = val;
501 break;
502
503 default:
504 s->dregs[iaddr] = val;
505 break;
506 }
507 dolog ("written value %#x to indirect register %d\n", val, iaddr);
508 break;
509
510 case Status:
511 if (s->regs[Status] & INT) {
3a38d437 512 qemu_irq_lower (s->pic);
cc53d26d 513 }
514 s->regs[Status] &= ~INT;
515 s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
516 break;
517
518 case PIO_Data:
519 lwarn ("attempt to write value %#x to PIO register\n", val);
520 break;
521 }
522}
523
524static int cs_write_audio (CSState *s, int nchan, int dma_pos,
525 int dma_len, int len)
526{
527 int temp, net;
528 uint8_t tmpbuf[4096];
2d011091 529 IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
cc53d26d 530
531 temp = len;
532 net = 0;
533
534 while (temp) {
535 int left = dma_len - dma_pos;
536 int copied;
537 size_t to_copy;
538
539 to_copy = audio_MIN (temp, left);
540 if (to_copy > sizeof (tmpbuf)) {
541 to_copy = sizeof (tmpbuf);
542 }
543
2d011091 544 copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy);
cc53d26d 545 if (s->tab) {
546 int i;
547 int16_t linbuf[4096];
548
549 for (i = 0; i < copied; ++i)
550 linbuf[i] = s->tab[tmpbuf[i]];
551 copied = AUD_write (s->voice, linbuf, copied << 1);
552 copied >>= 1;
553 }
554 else {
555 copied = AUD_write (s->voice, tmpbuf, copied);
556 }
557
558 temp -= copied;
559 dma_pos = (dma_pos + copied) % dma_len;
560 net += copied;
561
562 if (!copied) {
563 break;
564 }
565 }
566
567 return net;
568}
569
570static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
571{
572 CSState *s = opaque;
573 int copy, written;
574 int till = -1;
575
576 copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
577
578 if (s->dregs[Pin_Control] & IEN) {
579 till = (s->dregs[Playback_Lower_Base_Count]
580 | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
581 till -= s->transferred;
582 copy = audio_MIN (till, copy);
583 }
584
585 if ((copy <= 0) || (dma_len <= 0)) {
586 return dma_pos;
587 }
588
589 written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
590
591 dma_pos = (dma_pos + written) % dma_len;
592 s->audio_free -= (written << (s->tab != NULL));
593
594 if (written == till) {
595 s->regs[Status] |= INT;
596 s->dregs[Alternate_Feature_Status] |= PI;
597 s->transferred = 0;
3a38d437 598 qemu_irq_raise (s->pic);
cc53d26d 599 }
600 else {
601 s->transferred += written;
602 }
603
604 return dma_pos;
605}
606
1d190d5c 607static int cs4231a_pre_load (void *opaque)
cc53d26d 608{
609 CSState *s = opaque;
cc53d26d 610
1d190d5c 611 if (s->dma_running) {
2d011091
HP
612 IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
613 k->release_DREQ(s->isa_dma, s->dma);
1d190d5c
JQ
614 AUD_set_active_out (s->voice, 0);
615 }
616 s->dma_running = 0;
617 return 0;
cc53d26d 618}
619
1d190d5c 620static int cs4231a_post_load (void *opaque, int version_id)
cc53d26d 621{
622 CSState *s = opaque;
cc53d26d 623
1d190d5c
JQ
624 if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) {
625 s->dma_running = 0;
4143f3e0 626 cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
1d190d5c 627 }
cc53d26d 628 return 0;
629}
630
1d190d5c
JQ
631static const VMStateDescription vmstate_cs4231a = {
632 .name = "cs4231a",
633 .version_id = 1,
634 .minimum_version_id = 1,
1d190d5c
JQ
635 .pre_load = cs4231a_pre_load,
636 .post_load = cs4231a_post_load,
d49805ae 637 .fields = (VMStateField[]) {
cf4dc461 638 VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS),
639 VMSTATE_BUFFER (dregs, CSState),
640 VMSTATE_INT32 (dma_running, CSState),
641 VMSTATE_INT32 (audio_free, CSState),
642 VMSTATE_INT32 (transferred, CSState),
643 VMSTATE_INT32 (aci_counter, CSState),
644 VMSTATE_END_OF_LIST ()
1d190d5c
JQ
645 }
646};
647
beae3979
RH
648static const MemoryRegionOps cs_ioport_ops = {
649 .read = cs_read,
650 .write = cs_write,
651 .impl = {
652 .min_access_size = 1,
653 .max_access_size = 1,
654 }
655};
656
db895a1e 657static void cs4231a_initfn (Object *obj)
cc53d26d 658{
db895a1e
AF
659 CSState *s = CS4231A (obj);
660
64bde0f3
PB
661 memory_region_init_io (&s->ioports, OBJECT(s), &cs_ioport_ops, s,
662 "cs4231a", 4);
db895a1e
AF
663}
664
665static void cs4231a_realizefn (DeviceState *dev, Error **errp)
666{
667 ISADevice *d = ISA_DEVICE (dev);
a3dcca56 668 CSState *s = CS4231A (dev);
2d011091 669 IsaDmaClass *k;
cc53d26d 670
2d011091 671 s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma);
c9073238
TH
672 if (!s->isa_dma) {
673 error_setg(errp, "ISA controller does not support DMA");
674 return;
675 }
676
677 isa_init_irq(d, &s->pic, s->irq);
2d011091
HP
678 k = ISADMA_GET_CLASS(s->isa_dma);
679 k->register_channel(s->isa_dma, s->dma, cs_dma_read, s);
cc53d26d 680
db895a1e 681 isa_register_ioport (d, &s->ioports, s->port);
cc53d26d 682
1a7dafce 683 AUD_register_card ("cs4231a", &s->card);
cc53d26d 684}
f8ba7846 685
36cd6f6f 686static int cs4231a_init (ISABus *bus)
f8ba7846 687{
a3dcca56 688 isa_create_simple (bus, TYPE_CS4231A);
f8ba7846
GH
689 return 0;
690}
691
39bffca2 692static Property cs4231a_properties[] = {
c7bcc85d 693 DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534),
39bffca2
AL
694 DEFINE_PROP_UINT32 ("irq", CSState, irq, 9),
695 DEFINE_PROP_UINT32 ("dma", CSState, dma, 3),
696 DEFINE_PROP_END_OF_LIST (),
697};
698
cf4dc461 699static void cs4231a_class_initfn (ObjectClass *klass, void *data)
8f04ee08 700{
cf4dc461 701 DeviceClass *dc = DEVICE_CLASS (klass);
db895a1e
AF
702
703 dc->realize = cs4231a_realizefn;
a3dcca56 704 dc->reset = cs4231a_reset;
125ee0ed 705 set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
39bffca2
AL
706 dc->desc = "Crystal Semiconductor CS4231A";
707 dc->vmsd = &vmstate_cs4231a;
708 dc->props = cs4231a_properties;
8f04ee08
AL
709}
710
8c43a6f0 711static const TypeInfo cs4231a_info = {
a3dcca56 712 .name = TYPE_CS4231A,
39bffca2
AL
713 .parent = TYPE_ISA_DEVICE,
714 .instance_size = sizeof (CSState),
db895a1e 715 .instance_init = cs4231a_initfn,
39bffca2 716 .class_init = cs4231a_class_initfn,
f8ba7846
GH
717};
718
83f7d43a 719static void cs4231a_register_types (void)
f8ba7846 720{
cf4dc461 721 type_register_static (&cs4231a_info);
36cd6f6f 722 isa_register_soundhw("cs4231a", "CS4231A", cs4231a_init);
f8ba7846 723}
83f7d43a
AF
724
725type_init (cs4231a_register_types)