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