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