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