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