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