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