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