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