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