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