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