xref: /openbmc/qemu/hw/audio/cs4231a.c (revision 82c4f87e)
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 = AUD_FMT_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 = AUD_FMT_S16;
302         as.endianness = AUDIO_HOST_ENDIANNESS;
303         s->shift = as.nchannels == 2;
304         break;
305 
306     case 6:
307         as.endianness = 1;
308     case 2:
309         as.fmt = AUD_FMT_S16;
310         s->shift = as.nchannels;
311         break;
312 
313     case 7:
314     case 4:
315         lerr ("attempt to use reserved format value (%#x)\n", val);
316         goto error;
317 
318     case 5:
319         lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
320         goto error;
321     }
322 
323     s->voice = AUD_open_out (
324         &s->card,
325         s->voice,
326         "cs4231a",
327         s,
328         cs_audio_callback,
329         &as
330         );
331 
332     if (s->dregs[Interface_Configuration] & PEN) {
333         if (!s->dma_running) {
334             k->hold_DREQ(s->isa_dma, s->dma);
335             AUD_set_active_out (s->voice, 1);
336             s->transferred = 0;
337         }
338         s->dma_running = 1;
339     }
340     else {
341         if (s->dma_running) {
342             k->release_DREQ(s->isa_dma, s->dma);
343             AUD_set_active_out (s->voice, 0);
344         }
345         s->dma_running = 0;
346     }
347     return;
348 
349  error:
350     if (s->dma_running) {
351         k->release_DREQ(s->isa_dma, s->dma);
352         AUD_set_active_out (s->voice, 0);
353     }
354 }
355 
356 static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size)
357 {
358     CSState *s = opaque;
359     uint32_t saddr, iaddr, ret;
360 
361     saddr = addr;
362     iaddr = ~0U;
363 
364     switch (saddr) {
365     case Index_Address:
366         ret = s->regs[saddr] & ~0x80;
367         break;
368 
369     case Index_Data:
370         if (!(s->dregs[MODE_And_ID] & MODE2))
371             iaddr = s->regs[Index_Address] & 0x0f;
372         else
373             iaddr = s->regs[Index_Address] & 0x1f;
374 
375         ret = s->dregs[iaddr];
376         if (iaddr == Error_Status_And_Initialization) {
377             /* keep SEAL happy */
378             if (s->aci_counter) {
379                 ret |= 1 << 5;
380                 s->aci_counter -= 1;
381             }
382         }
383         break;
384 
385     default:
386         ret = s->regs[saddr];
387         break;
388     }
389     dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
390     return ret;
391 }
392 
393 static void cs_write (void *opaque, hwaddr addr,
394                       uint64_t val64, unsigned size)
395 {
396     CSState *s = opaque;
397     uint32_t saddr, iaddr, val;
398 
399     saddr = addr;
400     val = val64;
401 
402     switch (saddr) {
403     case Index_Address:
404         if (!(s->regs[Index_Address] & MCE) && (val & MCE)
405             && (s->dregs[Interface_Configuration] & (3 << 3)))
406             s->aci_counter = conf.aci_counter;
407 
408         s->regs[Index_Address] = val & ~(1 << 7);
409         break;
410 
411     case Index_Data:
412         if (!(s->dregs[MODE_And_ID] & MODE2))
413             iaddr = s->regs[Index_Address] & 0x0f;
414         else
415             iaddr = s->regs[Index_Address] & 0x1f;
416 
417         switch (iaddr) {
418         case RESERVED:
419         case RESERVED_2:
420         case RESERVED_3:
421             lwarn ("attempt to write %#x to reserved indirect register %d\n",
422                    val, iaddr);
423             break;
424 
425         case FS_And_Playback_Data_Format:
426             if (s->regs[Index_Address] & MCE) {
427                 cs_reset_voices (s, val);
428             }
429             else {
430                 if (s->dregs[Alternate_Feature_Status] & PMCE) {
431                     val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
432                     cs_reset_voices (s, val);
433                 }
434                 else {
435                     lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
436                            s->regs[Index_Address],
437                            s->dregs[Alternate_Feature_Status],
438                            val);
439                     break;
440                 }
441             }
442             s->dregs[iaddr] = val;
443             break;
444 
445         case Interface_Configuration:
446             val &= ~(1 << 5);   /* D5 is reserved */
447             s->dregs[iaddr] = val;
448             if (val & PPIO) {
449                 lwarn ("PIO is not supported (%#x)\n", val);
450                 break;
451             }
452             if (val & PEN) {
453                 if (!s->dma_running) {
454                     cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
455                 }
456             }
457             else {
458                 if (s->dma_running) {
459                     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
460                     k->release_DREQ(s->isa_dma, s->dma);
461                     AUD_set_active_out (s->voice, 0);
462                     s->dma_running = 0;
463                 }
464             }
465             break;
466 
467         case Error_Status_And_Initialization:
468             lwarn ("attempt to write to read only register %d\n", iaddr);
469             break;
470 
471         case MODE_And_ID:
472             dolog ("val=%#x\n", val);
473             if (val & MODE2)
474                 s->dregs[iaddr] |= MODE2;
475             else
476                 s->dregs[iaddr] &= ~MODE2;
477             break;
478 
479         case Alternate_Feature_Enable_I:
480             if (val & TE)
481                 lerr ("timer is not yet supported\n");
482             s->dregs[iaddr] = val;
483             break;
484 
485         case Alternate_Feature_Status:
486             if ((s->dregs[iaddr] & PI) && !(val & PI)) {
487                 /* XXX: TI CI */
488                 qemu_irq_lower (s->pic);
489                 s->regs[Status] &= ~INT;
490             }
491             s->dregs[iaddr] = val;
492             break;
493 
494         case Version_Chip_ID:
495             lwarn ("write to Version_Chip_ID register %#x\n", val);
496             s->dregs[iaddr] = val;
497             break;
498 
499         default:
500             s->dregs[iaddr] = val;
501             break;
502         }
503         dolog ("written value %#x to indirect register %d\n", val, iaddr);
504         break;
505 
506     case Status:
507         if (s->regs[Status] & INT) {
508             qemu_irq_lower (s->pic);
509         }
510         s->regs[Status] &= ~INT;
511         s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
512         break;
513 
514     case PIO_Data:
515         lwarn ("attempt to write value %#x to PIO register\n", val);
516         break;
517     }
518 }
519 
520 static int cs_write_audio (CSState *s, int nchan, int dma_pos,
521                            int dma_len, int len)
522 {
523     int temp, net;
524     uint8_t tmpbuf[4096];
525     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
526 
527     temp = len;
528     net = 0;
529 
530     while (temp) {
531         int left = dma_len - dma_pos;
532         int copied;
533         size_t to_copy;
534 
535         to_copy = audio_MIN (temp, left);
536         if (to_copy > sizeof (tmpbuf)) {
537             to_copy = sizeof (tmpbuf);
538         }
539 
540         copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy);
541         if (s->tab) {
542             int i;
543             int16_t linbuf[4096];
544 
545             for (i = 0; i < copied; ++i)
546                 linbuf[i] = s->tab[tmpbuf[i]];
547             copied = AUD_write (s->voice, linbuf, copied << 1);
548             copied >>= 1;
549         }
550         else {
551             copied = AUD_write (s->voice, tmpbuf, copied);
552         }
553 
554         temp -= copied;
555         dma_pos = (dma_pos + copied) % dma_len;
556         net += copied;
557 
558         if (!copied) {
559             break;
560         }
561     }
562 
563     return net;
564 }
565 
566 static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
567 {
568     CSState *s = opaque;
569     int copy, written;
570     int till = -1;
571 
572     copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
573 
574     if (s->dregs[Pin_Control] & IEN) {
575         till = (s->dregs[Playback_Lower_Base_Count]
576             | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
577         till -= s->transferred;
578         copy = audio_MIN (till, copy);
579     }
580 
581     if ((copy <= 0) || (dma_len <= 0)) {
582         return dma_pos;
583     }
584 
585     written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
586 
587     dma_pos = (dma_pos + written) % dma_len;
588     s->audio_free -= (written << (s->tab != NULL));
589 
590     if (written == till) {
591         s->regs[Status] |= INT;
592         s->dregs[Alternate_Feature_Status] |= PI;
593         s->transferred = 0;
594         qemu_irq_raise (s->pic);
595     }
596     else {
597         s->transferred += written;
598     }
599 
600     return dma_pos;
601 }
602 
603 static int cs4231a_pre_load (void *opaque)
604 {
605     CSState *s = opaque;
606 
607     if (s->dma_running) {
608         IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
609         k->release_DREQ(s->isa_dma, s->dma);
610         AUD_set_active_out (s->voice, 0);
611     }
612     s->dma_running = 0;
613     return 0;
614 }
615 
616 static int cs4231a_post_load (void *opaque, int version_id)
617 {
618     CSState *s = opaque;
619 
620     if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) {
621         s->dma_running = 0;
622         cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
623     }
624     return 0;
625 }
626 
627 static const VMStateDescription vmstate_cs4231a = {
628     .name = "cs4231a",
629     .version_id = 1,
630     .minimum_version_id = 1,
631     .pre_load = cs4231a_pre_load,
632     .post_load = cs4231a_post_load,
633     .fields = (VMStateField[]) {
634         VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS),
635         VMSTATE_BUFFER (dregs, CSState),
636         VMSTATE_INT32 (dma_running, CSState),
637         VMSTATE_INT32 (audio_free, CSState),
638         VMSTATE_INT32 (transferred, CSState),
639         VMSTATE_INT32 (aci_counter, CSState),
640         VMSTATE_END_OF_LIST ()
641     }
642 };
643 
644 static const MemoryRegionOps cs_ioport_ops = {
645     .read = cs_read,
646     .write = cs_write,
647     .impl = {
648         .min_access_size = 1,
649         .max_access_size = 1,
650     }
651 };
652 
653 static void cs4231a_initfn (Object *obj)
654 {
655     CSState *s = CS4231A (obj);
656 
657     memory_region_init_io (&s->ioports, OBJECT(s), &cs_ioport_ops, s,
658                            "cs4231a", 4);
659 }
660 
661 static void cs4231a_realizefn (DeviceState *dev, Error **errp)
662 {
663     ISADevice *d = ISA_DEVICE (dev);
664     CSState *s = CS4231A (dev);
665     IsaDmaClass *k;
666 
667     s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma);
668     if (!s->isa_dma) {
669         error_setg(errp, "ISA controller does not support DMA");
670         return;
671     }
672 
673     isa_init_irq(d, &s->pic, s->irq);
674     k = ISADMA_GET_CLASS(s->isa_dma);
675     k->register_channel(s->isa_dma, s->dma, cs_dma_read, s);
676 
677     isa_register_ioport (d, &s->ioports, s->port);
678 
679     AUD_register_card ("cs4231a", &s->card);
680 }
681 
682 static int cs4231a_init (ISABus *bus)
683 {
684     isa_create_simple (bus, TYPE_CS4231A);
685     return 0;
686 }
687 
688 static Property cs4231a_properties[] = {
689     DEFINE_PROP_UINT32 ("iobase",  CSState, port, 0x534),
690     DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
691     DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
692     DEFINE_PROP_END_OF_LIST (),
693 };
694 
695 static void cs4231a_class_initfn (ObjectClass *klass, void *data)
696 {
697     DeviceClass *dc = DEVICE_CLASS (klass);
698 
699     dc->realize = cs4231a_realizefn;
700     dc->reset = cs4231a_reset;
701     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
702     dc->desc = "Crystal Semiconductor CS4231A";
703     dc->vmsd = &vmstate_cs4231a;
704     dc->props = cs4231a_properties;
705 }
706 
707 static const TypeInfo cs4231a_info = {
708     .name          = TYPE_CS4231A,
709     .parent        = TYPE_ISA_DEVICE,
710     .instance_size = sizeof (CSState),
711     .instance_init = cs4231a_initfn,
712     .class_init    = cs4231a_class_initfn,
713 };
714 
715 static void cs4231a_register_types (void)
716 {
717     type_register_static (&cs4231a_info);
718     isa_register_soundhw("cs4231a", "CS4231A", cs4231a_init);
719 }
720 
721 type_init (cs4231a_register_types)
722