xref: /openbmc/qemu/hw/audio/cs4231a.c (revision edd7541b8ce25c1180eb5435ff3350a76d0f0b95)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU Crystal CS4231 audio chip emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2006 Fabrice Bellard
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
246086a565SPeter Maydell #include "qemu/osdep.h"
2549ab747fSPaolo Bonzini #include "hw/hw.h"
268a824e4dSEduardo Habkost #include "hw/audio/soundhw.h"
2749ab747fSPaolo Bonzini #include "audio/audio.h"
2849ab747fSPaolo Bonzini #include "hw/isa/isa.h"
2949ab747fSPaolo Bonzini #include "hw/qdev.h"
3049ab747fSPaolo Bonzini #include "qemu/timer.h"
31c9073238SThomas Huth #include "qapi/error.h"
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini /*
3449ab747fSPaolo Bonzini   Missing features:
3549ab747fSPaolo Bonzini   ADC
3649ab747fSPaolo Bonzini   Loopback
3749ab747fSPaolo Bonzini   Timer
3849ab747fSPaolo Bonzini   ADPCM
3949ab747fSPaolo Bonzini   More...
4049ab747fSPaolo Bonzini */
4149ab747fSPaolo Bonzini 
4249ab747fSPaolo Bonzini /* #define DEBUG */
4349ab747fSPaolo Bonzini /* #define DEBUG_XLAW */
4449ab747fSPaolo Bonzini 
4549ab747fSPaolo Bonzini static struct {
4649ab747fSPaolo Bonzini     int aci_counter;
4749ab747fSPaolo Bonzini } conf = {1};
4849ab747fSPaolo Bonzini 
4949ab747fSPaolo Bonzini #ifdef DEBUG
5049ab747fSPaolo Bonzini #define dolog(...) AUD_log ("cs4231a", __VA_ARGS__)
5149ab747fSPaolo Bonzini #else
5249ab747fSPaolo Bonzini #define dolog(...)
5349ab747fSPaolo Bonzini #endif
5449ab747fSPaolo Bonzini 
5549ab747fSPaolo Bonzini #define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__)
5649ab747fSPaolo Bonzini #define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__)
5749ab747fSPaolo Bonzini 
5849ab747fSPaolo Bonzini #define CS_REGS 16
5949ab747fSPaolo Bonzini #define CS_DREGS 32
6049ab747fSPaolo Bonzini 
61a3dcca56SAndreas Färber #define TYPE_CS4231A "cs4231a"
62a3dcca56SAndreas Färber #define CS4231A(obj) OBJECT_CHECK (CSState, (obj), TYPE_CS4231A)
63a3dcca56SAndreas Färber 
6449ab747fSPaolo Bonzini typedef struct CSState {
6549ab747fSPaolo Bonzini     ISADevice dev;
6649ab747fSPaolo Bonzini     QEMUSoundCard card;
6749ab747fSPaolo Bonzini     MemoryRegion ioports;
6849ab747fSPaolo Bonzini     qemu_irq pic;
6949ab747fSPaolo Bonzini     uint32_t regs[CS_REGS];
7049ab747fSPaolo Bonzini     uint8_t dregs[CS_DREGS];
7149ab747fSPaolo Bonzini     uint32_t irq;
7249ab747fSPaolo Bonzini     uint32_t dma;
7349ab747fSPaolo Bonzini     uint32_t port;
742d011091SHervé Poussineau     IsaDma *isa_dma;
7549ab747fSPaolo Bonzini     int shift;
7649ab747fSPaolo Bonzini     int dma_running;
7749ab747fSPaolo Bonzini     int audio_free;
7849ab747fSPaolo Bonzini     int transferred;
7949ab747fSPaolo Bonzini     int aci_counter;
8049ab747fSPaolo Bonzini     SWVoiceOut *voice;
8149ab747fSPaolo Bonzini     int16_t *tab;
8249ab747fSPaolo Bonzini } CSState;
8349ab747fSPaolo Bonzini 
8449ab747fSPaolo Bonzini #define MODE2 (1 << 6)
8549ab747fSPaolo Bonzini #define MCE (1 << 6)
8649ab747fSPaolo Bonzini #define PMCE (1 << 4)
8749ab747fSPaolo Bonzini #define CMCE (1 << 5)
8849ab747fSPaolo Bonzini #define TE (1 << 6)
8949ab747fSPaolo Bonzini #define PEN (1 << 0)
9049ab747fSPaolo Bonzini #define INT (1 << 0)
9149ab747fSPaolo Bonzini #define IEN (1 << 1)
9249ab747fSPaolo Bonzini #define PPIO (1 << 6)
9349ab747fSPaolo Bonzini #define PI (1 << 4)
9449ab747fSPaolo Bonzini #define CI (1 << 5)
9549ab747fSPaolo Bonzini #define TI (1 << 6)
9649ab747fSPaolo Bonzini 
9749ab747fSPaolo Bonzini enum {
9849ab747fSPaolo Bonzini     Index_Address,
9949ab747fSPaolo Bonzini     Index_Data,
10049ab747fSPaolo Bonzini     Status,
10149ab747fSPaolo Bonzini     PIO_Data
10249ab747fSPaolo Bonzini };
10349ab747fSPaolo Bonzini 
10449ab747fSPaolo Bonzini enum {
10549ab747fSPaolo Bonzini     Left_ADC_Input_Control,
10649ab747fSPaolo Bonzini     Right_ADC_Input_Control,
10749ab747fSPaolo Bonzini     Left_AUX1_Input_Control,
10849ab747fSPaolo Bonzini     Right_AUX1_Input_Control,
10949ab747fSPaolo Bonzini     Left_AUX2_Input_Control,
11049ab747fSPaolo Bonzini     Right_AUX2_Input_Control,
11149ab747fSPaolo Bonzini     Left_DAC_Output_Control,
11249ab747fSPaolo Bonzini     Right_DAC_Output_Control,
11349ab747fSPaolo Bonzini     FS_And_Playback_Data_Format,
11449ab747fSPaolo Bonzini     Interface_Configuration,
11549ab747fSPaolo Bonzini     Pin_Control,
11649ab747fSPaolo Bonzini     Error_Status_And_Initialization,
11749ab747fSPaolo Bonzini     MODE_And_ID,
11849ab747fSPaolo Bonzini     Loopback_Control,
11949ab747fSPaolo Bonzini     Playback_Upper_Base_Count,
12049ab747fSPaolo Bonzini     Playback_Lower_Base_Count,
12149ab747fSPaolo Bonzini     Alternate_Feature_Enable_I,
12249ab747fSPaolo Bonzini     Alternate_Feature_Enable_II,
12349ab747fSPaolo Bonzini     Left_Line_Input_Control,
12449ab747fSPaolo Bonzini     Right_Line_Input_Control,
12549ab747fSPaolo Bonzini     Timer_Low_Base,
12649ab747fSPaolo Bonzini     Timer_High_Base,
12749ab747fSPaolo Bonzini     RESERVED,
12849ab747fSPaolo Bonzini     Alternate_Feature_Enable_III,
12949ab747fSPaolo Bonzini     Alternate_Feature_Status,
13049ab747fSPaolo Bonzini     Version_Chip_ID,
13149ab747fSPaolo Bonzini     Mono_Input_And_Output_Control,
13249ab747fSPaolo Bonzini     RESERVED_2,
13349ab747fSPaolo Bonzini     Capture_Data_Format,
13449ab747fSPaolo Bonzini     RESERVED_3,
13549ab747fSPaolo Bonzini     Capture_Upper_Base_Count,
13649ab747fSPaolo Bonzini     Capture_Lower_Base_Count
13749ab747fSPaolo Bonzini };
13849ab747fSPaolo Bonzini 
13949ab747fSPaolo Bonzini static int freqs[2][8] = {
14049ab747fSPaolo Bonzini     { 8000, 16000, 27420, 32000,    -1,    -1, 48000, 9000 },
14149ab747fSPaolo Bonzini     { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
14249ab747fSPaolo Bonzini };
14349ab747fSPaolo Bonzini 
14449ab747fSPaolo Bonzini /* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
14549ab747fSPaolo Bonzini static int16_t MuLawDecompressTable[256] =
14649ab747fSPaolo Bonzini {
14749ab747fSPaolo Bonzini      -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
14849ab747fSPaolo Bonzini      -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
14949ab747fSPaolo Bonzini      -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
15049ab747fSPaolo Bonzini      -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
15149ab747fSPaolo Bonzini       -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
15249ab747fSPaolo Bonzini       -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
15349ab747fSPaolo Bonzini       -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
15449ab747fSPaolo Bonzini       -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
15549ab747fSPaolo Bonzini       -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
15649ab747fSPaolo Bonzini       -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
15749ab747fSPaolo Bonzini        -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
15849ab747fSPaolo Bonzini        -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
15949ab747fSPaolo Bonzini        -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
16049ab747fSPaolo Bonzini        -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
16149ab747fSPaolo Bonzini        -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
16249ab747fSPaolo Bonzini         -56,   -48,   -40,   -32,   -24,   -16,    -8,     0,
16349ab747fSPaolo Bonzini       32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
16449ab747fSPaolo Bonzini       23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
16549ab747fSPaolo Bonzini       15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
16649ab747fSPaolo Bonzini       11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
16749ab747fSPaolo Bonzini        7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
16849ab747fSPaolo Bonzini        5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
16949ab747fSPaolo Bonzini        3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
17049ab747fSPaolo Bonzini        2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
17149ab747fSPaolo Bonzini        1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
17249ab747fSPaolo Bonzini        1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
17349ab747fSPaolo Bonzini         876,   844,   812,   780,   748,   716,   684,   652,
17449ab747fSPaolo Bonzini         620,   588,   556,   524,   492,   460,   428,   396,
17549ab747fSPaolo Bonzini         372,   356,   340,   324,   308,   292,   276,   260,
17649ab747fSPaolo Bonzini         244,   228,   212,   196,   180,   164,   148,   132,
17749ab747fSPaolo Bonzini         120,   112,   104,    96,    88,    80,    72,    64,
17849ab747fSPaolo Bonzini          56,    48,    40,    32,    24,    16,     8,     0
17949ab747fSPaolo Bonzini };
18049ab747fSPaolo Bonzini 
18149ab747fSPaolo Bonzini static int16_t ALawDecompressTable[256] =
18249ab747fSPaolo Bonzini {
18349ab747fSPaolo Bonzini      -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
18449ab747fSPaolo Bonzini      -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
18549ab747fSPaolo Bonzini      -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
18649ab747fSPaolo Bonzini      -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
18749ab747fSPaolo Bonzini      -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
18849ab747fSPaolo Bonzini      -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
18949ab747fSPaolo Bonzini      -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
19049ab747fSPaolo Bonzini      -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
19149ab747fSPaolo Bonzini      -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,
19249ab747fSPaolo Bonzini      -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,
19349ab747fSPaolo Bonzini      -88,   -72,   -120,  -104,  -24,   -8,    -56,   -40,
19449ab747fSPaolo Bonzini      -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,
19549ab747fSPaolo Bonzini      -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
19649ab747fSPaolo Bonzini      -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
19749ab747fSPaolo Bonzini      -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,
19849ab747fSPaolo Bonzini      -944,  -912,  -1008, -976,  -816,  -784,  -880,  -848,
19949ab747fSPaolo Bonzini       5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,
20049ab747fSPaolo Bonzini       7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,
20149ab747fSPaolo Bonzini       2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,
20249ab747fSPaolo Bonzini       3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,
20349ab747fSPaolo Bonzini       22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
20449ab747fSPaolo Bonzini       30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
20549ab747fSPaolo Bonzini       11008, 10496, 12032, 11520, 8960,  8448,  9984,  9472,
20649ab747fSPaolo Bonzini       15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
20749ab747fSPaolo Bonzini       344,   328,   376,   360,   280,   264,   312,   296,
20849ab747fSPaolo Bonzini       472,   456,   504,   488,   408,   392,   440,   424,
20949ab747fSPaolo Bonzini       88,    72,   120,   104,    24,     8,    56,    40,
21049ab747fSPaolo Bonzini       216,   200,   248,   232,   152,   136,   184,   168,
21149ab747fSPaolo Bonzini       1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
21249ab747fSPaolo Bonzini       1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
21349ab747fSPaolo Bonzini       688,   656,   752,   720,   560,   528,   624,   592,
21449ab747fSPaolo Bonzini       944,   912,  1008,   976,   816,   784,   880,   848
21549ab747fSPaolo Bonzini };
21649ab747fSPaolo Bonzini 
217a3dcca56SAndreas Färber static void cs4231a_reset (DeviceState *dev)
21849ab747fSPaolo Bonzini {
219a3dcca56SAndreas Färber     CSState *s = CS4231A (dev);
22049ab747fSPaolo Bonzini 
22149ab747fSPaolo Bonzini     s->regs[Index_Address] = 0x40;
22249ab747fSPaolo Bonzini     s->regs[Index_Data]    = 0x00;
22349ab747fSPaolo Bonzini     s->regs[Status]        = 0x00;
22449ab747fSPaolo Bonzini     s->regs[PIO_Data]      = 0x00;
22549ab747fSPaolo Bonzini 
22649ab747fSPaolo Bonzini     s->dregs[Left_ADC_Input_Control]          = 0x00;
22749ab747fSPaolo Bonzini     s->dregs[Right_ADC_Input_Control]         = 0x00;
22849ab747fSPaolo Bonzini     s->dregs[Left_AUX1_Input_Control]         = 0x88;
22949ab747fSPaolo Bonzini     s->dregs[Right_AUX1_Input_Control]        = 0x88;
23049ab747fSPaolo Bonzini     s->dregs[Left_AUX2_Input_Control]         = 0x88;
23149ab747fSPaolo Bonzini     s->dregs[Right_AUX2_Input_Control]        = 0x88;
23249ab747fSPaolo Bonzini     s->dregs[Left_DAC_Output_Control]         = 0x80;
23349ab747fSPaolo Bonzini     s->dregs[Right_DAC_Output_Control]        = 0x80;
23449ab747fSPaolo Bonzini     s->dregs[FS_And_Playback_Data_Format]     = 0x00;
23549ab747fSPaolo Bonzini     s->dregs[Interface_Configuration]         = 0x08;
23649ab747fSPaolo Bonzini     s->dregs[Pin_Control]                     = 0x00;
23749ab747fSPaolo Bonzini     s->dregs[Error_Status_And_Initialization] = 0x00;
23849ab747fSPaolo Bonzini     s->dregs[MODE_And_ID]                     = 0x8a;
23949ab747fSPaolo Bonzini     s->dregs[Loopback_Control]                = 0x00;
24049ab747fSPaolo Bonzini     s->dregs[Playback_Upper_Base_Count]       = 0x00;
24149ab747fSPaolo Bonzini     s->dregs[Playback_Lower_Base_Count]       = 0x00;
24249ab747fSPaolo Bonzini     s->dregs[Alternate_Feature_Enable_I]      = 0x00;
24349ab747fSPaolo Bonzini     s->dregs[Alternate_Feature_Enable_II]     = 0x00;
24449ab747fSPaolo Bonzini     s->dregs[Left_Line_Input_Control]         = 0x88;
24549ab747fSPaolo Bonzini     s->dregs[Right_Line_Input_Control]        = 0x88;
24649ab747fSPaolo Bonzini     s->dregs[Timer_Low_Base]                  = 0x00;
24749ab747fSPaolo Bonzini     s->dregs[Timer_High_Base]                 = 0x00;
24849ab747fSPaolo Bonzini     s->dregs[RESERVED]                        = 0x00;
24949ab747fSPaolo Bonzini     s->dregs[Alternate_Feature_Enable_III]    = 0x00;
25049ab747fSPaolo Bonzini     s->dregs[Alternate_Feature_Status]        = 0x00;
25149ab747fSPaolo Bonzini     s->dregs[Version_Chip_ID]                 = 0xa0;
25249ab747fSPaolo Bonzini     s->dregs[Mono_Input_And_Output_Control]   = 0xa0;
25349ab747fSPaolo Bonzini     s->dregs[RESERVED_2]                      = 0x00;
25449ab747fSPaolo Bonzini     s->dregs[Capture_Data_Format]             = 0x00;
25549ab747fSPaolo Bonzini     s->dregs[RESERVED_3]                      = 0x00;
25649ab747fSPaolo Bonzini     s->dregs[Capture_Upper_Base_Count]        = 0x00;
25749ab747fSPaolo Bonzini     s->dregs[Capture_Lower_Base_Count]        = 0x00;
25849ab747fSPaolo Bonzini }
25949ab747fSPaolo Bonzini 
26049ab747fSPaolo Bonzini static void cs_audio_callback (void *opaque, int free)
26149ab747fSPaolo Bonzini {
26249ab747fSPaolo Bonzini     CSState *s = opaque;
26349ab747fSPaolo Bonzini     s->audio_free = free;
26449ab747fSPaolo Bonzini }
26549ab747fSPaolo Bonzini 
26649ab747fSPaolo Bonzini static void cs_reset_voices (CSState *s, uint32_t val)
26749ab747fSPaolo Bonzini {
26849ab747fSPaolo Bonzini     int xtal;
26949ab747fSPaolo Bonzini     struct audsettings as;
2702d011091SHervé Poussineau     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
27149ab747fSPaolo Bonzini 
27249ab747fSPaolo Bonzini #ifdef DEBUG_XLAW
27349ab747fSPaolo Bonzini     if (val == 0 || val == 32)
27449ab747fSPaolo Bonzini         val = (1 << 4) | (1 << 5);
27549ab747fSPaolo Bonzini #endif
27649ab747fSPaolo Bonzini 
27749ab747fSPaolo Bonzini     xtal = val & 1;
27849ab747fSPaolo Bonzini     as.freq = freqs[xtal][(val >> 1) & 7];
27949ab747fSPaolo Bonzini 
28049ab747fSPaolo Bonzini     if (as.freq == -1) {
28149ab747fSPaolo Bonzini         lerr ("unsupported frequency (val=%#x)\n", val);
28249ab747fSPaolo Bonzini         goto error;
28349ab747fSPaolo Bonzini     }
28449ab747fSPaolo Bonzini 
28549ab747fSPaolo Bonzini     as.nchannels = (val & (1 << 4)) ? 2 : 1;
28649ab747fSPaolo Bonzini     as.endianness = 0;
28749ab747fSPaolo Bonzini     s->tab = NULL;
28849ab747fSPaolo Bonzini 
28949ab747fSPaolo Bonzini     switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
29049ab747fSPaolo Bonzini     case 0:
29149ab747fSPaolo Bonzini         as.fmt = AUD_FMT_U8;
29249ab747fSPaolo Bonzini         s->shift = as.nchannels == 2;
29349ab747fSPaolo Bonzini         break;
29449ab747fSPaolo Bonzini 
29549ab747fSPaolo Bonzini     case 1:
29649ab747fSPaolo Bonzini         s->tab = MuLawDecompressTable;
29749ab747fSPaolo Bonzini         goto x_law;
29849ab747fSPaolo Bonzini     case 3:
29949ab747fSPaolo Bonzini         s->tab = ALawDecompressTable;
30049ab747fSPaolo Bonzini     x_law:
30149ab747fSPaolo Bonzini         as.fmt = AUD_FMT_S16;
30249ab747fSPaolo Bonzini         as.endianness = AUDIO_HOST_ENDIANNESS;
30349ab747fSPaolo Bonzini         s->shift = as.nchannels == 2;
30449ab747fSPaolo Bonzini         break;
30549ab747fSPaolo Bonzini 
30649ab747fSPaolo Bonzini     case 6:
30749ab747fSPaolo Bonzini         as.endianness = 1;
308*edd7541bSPaolo Bonzini         /* fall through */
30949ab747fSPaolo Bonzini     case 2:
31049ab747fSPaolo Bonzini         as.fmt = AUD_FMT_S16;
31149ab747fSPaolo Bonzini         s->shift = as.nchannels;
31249ab747fSPaolo Bonzini         break;
31349ab747fSPaolo Bonzini 
31449ab747fSPaolo Bonzini     case 7:
31549ab747fSPaolo Bonzini     case 4:
31649ab747fSPaolo Bonzini         lerr ("attempt to use reserved format value (%#x)\n", val);
31749ab747fSPaolo Bonzini         goto error;
31849ab747fSPaolo Bonzini 
31949ab747fSPaolo Bonzini     case 5:
32049ab747fSPaolo Bonzini         lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
32149ab747fSPaolo Bonzini         goto error;
32249ab747fSPaolo Bonzini     }
32349ab747fSPaolo Bonzini 
32449ab747fSPaolo Bonzini     s->voice = AUD_open_out (
32549ab747fSPaolo Bonzini         &s->card,
32649ab747fSPaolo Bonzini         s->voice,
32749ab747fSPaolo Bonzini         "cs4231a",
32849ab747fSPaolo Bonzini         s,
32949ab747fSPaolo Bonzini         cs_audio_callback,
33049ab747fSPaolo Bonzini         &as
33149ab747fSPaolo Bonzini         );
33249ab747fSPaolo Bonzini 
33349ab747fSPaolo Bonzini     if (s->dregs[Interface_Configuration] & PEN) {
33449ab747fSPaolo Bonzini         if (!s->dma_running) {
3352d011091SHervé Poussineau             k->hold_DREQ(s->isa_dma, s->dma);
33649ab747fSPaolo Bonzini             AUD_set_active_out (s->voice, 1);
33749ab747fSPaolo Bonzini             s->transferred = 0;
33849ab747fSPaolo Bonzini         }
33949ab747fSPaolo Bonzini         s->dma_running = 1;
34049ab747fSPaolo Bonzini     }
34149ab747fSPaolo Bonzini     else {
34249ab747fSPaolo Bonzini         if (s->dma_running) {
3432d011091SHervé Poussineau             k->release_DREQ(s->isa_dma, s->dma);
34449ab747fSPaolo Bonzini             AUD_set_active_out (s->voice, 0);
34549ab747fSPaolo Bonzini         }
34649ab747fSPaolo Bonzini         s->dma_running = 0;
34749ab747fSPaolo Bonzini     }
34849ab747fSPaolo Bonzini     return;
34949ab747fSPaolo Bonzini 
35049ab747fSPaolo Bonzini  error:
35149ab747fSPaolo Bonzini     if (s->dma_running) {
3522d011091SHervé Poussineau         k->release_DREQ(s->isa_dma, s->dma);
35349ab747fSPaolo Bonzini         AUD_set_active_out (s->voice, 0);
35449ab747fSPaolo Bonzini     }
35549ab747fSPaolo Bonzini }
35649ab747fSPaolo Bonzini 
35749ab747fSPaolo Bonzini static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size)
35849ab747fSPaolo Bonzini {
35949ab747fSPaolo Bonzini     CSState *s = opaque;
36049ab747fSPaolo Bonzini     uint32_t saddr, iaddr, ret;
36149ab747fSPaolo Bonzini 
36249ab747fSPaolo Bonzini     saddr = addr;
36349ab747fSPaolo Bonzini     iaddr = ~0U;
36449ab747fSPaolo Bonzini 
36549ab747fSPaolo Bonzini     switch (saddr) {
36649ab747fSPaolo Bonzini     case Index_Address:
36749ab747fSPaolo Bonzini         ret = s->regs[saddr] & ~0x80;
36849ab747fSPaolo Bonzini         break;
36949ab747fSPaolo Bonzini 
37049ab747fSPaolo Bonzini     case Index_Data:
37149ab747fSPaolo Bonzini         if (!(s->dregs[MODE_And_ID] & MODE2))
37249ab747fSPaolo Bonzini             iaddr = s->regs[Index_Address] & 0x0f;
37349ab747fSPaolo Bonzini         else
37449ab747fSPaolo Bonzini             iaddr = s->regs[Index_Address] & 0x1f;
37549ab747fSPaolo Bonzini 
37649ab747fSPaolo Bonzini         ret = s->dregs[iaddr];
37749ab747fSPaolo Bonzini         if (iaddr == Error_Status_And_Initialization) {
37849ab747fSPaolo Bonzini             /* keep SEAL happy */
37949ab747fSPaolo Bonzini             if (s->aci_counter) {
38049ab747fSPaolo Bonzini                 ret |= 1 << 5;
38149ab747fSPaolo Bonzini                 s->aci_counter -= 1;
38249ab747fSPaolo Bonzini             }
38349ab747fSPaolo Bonzini         }
38449ab747fSPaolo Bonzini         break;
38549ab747fSPaolo Bonzini 
38649ab747fSPaolo Bonzini     default:
38749ab747fSPaolo Bonzini         ret = s->regs[saddr];
38849ab747fSPaolo Bonzini         break;
38949ab747fSPaolo Bonzini     }
39049ab747fSPaolo Bonzini     dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
39149ab747fSPaolo Bonzini     return ret;
39249ab747fSPaolo Bonzini }
39349ab747fSPaolo Bonzini 
39449ab747fSPaolo Bonzini static void cs_write (void *opaque, hwaddr addr,
39549ab747fSPaolo Bonzini                       uint64_t val64, unsigned size)
39649ab747fSPaolo Bonzini {
39749ab747fSPaolo Bonzini     CSState *s = opaque;
39849ab747fSPaolo Bonzini     uint32_t saddr, iaddr, val;
39949ab747fSPaolo Bonzini 
40049ab747fSPaolo Bonzini     saddr = addr;
40149ab747fSPaolo Bonzini     val = val64;
40249ab747fSPaolo Bonzini 
40349ab747fSPaolo Bonzini     switch (saddr) {
40449ab747fSPaolo Bonzini     case Index_Address:
40549ab747fSPaolo Bonzini         if (!(s->regs[Index_Address] & MCE) && (val & MCE)
40649ab747fSPaolo Bonzini             && (s->dregs[Interface_Configuration] & (3 << 3)))
40749ab747fSPaolo Bonzini             s->aci_counter = conf.aci_counter;
40849ab747fSPaolo Bonzini 
40949ab747fSPaolo Bonzini         s->regs[Index_Address] = val & ~(1 << 7);
41049ab747fSPaolo Bonzini         break;
41149ab747fSPaolo Bonzini 
41249ab747fSPaolo Bonzini     case Index_Data:
41349ab747fSPaolo Bonzini         if (!(s->dregs[MODE_And_ID] & MODE2))
41449ab747fSPaolo Bonzini             iaddr = s->regs[Index_Address] & 0x0f;
41549ab747fSPaolo Bonzini         else
41649ab747fSPaolo Bonzini             iaddr = s->regs[Index_Address] & 0x1f;
41749ab747fSPaolo Bonzini 
41849ab747fSPaolo Bonzini         switch (iaddr) {
41949ab747fSPaolo Bonzini         case RESERVED:
42049ab747fSPaolo Bonzini         case RESERVED_2:
42149ab747fSPaolo Bonzini         case RESERVED_3:
42249ab747fSPaolo Bonzini             lwarn ("attempt to write %#x to reserved indirect register %d\n",
42349ab747fSPaolo Bonzini                    val, iaddr);
42449ab747fSPaolo Bonzini             break;
42549ab747fSPaolo Bonzini 
42649ab747fSPaolo Bonzini         case FS_And_Playback_Data_Format:
42749ab747fSPaolo Bonzini             if (s->regs[Index_Address] & MCE) {
42849ab747fSPaolo Bonzini                 cs_reset_voices (s, val);
42949ab747fSPaolo Bonzini             }
43049ab747fSPaolo Bonzini             else {
43149ab747fSPaolo Bonzini                 if (s->dregs[Alternate_Feature_Status] & PMCE) {
43249ab747fSPaolo Bonzini                     val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
43349ab747fSPaolo Bonzini                     cs_reset_voices (s, val);
43449ab747fSPaolo Bonzini                 }
43549ab747fSPaolo Bonzini                 else {
43649ab747fSPaolo Bonzini                     lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
43749ab747fSPaolo Bonzini                            s->regs[Index_Address],
43849ab747fSPaolo Bonzini                            s->dregs[Alternate_Feature_Status],
43949ab747fSPaolo Bonzini                            val);
44049ab747fSPaolo Bonzini                     break;
44149ab747fSPaolo Bonzini                 }
44249ab747fSPaolo Bonzini             }
44349ab747fSPaolo Bonzini             s->dregs[iaddr] = val;
44449ab747fSPaolo Bonzini             break;
44549ab747fSPaolo Bonzini 
44649ab747fSPaolo Bonzini         case Interface_Configuration:
44749ab747fSPaolo Bonzini             val &= ~(1 << 5);   /* D5 is reserved */
44849ab747fSPaolo Bonzini             s->dregs[iaddr] = val;
44949ab747fSPaolo Bonzini             if (val & PPIO) {
45049ab747fSPaolo Bonzini                 lwarn ("PIO is not supported (%#x)\n", val);
45149ab747fSPaolo Bonzini                 break;
45249ab747fSPaolo Bonzini             }
45349ab747fSPaolo Bonzini             if (val & PEN) {
45449ab747fSPaolo Bonzini                 if (!s->dma_running) {
45549ab747fSPaolo Bonzini                     cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
45649ab747fSPaolo Bonzini                 }
45749ab747fSPaolo Bonzini             }
45849ab747fSPaolo Bonzini             else {
45949ab747fSPaolo Bonzini                 if (s->dma_running) {
4602d011091SHervé Poussineau                     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
4612d011091SHervé Poussineau                     k->release_DREQ(s->isa_dma, s->dma);
46249ab747fSPaolo Bonzini                     AUD_set_active_out (s->voice, 0);
46349ab747fSPaolo Bonzini                     s->dma_running = 0;
46449ab747fSPaolo Bonzini                 }
46549ab747fSPaolo Bonzini             }
46649ab747fSPaolo Bonzini             break;
46749ab747fSPaolo Bonzini 
46849ab747fSPaolo Bonzini         case Error_Status_And_Initialization:
46949ab747fSPaolo Bonzini             lwarn ("attempt to write to read only register %d\n", iaddr);
47049ab747fSPaolo Bonzini             break;
47149ab747fSPaolo Bonzini 
47249ab747fSPaolo Bonzini         case MODE_And_ID:
47349ab747fSPaolo Bonzini             dolog ("val=%#x\n", val);
47449ab747fSPaolo Bonzini             if (val & MODE2)
47549ab747fSPaolo Bonzini                 s->dregs[iaddr] |= MODE2;
47649ab747fSPaolo Bonzini             else
47749ab747fSPaolo Bonzini                 s->dregs[iaddr] &= ~MODE2;
47849ab747fSPaolo Bonzini             break;
47949ab747fSPaolo Bonzini 
48049ab747fSPaolo Bonzini         case Alternate_Feature_Enable_I:
48149ab747fSPaolo Bonzini             if (val & TE)
48249ab747fSPaolo Bonzini                 lerr ("timer is not yet supported\n");
48349ab747fSPaolo Bonzini             s->dregs[iaddr] = val;
48449ab747fSPaolo Bonzini             break;
48549ab747fSPaolo Bonzini 
48649ab747fSPaolo Bonzini         case Alternate_Feature_Status:
48749ab747fSPaolo Bonzini             if ((s->dregs[iaddr] & PI) && !(val & PI)) {
48849ab747fSPaolo Bonzini                 /* XXX: TI CI */
48949ab747fSPaolo Bonzini                 qemu_irq_lower (s->pic);
49049ab747fSPaolo Bonzini                 s->regs[Status] &= ~INT;
49149ab747fSPaolo Bonzini             }
49249ab747fSPaolo Bonzini             s->dregs[iaddr] = val;
49349ab747fSPaolo Bonzini             break;
49449ab747fSPaolo Bonzini 
49549ab747fSPaolo Bonzini         case Version_Chip_ID:
49649ab747fSPaolo Bonzini             lwarn ("write to Version_Chip_ID register %#x\n", val);
49749ab747fSPaolo Bonzini             s->dregs[iaddr] = val;
49849ab747fSPaolo Bonzini             break;
49949ab747fSPaolo Bonzini 
50049ab747fSPaolo Bonzini         default:
50149ab747fSPaolo Bonzini             s->dregs[iaddr] = val;
50249ab747fSPaolo Bonzini             break;
50349ab747fSPaolo Bonzini         }
50449ab747fSPaolo Bonzini         dolog ("written value %#x to indirect register %d\n", val, iaddr);
50549ab747fSPaolo Bonzini         break;
50649ab747fSPaolo Bonzini 
50749ab747fSPaolo Bonzini     case Status:
50849ab747fSPaolo Bonzini         if (s->regs[Status] & INT) {
50949ab747fSPaolo Bonzini             qemu_irq_lower (s->pic);
51049ab747fSPaolo Bonzini         }
51149ab747fSPaolo Bonzini         s->regs[Status] &= ~INT;
51249ab747fSPaolo Bonzini         s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
51349ab747fSPaolo Bonzini         break;
51449ab747fSPaolo Bonzini 
51549ab747fSPaolo Bonzini     case PIO_Data:
51649ab747fSPaolo Bonzini         lwarn ("attempt to write value %#x to PIO register\n", val);
51749ab747fSPaolo Bonzini         break;
51849ab747fSPaolo Bonzini     }
51949ab747fSPaolo Bonzini }
52049ab747fSPaolo Bonzini 
52149ab747fSPaolo Bonzini static int cs_write_audio (CSState *s, int nchan, int dma_pos,
52249ab747fSPaolo Bonzini                            int dma_len, int len)
52349ab747fSPaolo Bonzini {
52449ab747fSPaolo Bonzini     int temp, net;
52549ab747fSPaolo Bonzini     uint8_t tmpbuf[4096];
5262d011091SHervé Poussineau     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
52749ab747fSPaolo Bonzini 
52849ab747fSPaolo Bonzini     temp = len;
52949ab747fSPaolo Bonzini     net = 0;
53049ab747fSPaolo Bonzini 
53149ab747fSPaolo Bonzini     while (temp) {
53249ab747fSPaolo Bonzini         int left = dma_len - dma_pos;
53349ab747fSPaolo Bonzini         int copied;
53449ab747fSPaolo Bonzini         size_t to_copy;
53549ab747fSPaolo Bonzini 
53649ab747fSPaolo Bonzini         to_copy = audio_MIN (temp, left);
53749ab747fSPaolo Bonzini         if (to_copy > sizeof (tmpbuf)) {
53849ab747fSPaolo Bonzini             to_copy = sizeof (tmpbuf);
53949ab747fSPaolo Bonzini         }
54049ab747fSPaolo Bonzini 
5412d011091SHervé Poussineau         copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy);
54249ab747fSPaolo Bonzini         if (s->tab) {
54349ab747fSPaolo Bonzini             int i;
54449ab747fSPaolo Bonzini             int16_t linbuf[4096];
54549ab747fSPaolo Bonzini 
54649ab747fSPaolo Bonzini             for (i = 0; i < copied; ++i)
54749ab747fSPaolo Bonzini                 linbuf[i] = s->tab[tmpbuf[i]];
54849ab747fSPaolo Bonzini             copied = AUD_write (s->voice, linbuf, copied << 1);
54949ab747fSPaolo Bonzini             copied >>= 1;
55049ab747fSPaolo Bonzini         }
55149ab747fSPaolo Bonzini         else {
55249ab747fSPaolo Bonzini             copied = AUD_write (s->voice, tmpbuf, copied);
55349ab747fSPaolo Bonzini         }
55449ab747fSPaolo Bonzini 
55549ab747fSPaolo Bonzini         temp -= copied;
55649ab747fSPaolo Bonzini         dma_pos = (dma_pos + copied) % dma_len;
55749ab747fSPaolo Bonzini         net += copied;
55849ab747fSPaolo Bonzini 
55949ab747fSPaolo Bonzini         if (!copied) {
56049ab747fSPaolo Bonzini             break;
56149ab747fSPaolo Bonzini         }
56249ab747fSPaolo Bonzini     }
56349ab747fSPaolo Bonzini 
56449ab747fSPaolo Bonzini     return net;
56549ab747fSPaolo Bonzini }
56649ab747fSPaolo Bonzini 
56749ab747fSPaolo Bonzini static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
56849ab747fSPaolo Bonzini {
56949ab747fSPaolo Bonzini     CSState *s = opaque;
57049ab747fSPaolo Bonzini     int copy, written;
57149ab747fSPaolo Bonzini     int till = -1;
57249ab747fSPaolo Bonzini 
57349ab747fSPaolo Bonzini     copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
57449ab747fSPaolo Bonzini 
57549ab747fSPaolo Bonzini     if (s->dregs[Pin_Control] & IEN) {
57649ab747fSPaolo Bonzini         till = (s->dregs[Playback_Lower_Base_Count]
57749ab747fSPaolo Bonzini             | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
57849ab747fSPaolo Bonzini         till -= s->transferred;
57949ab747fSPaolo Bonzini         copy = audio_MIN (till, copy);
58049ab747fSPaolo Bonzini     }
58149ab747fSPaolo Bonzini 
58249ab747fSPaolo Bonzini     if ((copy <= 0) || (dma_len <= 0)) {
58349ab747fSPaolo Bonzini         return dma_pos;
58449ab747fSPaolo Bonzini     }
58549ab747fSPaolo Bonzini 
58649ab747fSPaolo Bonzini     written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
58749ab747fSPaolo Bonzini 
58849ab747fSPaolo Bonzini     dma_pos = (dma_pos + written) % dma_len;
58949ab747fSPaolo Bonzini     s->audio_free -= (written << (s->tab != NULL));
59049ab747fSPaolo Bonzini 
59149ab747fSPaolo Bonzini     if (written == till) {
59249ab747fSPaolo Bonzini         s->regs[Status] |= INT;
59349ab747fSPaolo Bonzini         s->dregs[Alternate_Feature_Status] |= PI;
59449ab747fSPaolo Bonzini         s->transferred = 0;
59549ab747fSPaolo Bonzini         qemu_irq_raise (s->pic);
59649ab747fSPaolo Bonzini     }
59749ab747fSPaolo Bonzini     else {
59849ab747fSPaolo Bonzini         s->transferred += written;
59949ab747fSPaolo Bonzini     }
60049ab747fSPaolo Bonzini 
60149ab747fSPaolo Bonzini     return dma_pos;
60249ab747fSPaolo Bonzini }
60349ab747fSPaolo Bonzini 
60449ab747fSPaolo Bonzini static int cs4231a_pre_load (void *opaque)
60549ab747fSPaolo Bonzini {
60649ab747fSPaolo Bonzini     CSState *s = opaque;
60749ab747fSPaolo Bonzini 
60849ab747fSPaolo Bonzini     if (s->dma_running) {
6092d011091SHervé Poussineau         IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
6102d011091SHervé Poussineau         k->release_DREQ(s->isa_dma, s->dma);
61149ab747fSPaolo Bonzini         AUD_set_active_out (s->voice, 0);
61249ab747fSPaolo Bonzini     }
61349ab747fSPaolo Bonzini     s->dma_running = 0;
61449ab747fSPaolo Bonzini     return 0;
61549ab747fSPaolo Bonzini }
61649ab747fSPaolo Bonzini 
61749ab747fSPaolo Bonzini static int cs4231a_post_load (void *opaque, int version_id)
61849ab747fSPaolo Bonzini {
61949ab747fSPaolo Bonzini     CSState *s = opaque;
62049ab747fSPaolo Bonzini 
62149ab747fSPaolo Bonzini     if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) {
62249ab747fSPaolo Bonzini         s->dma_running = 0;
62349ab747fSPaolo Bonzini         cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
62449ab747fSPaolo Bonzini     }
62549ab747fSPaolo Bonzini     return 0;
62649ab747fSPaolo Bonzini }
62749ab747fSPaolo Bonzini 
62849ab747fSPaolo Bonzini static const VMStateDescription vmstate_cs4231a = {
62949ab747fSPaolo Bonzini     .name = "cs4231a",
63049ab747fSPaolo Bonzini     .version_id = 1,
63149ab747fSPaolo Bonzini     .minimum_version_id = 1,
63249ab747fSPaolo Bonzini     .pre_load = cs4231a_pre_load,
63349ab747fSPaolo Bonzini     .post_load = cs4231a_post_load,
63449ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
63549ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS),
63649ab747fSPaolo Bonzini         VMSTATE_BUFFER (dregs, CSState),
63749ab747fSPaolo Bonzini         VMSTATE_INT32 (dma_running, CSState),
63849ab747fSPaolo Bonzini         VMSTATE_INT32 (audio_free, CSState),
63949ab747fSPaolo Bonzini         VMSTATE_INT32 (transferred, CSState),
64049ab747fSPaolo Bonzini         VMSTATE_INT32 (aci_counter, CSState),
64149ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST ()
64249ab747fSPaolo Bonzini     }
64349ab747fSPaolo Bonzini };
64449ab747fSPaolo Bonzini 
64549ab747fSPaolo Bonzini static const MemoryRegionOps cs_ioport_ops = {
64649ab747fSPaolo Bonzini     .read = cs_read,
64749ab747fSPaolo Bonzini     .write = cs_write,
64849ab747fSPaolo Bonzini     .impl = {
64949ab747fSPaolo Bonzini         .min_access_size = 1,
65049ab747fSPaolo Bonzini         .max_access_size = 1,
65149ab747fSPaolo Bonzini     }
65249ab747fSPaolo Bonzini };
65349ab747fSPaolo Bonzini 
654db895a1eSAndreas Färber static void cs4231a_initfn (Object *obj)
65549ab747fSPaolo Bonzini {
656db895a1eSAndreas Färber     CSState *s = CS4231A (obj);
65749ab747fSPaolo Bonzini 
65864bde0f3SPaolo Bonzini     memory_region_init_io (&s->ioports, OBJECT(s), &cs_ioport_ops, s,
65964bde0f3SPaolo Bonzini                            "cs4231a", 4);
660db895a1eSAndreas Färber }
661db895a1eSAndreas Färber 
662db895a1eSAndreas Färber static void cs4231a_realizefn (DeviceState *dev, Error **errp)
663db895a1eSAndreas Färber {
664db895a1eSAndreas Färber     ISADevice *d = ISA_DEVICE (dev);
665db895a1eSAndreas Färber     CSState *s = CS4231A (dev);
6662d011091SHervé Poussineau     IsaDmaClass *k;
667db895a1eSAndreas Färber 
6682d011091SHervé Poussineau     s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma);
669c9073238SThomas Huth     if (!s->isa_dma) {
670c9073238SThomas Huth         error_setg(errp, "ISA controller does not support DMA");
671c9073238SThomas Huth         return;
672c9073238SThomas Huth     }
673c9073238SThomas Huth 
674c9073238SThomas Huth     isa_init_irq(d, &s->pic, s->irq);
6752d011091SHervé Poussineau     k = ISADMA_GET_CLASS(s->isa_dma);
6762d011091SHervé Poussineau     k->register_channel(s->isa_dma, s->dma, cs_dma_read, s);
677db895a1eSAndreas Färber 
678db895a1eSAndreas Färber     isa_register_ioport (d, &s->ioports, s->port);
67949ab747fSPaolo Bonzini 
68049ab747fSPaolo Bonzini     AUD_register_card ("cs4231a", &s->card);
68149ab747fSPaolo Bonzini }
68249ab747fSPaolo Bonzini 
68336cd6f6fSPaolo Bonzini static int cs4231a_init (ISABus *bus)
68449ab747fSPaolo Bonzini {
685a3dcca56SAndreas Färber     isa_create_simple (bus, TYPE_CS4231A);
68649ab747fSPaolo Bonzini     return 0;
68749ab747fSPaolo Bonzini }
68849ab747fSPaolo Bonzini 
68949ab747fSPaolo Bonzini static Property cs4231a_properties[] = {
690c7bcc85dSPaolo Bonzini     DEFINE_PROP_UINT32 ("iobase",  CSState, port, 0x534),
69149ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
69249ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
69349ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST (),
69449ab747fSPaolo Bonzini };
69549ab747fSPaolo Bonzini 
69649ab747fSPaolo Bonzini static void cs4231a_class_initfn (ObjectClass *klass, void *data)
69749ab747fSPaolo Bonzini {
69849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS (klass);
699db895a1eSAndreas Färber 
700db895a1eSAndreas Färber     dc->realize = cs4231a_realizefn;
701a3dcca56SAndreas Färber     dc->reset = cs4231a_reset;
702125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
70349ab747fSPaolo Bonzini     dc->desc = "Crystal Semiconductor CS4231A";
70449ab747fSPaolo Bonzini     dc->vmsd = &vmstate_cs4231a;
70549ab747fSPaolo Bonzini     dc->props = cs4231a_properties;
70649ab747fSPaolo Bonzini }
70749ab747fSPaolo Bonzini 
70849ab747fSPaolo Bonzini static const TypeInfo cs4231a_info = {
709a3dcca56SAndreas Färber     .name          = TYPE_CS4231A,
71049ab747fSPaolo Bonzini     .parent        = TYPE_ISA_DEVICE,
71149ab747fSPaolo Bonzini     .instance_size = sizeof (CSState),
712db895a1eSAndreas Färber     .instance_init = cs4231a_initfn,
71349ab747fSPaolo Bonzini     .class_init    = cs4231a_class_initfn,
71449ab747fSPaolo Bonzini };
71549ab747fSPaolo Bonzini 
71649ab747fSPaolo Bonzini static void cs4231a_register_types (void)
71749ab747fSPaolo Bonzini {
71849ab747fSPaolo Bonzini     type_register_static (&cs4231a_info);
71936cd6f6fSPaolo Bonzini     isa_register_soundhw("cs4231a", "CS4231A", cs4231a_init);
72049ab747fSPaolo Bonzini }
72149ab747fSPaolo Bonzini 
72249ab747fSPaolo Bonzini type_init (cs4231a_register_types)
723