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