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