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