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