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
  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
  49static 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"
  66typedef struct CSState CSState;
  67DECLARE_INSTANCE_CHECKER(CSState, CS4231A,
  68                         TYPE_CS4231A)
  69
  70struct 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    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
 103enum {
 104    Index_Address,
 105    Index_Data,
 106    Status,
 107    PIO_Data
 108};
 109
 110enum {
 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
 145static 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 */
 151static 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
 187static 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
 223static 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
 266static void cs_audio_callback (void *opaque, int free)
 267{
 268    CSState *s = opaque;
 269    s->audio_free = free;
 270}
 271
 272static 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
 363static 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
 400static 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
 527static 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
 573static 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
 610static 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
 623static 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
 634static 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 = (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
 651static 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
 660static 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
 668static void cs4231a_realizefn (DeviceState *dev, Error **errp)
 669{
 670    ISADevice *d = ISA_DEVICE (dev);
 671    CSState *s = CS4231A (dev);
 672    IsaDmaClass *k;
 673
 674    s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma);
 675    if (!s->isa_dma) {
 676        error_setg(errp, "ISA controller does not support DMA");
 677        return;
 678    }
 679
 680    isa_init_irq(d, &s->pic, s->irq);
 681    k = ISADMA_GET_CLASS(s->isa_dma);
 682    k->register_channel(s->isa_dma, s->dma, cs_dma_read, s);
 683
 684    isa_register_ioport (d, &s->ioports, s->port);
 685
 686    AUD_register_card ("cs4231a", &s->card);
 687}
 688
 689static Property cs4231a_properties[] = {
 690    DEFINE_AUDIO_PROPERTIES(CSState, card),
 691    DEFINE_PROP_UINT32 ("iobase",  CSState, port, 0x534),
 692    DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
 693    DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
 694    DEFINE_PROP_END_OF_LIST (),
 695};
 696
 697static void cs4231a_class_initfn (ObjectClass *klass, void *data)
 698{
 699    DeviceClass *dc = DEVICE_CLASS (klass);
 700
 701    dc->realize = cs4231a_realizefn;
 702    dc->reset = cs4231a_reset;
 703    set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
 704    dc->desc = "Crystal Semiconductor CS4231A";
 705    dc->vmsd = &vmstate_cs4231a;
 706    device_class_set_props(dc, cs4231a_properties);
 707}
 708
 709static const TypeInfo cs4231a_info = {
 710    .name          = TYPE_CS4231A,
 711    .parent        = TYPE_ISA_DEVICE,
 712    .instance_size = sizeof (CSState),
 713    .instance_init = cs4231a_initfn,
 714    .class_init    = cs4231a_class_initfn,
 715};
 716
 717static void cs4231a_register_types (void)
 718{
 719    type_register_static (&cs4231a_info);
 720    deprecated_register_soundhw("cs4231a", "CS4231A", 1, TYPE_CS4231A);
 721}
 722
 723type_init (cs4231a_register_types)
 724