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