xref: /qemu/hw/audio/cs4231a.c (revision edd7541b8ce25c1180eb5435ff3350a76d0f0b95)
1cc53d26dSmalc /*
2cc53d26dSmalc  * QEMU Crystal CS4231 audio chip emulation
3cc53d26dSmalc  *
4cc53d26dSmalc  * Copyright (c) 2006 Fabrice Bellard
5cc53d26dSmalc  *
6cc53d26dSmalc  * Permission is hereby granted, free of charge, to any person obtaining a copy
7cc53d26dSmalc  * of this software and associated documentation files (the "Software"), to deal
8cc53d26dSmalc  * in the Software without restriction, including without limitation the rights
9cc53d26dSmalc  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10cc53d26dSmalc  * copies of the Software, and to permit persons to whom the Software is
11cc53d26dSmalc  * furnished to do so, subject to the following conditions:
12cc53d26dSmalc  *
13cc53d26dSmalc  * The above copyright notice and this permission notice shall be included in
14cc53d26dSmalc  * all copies or substantial portions of the Software.
15cc53d26dSmalc  *
16cc53d26dSmalc  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17cc53d26dSmalc  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18cc53d26dSmalc  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19cc53d26dSmalc  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20cc53d26dSmalc  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21cc53d26dSmalc  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22cc53d26dSmalc  * THE SOFTWARE.
23cc53d26dSmalc  */
246086a565SPeter Maydell #include "qemu/osdep.h"
2583c9f4caSPaolo Bonzini #include "hw/hw.h"
268a824e4dSEduardo Habkost #include "hw/audio/soundhw.h"
27cc53d26dSmalc #include "audio/audio.h"
280d09e41aSPaolo Bonzini #include "hw/isa/isa.h"
2983c9f4caSPaolo Bonzini #include "hw/qdev.h"
301de7afc9SPaolo Bonzini #include "qemu/timer.h"
31c9073238SThomas Huth #include "qapi/error.h"
32cc53d26dSmalc 
33cc53d26dSmalc /*
34cc53d26dSmalc   Missing features:
35cc53d26dSmalc   ADC
36cc53d26dSmalc   Loopback
37cc53d26dSmalc   Timer
38cc53d26dSmalc   ADPCM
39cc53d26dSmalc   More...
40cc53d26dSmalc */
41cc53d26dSmalc 
42cc53d26dSmalc /* #define DEBUG */
4377599a1fSmalc /* #define DEBUG_XLAW */
44cc53d26dSmalc 
45cc53d26dSmalc static struct {
46cc53d26dSmalc     int aci_counter;
47f8ba7846SGerd Hoffmann } conf = {1};
48cc53d26dSmalc 
49cc53d26dSmalc #ifdef DEBUG
50cc53d26dSmalc #define dolog(...) AUD_log ("cs4231a", __VA_ARGS__)
51cc53d26dSmalc #else
52cc53d26dSmalc #define dolog(...)
53cc53d26dSmalc #endif
54cc53d26dSmalc 
55cc53d26dSmalc #define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__)
56cc53d26dSmalc #define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__)
57cc53d26dSmalc 
58cc53d26dSmalc #define CS_REGS 16
59cc53d26dSmalc #define CS_DREGS 32
60cc53d26dSmalc 
61a3dcca56SAndreas Färber #define TYPE_CS4231A "cs4231a"
62a3dcca56SAndreas Färber #define CS4231A(obj) OBJECT_CHECK (CSState, (obj), TYPE_CS4231A)
63a3dcca56SAndreas Färber 
64cc53d26dSmalc typedef struct CSState {
65f8ba7846SGerd Hoffmann     ISADevice dev;
66cc53d26dSmalc     QEMUSoundCard card;
67beae3979SRichard Henderson     MemoryRegion ioports;
683a38d437SJes Sorensen     qemu_irq pic;
69cc53d26dSmalc     uint32_t regs[CS_REGS];
70cc53d26dSmalc     uint8_t dregs[CS_DREGS];
71f8ba7846SGerd Hoffmann     uint32_t irq;
72f8ba7846SGerd Hoffmann     uint32_t dma;
73f8ba7846SGerd Hoffmann     uint32_t port;
742d011091SHervé Poussineau     IsaDma *isa_dma;
75cc53d26dSmalc     int shift;
76cc53d26dSmalc     int dma_running;
77cc53d26dSmalc     int audio_free;
78cc53d26dSmalc     int transferred;
79cc53d26dSmalc     int aci_counter;
80cc53d26dSmalc     SWVoiceOut *voice;
81cc53d26dSmalc     int16_t *tab;
82cc53d26dSmalc } CSState;
83cc53d26dSmalc 
84cc53d26dSmalc #define MODE2 (1 << 6)
85cc53d26dSmalc #define MCE (1 << 6)
86cc53d26dSmalc #define PMCE (1 << 4)
87cc53d26dSmalc #define CMCE (1 << 5)
88cc53d26dSmalc #define TE (1 << 6)
89cc53d26dSmalc #define PEN (1 << 0)
90cc53d26dSmalc #define INT (1 << 0)
91cc53d26dSmalc #define IEN (1 << 1)
92cc53d26dSmalc #define PPIO (1 << 6)
93cc53d26dSmalc #define PI (1 << 4)
94cc53d26dSmalc #define CI (1 << 5)
95cc53d26dSmalc #define TI (1 << 6)
96cc53d26dSmalc 
97cc53d26dSmalc enum {
98cc53d26dSmalc     Index_Address,
99cc53d26dSmalc     Index_Data,
100cc53d26dSmalc     Status,
101cc53d26dSmalc     PIO_Data
102cc53d26dSmalc };
103cc53d26dSmalc 
104cc53d26dSmalc enum {
105cc53d26dSmalc     Left_ADC_Input_Control,
106cc53d26dSmalc     Right_ADC_Input_Control,
107cc53d26dSmalc     Left_AUX1_Input_Control,
108cc53d26dSmalc     Right_AUX1_Input_Control,
109cc53d26dSmalc     Left_AUX2_Input_Control,
110cc53d26dSmalc     Right_AUX2_Input_Control,
111cc53d26dSmalc     Left_DAC_Output_Control,
112cc53d26dSmalc     Right_DAC_Output_Control,
113cc53d26dSmalc     FS_And_Playback_Data_Format,
114cc53d26dSmalc     Interface_Configuration,
115cc53d26dSmalc     Pin_Control,
116cc53d26dSmalc     Error_Status_And_Initialization,
117cc53d26dSmalc     MODE_And_ID,
118cc53d26dSmalc     Loopback_Control,
119cc53d26dSmalc     Playback_Upper_Base_Count,
120cc53d26dSmalc     Playback_Lower_Base_Count,
121cc53d26dSmalc     Alternate_Feature_Enable_I,
122cc53d26dSmalc     Alternate_Feature_Enable_II,
123cc53d26dSmalc     Left_Line_Input_Control,
124cc53d26dSmalc     Right_Line_Input_Control,
125cc53d26dSmalc     Timer_Low_Base,
126cc53d26dSmalc     Timer_High_Base,
127cc53d26dSmalc     RESERVED,
128cc53d26dSmalc     Alternate_Feature_Enable_III,
129cc53d26dSmalc     Alternate_Feature_Status,
130cc53d26dSmalc     Version_Chip_ID,
131cc53d26dSmalc     Mono_Input_And_Output_Control,
132cc53d26dSmalc     RESERVED_2,
133cc53d26dSmalc     Capture_Data_Format,
134cc53d26dSmalc     RESERVED_3,
135cc53d26dSmalc     Capture_Upper_Base_Count,
136cc53d26dSmalc     Capture_Lower_Base_Count
137cc53d26dSmalc };
138cc53d26dSmalc 
139cc53d26dSmalc static int freqs[2][8] = {
140cc53d26dSmalc     { 8000, 16000, 27420, 32000,    -1,    -1, 48000, 9000 },
141cc53d26dSmalc     { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
142cc53d26dSmalc };
143cc53d26dSmalc 
144cc53d26dSmalc /* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
145cc53d26dSmalc static int16_t MuLawDecompressTable[256] =
146cc53d26dSmalc {
147cc53d26dSmalc      -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
148cc53d26dSmalc      -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
149cc53d26dSmalc      -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
150cc53d26dSmalc      -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
151cc53d26dSmalc       -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
152cc53d26dSmalc       -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
153cc53d26dSmalc       -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
154cc53d26dSmalc       -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
155cc53d26dSmalc       -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
156cc53d26dSmalc       -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
157cc53d26dSmalc        -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
158cc53d26dSmalc        -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
159cc53d26dSmalc        -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
160cc53d26dSmalc        -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
161cc53d26dSmalc        -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
162cc53d26dSmalc         -56,   -48,   -40,   -32,   -24,   -16,    -8,     0,
163cc53d26dSmalc       32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
164cc53d26dSmalc       23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
165cc53d26dSmalc       15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
166cc53d26dSmalc       11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
167cc53d26dSmalc        7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
168cc53d26dSmalc        5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
169cc53d26dSmalc        3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
170cc53d26dSmalc        2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
171cc53d26dSmalc        1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
172cc53d26dSmalc        1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
173cc53d26dSmalc         876,   844,   812,   780,   748,   716,   684,   652,
174cc53d26dSmalc         620,   588,   556,   524,   492,   460,   428,   396,
175cc53d26dSmalc         372,   356,   340,   324,   308,   292,   276,   260,
176cc53d26dSmalc         244,   228,   212,   196,   180,   164,   148,   132,
177cc53d26dSmalc         120,   112,   104,    96,    88,    80,    72,    64,
178cc53d26dSmalc          56,    48,    40,    32,    24,    16,     8,     0
179cc53d26dSmalc };
180cc53d26dSmalc 
181cc53d26dSmalc static int16_t ALawDecompressTable[256] =
182cc53d26dSmalc {
183cc53d26dSmalc      -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
184cc53d26dSmalc      -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
185cc53d26dSmalc      -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
186cc53d26dSmalc      -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
187cc53d26dSmalc      -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
188cc53d26dSmalc      -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
189cc53d26dSmalc      -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
190cc53d26dSmalc      -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
191cc53d26dSmalc      -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,
192cc53d26dSmalc      -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,
193cc53d26dSmalc      -88,   -72,   -120,  -104,  -24,   -8,    -56,   -40,
194cc53d26dSmalc      -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,
195cc53d26dSmalc      -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
196cc53d26dSmalc      -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
197cc53d26dSmalc      -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,
198cc53d26dSmalc      -944,  -912,  -1008, -976,  -816,  -784,  -880,  -848,
199cc53d26dSmalc       5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,
200cc53d26dSmalc       7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,
201cc53d26dSmalc       2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,
202cc53d26dSmalc       3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,
203cc53d26dSmalc       22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
204cc53d26dSmalc       30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
205cc53d26dSmalc       11008, 10496, 12032, 11520, 8960,  8448,  9984,  9472,
206cc53d26dSmalc       15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
207cc53d26dSmalc       344,   328,   376,   360,   280,   264,   312,   296,
208cc53d26dSmalc       472,   456,   504,   488,   408,   392,   440,   424,
209cc53d26dSmalc       88,    72,   120,   104,    24,     8,    56,    40,
210cc53d26dSmalc       216,   200,   248,   232,   152,   136,   184,   168,
211cc53d26dSmalc       1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
212cc53d26dSmalc       1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
213cc53d26dSmalc       688,   656,   752,   720,   560,   528,   624,   592,
214cc53d26dSmalc       944,   912,  1008,   976,   816,   784,   880,   848
215cc53d26dSmalc };
216cc53d26dSmalc 
217a3dcca56SAndreas Färber static void cs4231a_reset (DeviceState *dev)
218cc53d26dSmalc {
219a3dcca56SAndreas Färber     CSState *s = CS4231A (dev);
220cc53d26dSmalc 
221cc53d26dSmalc     s->regs[Index_Address] = 0x40;
222cc53d26dSmalc     s->regs[Index_Data]    = 0x00;
223cc53d26dSmalc     s->regs[Status]        = 0x00;
224cc53d26dSmalc     s->regs[PIO_Data]      = 0x00;
225cc53d26dSmalc 
226cc53d26dSmalc     s->dregs[Left_ADC_Input_Control]          = 0x00;
227cc53d26dSmalc     s->dregs[Right_ADC_Input_Control]         = 0x00;
228cc53d26dSmalc     s->dregs[Left_AUX1_Input_Control]         = 0x88;
229cc53d26dSmalc     s->dregs[Right_AUX1_Input_Control]        = 0x88;
230cc53d26dSmalc     s->dregs[Left_AUX2_Input_Control]         = 0x88;
231cc53d26dSmalc     s->dregs[Right_AUX2_Input_Control]        = 0x88;
232cc53d26dSmalc     s->dregs[Left_DAC_Output_Control]         = 0x80;
233cc53d26dSmalc     s->dregs[Right_DAC_Output_Control]        = 0x80;
234cc53d26dSmalc     s->dregs[FS_And_Playback_Data_Format]     = 0x00;
235cc53d26dSmalc     s->dregs[Interface_Configuration]         = 0x08;
236cc53d26dSmalc     s->dregs[Pin_Control]                     = 0x00;
237cc53d26dSmalc     s->dregs[Error_Status_And_Initialization] = 0x00;
238cc53d26dSmalc     s->dregs[MODE_And_ID]                     = 0x8a;
239cc53d26dSmalc     s->dregs[Loopback_Control]                = 0x00;
240cc53d26dSmalc     s->dregs[Playback_Upper_Base_Count]       = 0x00;
241cc53d26dSmalc     s->dregs[Playback_Lower_Base_Count]       = 0x00;
242cc53d26dSmalc     s->dregs[Alternate_Feature_Enable_I]      = 0x00;
243cc53d26dSmalc     s->dregs[Alternate_Feature_Enable_II]     = 0x00;
244cc53d26dSmalc     s->dregs[Left_Line_Input_Control]         = 0x88;
245cc53d26dSmalc     s->dregs[Right_Line_Input_Control]        = 0x88;
246cc53d26dSmalc     s->dregs[Timer_Low_Base]                  = 0x00;
247cc53d26dSmalc     s->dregs[Timer_High_Base]                 = 0x00;
248cc53d26dSmalc     s->dregs[RESERVED]                        = 0x00;
249cc53d26dSmalc     s->dregs[Alternate_Feature_Enable_III]    = 0x00;
250cc53d26dSmalc     s->dregs[Alternate_Feature_Status]        = 0x00;
251cc53d26dSmalc     s->dregs[Version_Chip_ID]                 = 0xa0;
252cc53d26dSmalc     s->dregs[Mono_Input_And_Output_Control]   = 0xa0;
253cc53d26dSmalc     s->dregs[RESERVED_2]                      = 0x00;
254cc53d26dSmalc     s->dregs[Capture_Data_Format]             = 0x00;
255cc53d26dSmalc     s->dregs[RESERVED_3]                      = 0x00;
256cc53d26dSmalc     s->dregs[Capture_Upper_Base_Count]        = 0x00;
257cc53d26dSmalc     s->dregs[Capture_Lower_Base_Count]        = 0x00;
258cc53d26dSmalc }
259cc53d26dSmalc 
260cc53d26dSmalc static void cs_audio_callback (void *opaque, int free)
261cc53d26dSmalc {
262cc53d26dSmalc     CSState *s = opaque;
263cc53d26dSmalc     s->audio_free = free;
264cc53d26dSmalc }
265cc53d26dSmalc 
266cc53d26dSmalc static void cs_reset_voices (CSState *s, uint32_t val)
267cc53d26dSmalc {
268cc53d26dSmalc     int xtal;
2691ea879e5Smalc     struct audsettings as;
2702d011091SHervé Poussineau     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
271cc53d26dSmalc 
272cc53d26dSmalc #ifdef DEBUG_XLAW
273cc53d26dSmalc     if (val == 0 || val == 32)
274cc53d26dSmalc         val = (1 << 4) | (1 << 5);
275cc53d26dSmalc #endif
276cc53d26dSmalc 
277cc53d26dSmalc     xtal = val & 1;
278cc53d26dSmalc     as.freq = freqs[xtal][(val >> 1) & 7];
279cc53d26dSmalc 
280cc53d26dSmalc     if (as.freq == -1) {
281cc53d26dSmalc         lerr ("unsupported frequency (val=%#x)\n", val);
282cc53d26dSmalc         goto error;
283cc53d26dSmalc     }
284cc53d26dSmalc 
285cc53d26dSmalc     as.nchannels = (val & (1 << 4)) ? 2 : 1;
286cc53d26dSmalc     as.endianness = 0;
287cc53d26dSmalc     s->tab = NULL;
288cc53d26dSmalc 
289cc53d26dSmalc     switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
290cc53d26dSmalc     case 0:
291cc53d26dSmalc         as.fmt = AUD_FMT_U8;
292cc53d26dSmalc         s->shift = as.nchannels == 2;
293cc53d26dSmalc         break;
294cc53d26dSmalc 
295cc53d26dSmalc     case 1:
296cc53d26dSmalc         s->tab = MuLawDecompressTable;
297cc53d26dSmalc         goto x_law;
298cc53d26dSmalc     case 3:
299cc53d26dSmalc         s->tab = ALawDecompressTable;
300cc53d26dSmalc     x_law:
301cc53d26dSmalc         as.fmt = AUD_FMT_S16;
302cc53d26dSmalc         as.endianness = AUDIO_HOST_ENDIANNESS;
303cc53d26dSmalc         s->shift = as.nchannels == 2;
304cc53d26dSmalc         break;
305cc53d26dSmalc 
306cc53d26dSmalc     case 6:
307cc53d26dSmalc         as.endianness = 1;
308*edd7541bSPaolo Bonzini         /* fall through */
309cc53d26dSmalc     case 2:
310cc53d26dSmalc         as.fmt = AUD_FMT_S16;
311cc53d26dSmalc         s->shift = as.nchannels;
312cc53d26dSmalc         break;
313cc53d26dSmalc 
314cc53d26dSmalc     case 7:
315cc53d26dSmalc     case 4:
316cc53d26dSmalc         lerr ("attempt to use reserved format value (%#x)\n", val);
317cc53d26dSmalc         goto error;
318cc53d26dSmalc 
319cc53d26dSmalc     case 5:
320cc53d26dSmalc         lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
321cc53d26dSmalc         goto error;
322cc53d26dSmalc     }
323cc53d26dSmalc 
324cc53d26dSmalc     s->voice = AUD_open_out (
325cc53d26dSmalc         &s->card,
326cc53d26dSmalc         s->voice,
327cc53d26dSmalc         "cs4231a",
328cc53d26dSmalc         s,
329cc53d26dSmalc         cs_audio_callback,
330cc53d26dSmalc         &as
331cc53d26dSmalc         );
332cc53d26dSmalc 
333cc53d26dSmalc     if (s->dregs[Interface_Configuration] & PEN) {
334cc53d26dSmalc         if (!s->dma_running) {
3352d011091SHervé Poussineau             k->hold_DREQ(s->isa_dma, s->dma);
336cc53d26dSmalc             AUD_set_active_out (s->voice, 1);
337cc53d26dSmalc             s->transferred = 0;
338cc53d26dSmalc         }
339cc53d26dSmalc         s->dma_running = 1;
340cc53d26dSmalc     }
341cc53d26dSmalc     else {
342cc53d26dSmalc         if (s->dma_running) {
3432d011091SHervé Poussineau             k->release_DREQ(s->isa_dma, s->dma);
344cc53d26dSmalc             AUD_set_active_out (s->voice, 0);
345cc53d26dSmalc         }
346cc53d26dSmalc         s->dma_running = 0;
347cc53d26dSmalc     }
348cc53d26dSmalc     return;
349cc53d26dSmalc 
350cc53d26dSmalc  error:
351cc53d26dSmalc     if (s->dma_running) {
3522d011091SHervé Poussineau         k->release_DREQ(s->isa_dma, s->dma);
353cc53d26dSmalc         AUD_set_active_out (s->voice, 0);
354cc53d26dSmalc     }
355cc53d26dSmalc }
356cc53d26dSmalc 
357a8170e5eSAvi Kivity static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size)
358cc53d26dSmalc {
359cc53d26dSmalc     CSState *s = opaque;
360cc53d26dSmalc     uint32_t saddr, iaddr, ret;
361cc53d26dSmalc 
362beae3979SRichard Henderson     saddr = addr;
363cc53d26dSmalc     iaddr = ~0U;
364cc53d26dSmalc 
365cc53d26dSmalc     switch (saddr) {
366cc53d26dSmalc     case Index_Address:
367cc53d26dSmalc         ret = s->regs[saddr] & ~0x80;
368cc53d26dSmalc         break;
369cc53d26dSmalc 
370cc53d26dSmalc     case Index_Data:
371cc53d26dSmalc         if (!(s->dregs[MODE_And_ID] & MODE2))
372cc53d26dSmalc             iaddr = s->regs[Index_Address] & 0x0f;
373cc53d26dSmalc         else
374cc53d26dSmalc             iaddr = s->regs[Index_Address] & 0x1f;
375cc53d26dSmalc 
376cc53d26dSmalc         ret = s->dregs[iaddr];
377cc53d26dSmalc         if (iaddr == Error_Status_And_Initialization) {
378cc53d26dSmalc             /* keep SEAL happy */
379cc53d26dSmalc             if (s->aci_counter) {
380cc53d26dSmalc                 ret |= 1 << 5;
381cc53d26dSmalc                 s->aci_counter -= 1;
382cc53d26dSmalc             }
383cc53d26dSmalc         }
384cc53d26dSmalc         break;
385cc53d26dSmalc 
386cc53d26dSmalc     default:
387cc53d26dSmalc         ret = s->regs[saddr];
388cc53d26dSmalc         break;
389cc53d26dSmalc     }
390cc53d26dSmalc     dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
391cc53d26dSmalc     return ret;
392cc53d26dSmalc }
393cc53d26dSmalc 
394a8170e5eSAvi Kivity static void cs_write (void *opaque, hwaddr addr,
395beae3979SRichard Henderson                       uint64_t val64, unsigned size)
396cc53d26dSmalc {
397cc53d26dSmalc     CSState *s = opaque;
398beae3979SRichard Henderson     uint32_t saddr, iaddr, val;
399cc53d26dSmalc 
400beae3979SRichard Henderson     saddr = addr;
401beae3979SRichard Henderson     val = val64;
402cc53d26dSmalc 
403cc53d26dSmalc     switch (saddr) {
404cc53d26dSmalc     case Index_Address:
405cc53d26dSmalc         if (!(s->regs[Index_Address] & MCE) && (val & MCE)
406cc53d26dSmalc             && (s->dregs[Interface_Configuration] & (3 << 3)))
407cc53d26dSmalc             s->aci_counter = conf.aci_counter;
408cc53d26dSmalc 
409cc53d26dSmalc         s->regs[Index_Address] = val & ~(1 << 7);
410cc53d26dSmalc         break;
411cc53d26dSmalc 
412cc53d26dSmalc     case Index_Data:
413cc53d26dSmalc         if (!(s->dregs[MODE_And_ID] & MODE2))
414cc53d26dSmalc             iaddr = s->regs[Index_Address] & 0x0f;
415cc53d26dSmalc         else
416cc53d26dSmalc             iaddr = s->regs[Index_Address] & 0x1f;
417cc53d26dSmalc 
418cc53d26dSmalc         switch (iaddr) {
419cc53d26dSmalc         case RESERVED:
420cc53d26dSmalc         case RESERVED_2:
421cc53d26dSmalc         case RESERVED_3:
422cc53d26dSmalc             lwarn ("attempt to write %#x to reserved indirect register %d\n",
423cc53d26dSmalc                    val, iaddr);
424cc53d26dSmalc             break;
425cc53d26dSmalc 
426cc53d26dSmalc         case FS_And_Playback_Data_Format:
427cc53d26dSmalc             if (s->regs[Index_Address] & MCE) {
428cc53d26dSmalc                 cs_reset_voices (s, val);
429cc53d26dSmalc             }
430cc53d26dSmalc             else {
431cc53d26dSmalc                 if (s->dregs[Alternate_Feature_Status] & PMCE) {
432cc53d26dSmalc                     val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
433cc53d26dSmalc                     cs_reset_voices (s, val);
434cc53d26dSmalc                 }
435cc53d26dSmalc                 else {
436cc53d26dSmalc                     lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
437cc53d26dSmalc                            s->regs[Index_Address],
438cc53d26dSmalc                            s->dregs[Alternate_Feature_Status],
439cc53d26dSmalc                            val);
440cc53d26dSmalc                     break;
441cc53d26dSmalc                 }
442cc53d26dSmalc             }
443cc53d26dSmalc             s->dregs[iaddr] = val;
444cc53d26dSmalc             break;
445cc53d26dSmalc 
446cc53d26dSmalc         case Interface_Configuration:
447cc53d26dSmalc             val &= ~(1 << 5);   /* D5 is reserved */
448cc53d26dSmalc             s->dregs[iaddr] = val;
449cc53d26dSmalc             if (val & PPIO) {
450cc53d26dSmalc                 lwarn ("PIO is not supported (%#x)\n", val);
451cc53d26dSmalc                 break;
452cc53d26dSmalc             }
453cc53d26dSmalc             if (val & PEN) {
454cc53d26dSmalc                 if (!s->dma_running) {
455cc53d26dSmalc                     cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
456cc53d26dSmalc                 }
457cc53d26dSmalc             }
458cc53d26dSmalc             else {
459cc53d26dSmalc                 if (s->dma_running) {
4602d011091SHervé Poussineau                     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
4612d011091SHervé Poussineau                     k->release_DREQ(s->isa_dma, s->dma);
462cc53d26dSmalc                     AUD_set_active_out (s->voice, 0);
463cc53d26dSmalc                     s->dma_running = 0;
464cc53d26dSmalc                 }
465cc53d26dSmalc             }
466cc53d26dSmalc             break;
467cc53d26dSmalc 
468cc53d26dSmalc         case Error_Status_And_Initialization:
469cc53d26dSmalc             lwarn ("attempt to write to read only register %d\n", iaddr);
470cc53d26dSmalc             break;
471cc53d26dSmalc 
472cc53d26dSmalc         case MODE_And_ID:
473cc53d26dSmalc             dolog ("val=%#x\n", val);
474cc53d26dSmalc             if (val & MODE2)
475cc53d26dSmalc                 s->dregs[iaddr] |= MODE2;
476cc53d26dSmalc             else
477cc53d26dSmalc                 s->dregs[iaddr] &= ~MODE2;
478cc53d26dSmalc             break;
479cc53d26dSmalc 
480cc53d26dSmalc         case Alternate_Feature_Enable_I:
481cc53d26dSmalc             if (val & TE)
482cc53d26dSmalc                 lerr ("timer is not yet supported\n");
483cc53d26dSmalc             s->dregs[iaddr] = val;
484cc53d26dSmalc             break;
485cc53d26dSmalc 
486cc53d26dSmalc         case Alternate_Feature_Status:
487cc53d26dSmalc             if ((s->dregs[iaddr] & PI) && !(val & PI)) {
488cc53d26dSmalc                 /* XXX: TI CI */
4893a38d437SJes Sorensen                 qemu_irq_lower (s->pic);
490cc53d26dSmalc                 s->regs[Status] &= ~INT;
491cc53d26dSmalc             }
492cc53d26dSmalc             s->dregs[iaddr] = val;
493cc53d26dSmalc             break;
494cc53d26dSmalc 
495cc53d26dSmalc         case Version_Chip_ID:
496cc53d26dSmalc             lwarn ("write to Version_Chip_ID register %#x\n", val);
497cc53d26dSmalc             s->dregs[iaddr] = val;
498cc53d26dSmalc             break;
499cc53d26dSmalc 
500cc53d26dSmalc         default:
501cc53d26dSmalc             s->dregs[iaddr] = val;
502cc53d26dSmalc             break;
503cc53d26dSmalc         }
504cc53d26dSmalc         dolog ("written value %#x to indirect register %d\n", val, iaddr);
505cc53d26dSmalc         break;
506cc53d26dSmalc 
507cc53d26dSmalc     case Status:
508cc53d26dSmalc         if (s->regs[Status] & INT) {
5093a38d437SJes Sorensen             qemu_irq_lower (s->pic);
510cc53d26dSmalc         }
511cc53d26dSmalc         s->regs[Status] &= ~INT;
512cc53d26dSmalc         s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
513cc53d26dSmalc         break;
514cc53d26dSmalc 
515cc53d26dSmalc     case PIO_Data:
516cc53d26dSmalc         lwarn ("attempt to write value %#x to PIO register\n", val);
517cc53d26dSmalc         break;
518cc53d26dSmalc     }
519cc53d26dSmalc }
520cc53d26dSmalc 
521cc53d26dSmalc static int cs_write_audio (CSState *s, int nchan, int dma_pos,
522cc53d26dSmalc                            int dma_len, int len)
523cc53d26dSmalc {
524cc53d26dSmalc     int temp, net;
525cc53d26dSmalc     uint8_t tmpbuf[4096];
5262d011091SHervé Poussineau     IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
527cc53d26dSmalc 
528cc53d26dSmalc     temp = len;
529cc53d26dSmalc     net = 0;
530cc53d26dSmalc 
531cc53d26dSmalc     while (temp) {
532cc53d26dSmalc         int left = dma_len - dma_pos;
533cc53d26dSmalc         int copied;
534cc53d26dSmalc         size_t to_copy;
535cc53d26dSmalc 
536cc53d26dSmalc         to_copy = audio_MIN (temp, left);
537cc53d26dSmalc         if (to_copy > sizeof (tmpbuf)) {
538cc53d26dSmalc             to_copy = sizeof (tmpbuf);
539cc53d26dSmalc         }
540cc53d26dSmalc 
5412d011091SHervé Poussineau         copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy);
542cc53d26dSmalc         if (s->tab) {
543cc53d26dSmalc             int i;
544cc53d26dSmalc             int16_t linbuf[4096];
545cc53d26dSmalc 
546cc53d26dSmalc             for (i = 0; i < copied; ++i)
547cc53d26dSmalc                 linbuf[i] = s->tab[tmpbuf[i]];
548cc53d26dSmalc             copied = AUD_write (s->voice, linbuf, copied << 1);
549cc53d26dSmalc             copied >>= 1;
550cc53d26dSmalc         }
551cc53d26dSmalc         else {
552cc53d26dSmalc             copied = AUD_write (s->voice, tmpbuf, copied);
553cc53d26dSmalc         }
554cc53d26dSmalc 
555cc53d26dSmalc         temp -= copied;
556cc53d26dSmalc         dma_pos = (dma_pos + copied) % dma_len;
557cc53d26dSmalc         net += copied;
558cc53d26dSmalc 
559cc53d26dSmalc         if (!copied) {
560cc53d26dSmalc             break;
561cc53d26dSmalc         }
562cc53d26dSmalc     }
563cc53d26dSmalc 
564cc53d26dSmalc     return net;
565cc53d26dSmalc }
566cc53d26dSmalc 
567cc53d26dSmalc static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
568cc53d26dSmalc {
569cc53d26dSmalc     CSState *s = opaque;
570cc53d26dSmalc     int copy, written;
571cc53d26dSmalc     int till = -1;
572cc53d26dSmalc 
573cc53d26dSmalc     copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
574cc53d26dSmalc 
575cc53d26dSmalc     if (s->dregs[Pin_Control] & IEN) {
576cc53d26dSmalc         till = (s->dregs[Playback_Lower_Base_Count]
577cc53d26dSmalc             | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
578cc53d26dSmalc         till -= s->transferred;
579cc53d26dSmalc         copy = audio_MIN (till, copy);
580cc53d26dSmalc     }
581cc53d26dSmalc 
582cc53d26dSmalc     if ((copy <= 0) || (dma_len <= 0)) {
583cc53d26dSmalc         return dma_pos;
584cc53d26dSmalc     }
585cc53d26dSmalc 
586cc53d26dSmalc     written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
587cc53d26dSmalc 
588cc53d26dSmalc     dma_pos = (dma_pos + written) % dma_len;
589cc53d26dSmalc     s->audio_free -= (written << (s->tab != NULL));
590cc53d26dSmalc 
591cc53d26dSmalc     if (written == till) {
592cc53d26dSmalc         s->regs[Status] |= INT;
593cc53d26dSmalc         s->dregs[Alternate_Feature_Status] |= PI;
594cc53d26dSmalc         s->transferred = 0;
5953a38d437SJes Sorensen         qemu_irq_raise (s->pic);
596cc53d26dSmalc     }
597cc53d26dSmalc     else {
598cc53d26dSmalc         s->transferred += written;
599cc53d26dSmalc     }
600cc53d26dSmalc 
601cc53d26dSmalc     return dma_pos;
602cc53d26dSmalc }
603cc53d26dSmalc 
6041d190d5cSJuan Quintela static int cs4231a_pre_load (void *opaque)
605cc53d26dSmalc {
606cc53d26dSmalc     CSState *s = opaque;
607cc53d26dSmalc 
6081d190d5cSJuan Quintela     if (s->dma_running) {
6092d011091SHervé Poussineau         IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
6102d011091SHervé Poussineau         k->release_DREQ(s->isa_dma, s->dma);
6111d190d5cSJuan Quintela         AUD_set_active_out (s->voice, 0);
612cc53d26dSmalc     }
6131d190d5cSJuan Quintela     s->dma_running = 0;
614cc53d26dSmalc     return 0;
615cc53d26dSmalc }
616cc53d26dSmalc 
6171d190d5cSJuan Quintela static int cs4231a_post_load (void *opaque, int version_id)
6181d190d5cSJuan Quintela {
6191d190d5cSJuan Quintela     CSState *s = opaque;
6201d190d5cSJuan Quintela 
6211d190d5cSJuan Quintela     if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) {
6221d190d5cSJuan Quintela         s->dma_running = 0;
6231d190d5cSJuan Quintela         cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
6241d190d5cSJuan Quintela     }
6251d190d5cSJuan Quintela     return 0;
6261d190d5cSJuan Quintela }
6271d190d5cSJuan Quintela 
6281d190d5cSJuan Quintela static const VMStateDescription vmstate_cs4231a = {
6291d190d5cSJuan Quintela     .name = "cs4231a",
6301d190d5cSJuan Quintela     .version_id = 1,
6311d190d5cSJuan Quintela     .minimum_version_id = 1,
6321d190d5cSJuan Quintela     .pre_load = cs4231a_pre_load,
6331d190d5cSJuan Quintela     .post_load = cs4231a_post_load,
6341d190d5cSJuan Quintela     .fields = (VMStateField[]) {
6351d190d5cSJuan Quintela         VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS),
6361d190d5cSJuan Quintela         VMSTATE_BUFFER (dregs, CSState),
6371d190d5cSJuan Quintela         VMSTATE_INT32 (dma_running, CSState),
6381d190d5cSJuan Quintela         VMSTATE_INT32 (audio_free, CSState),
6391d190d5cSJuan Quintela         VMSTATE_INT32 (transferred, CSState),
6401d190d5cSJuan Quintela         VMSTATE_INT32 (aci_counter, CSState),
6411d190d5cSJuan Quintela         VMSTATE_END_OF_LIST ()
6421d190d5cSJuan Quintela     }
6431d190d5cSJuan Quintela };
6441d190d5cSJuan Quintela 
645beae3979SRichard Henderson static const MemoryRegionOps cs_ioport_ops = {
646beae3979SRichard Henderson     .read = cs_read,
647beae3979SRichard Henderson     .write = cs_write,
648beae3979SRichard Henderson     .impl = {
649beae3979SRichard Henderson         .min_access_size = 1,
650beae3979SRichard Henderson         .max_access_size = 1,
651beae3979SRichard Henderson     }
652beae3979SRichard Henderson };
653beae3979SRichard Henderson 
654db895a1eSAndreas Färber static void cs4231a_initfn (Object *obj)
655cc53d26dSmalc {
656db895a1eSAndreas Färber     CSState *s = CS4231A (obj);
657cc53d26dSmalc 
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);
679cc53d26dSmalc 
6801a7dafceSmalc     AUD_register_card ("cs4231a", &s->card);
681cc53d26dSmalc }
682f8ba7846SGerd Hoffmann 
68336cd6f6fSPaolo Bonzini static int cs4231a_init (ISABus *bus)
684f8ba7846SGerd Hoffmann {
685a3dcca56SAndreas Färber     isa_create_simple (bus, TYPE_CS4231A);
686f8ba7846SGerd Hoffmann     return 0;
687f8ba7846SGerd Hoffmann }
688f8ba7846SGerd Hoffmann 
68939bffca2SAnthony Liguori static Property cs4231a_properties[] = {
690c7bcc85dSPaolo Bonzini     DEFINE_PROP_UINT32 ("iobase",  CSState, port, 0x534),
691f8ba7846SGerd Hoffmann     DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
692f8ba7846SGerd Hoffmann     DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
693f8ba7846SGerd Hoffmann     DEFINE_PROP_END_OF_LIST (),
69439bffca2SAnthony Liguori };
69539bffca2SAnthony Liguori 
69639bffca2SAnthony Liguori static void cs4231a_class_initfn (ObjectClass *klass, void *data)
69739bffca2SAnthony Liguori {
69839bffca2SAnthony Liguori     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);
70339bffca2SAnthony Liguori     dc->desc = "Crystal Semiconductor CS4231A";
70439bffca2SAnthony Liguori     dc->vmsd = &vmstate_cs4231a;
70539bffca2SAnthony Liguori     dc->props = cs4231a_properties;
70639bffca2SAnthony Liguori }
70739bffca2SAnthony Liguori 
7088c43a6f0SAndreas Färber static const TypeInfo cs4231a_info = {
709a3dcca56SAndreas Färber     .name          = TYPE_CS4231A,
71039bffca2SAnthony Liguori     .parent        = TYPE_ISA_DEVICE,
71139bffca2SAnthony Liguori     .instance_size = sizeof (CSState),
712db895a1eSAndreas Färber     .instance_init = cs4231a_initfn,
71339bffca2SAnthony Liguori     .class_init    = cs4231a_class_initfn,
714f8ba7846SGerd Hoffmann };
715f8ba7846SGerd Hoffmann 
71683f7d43aSAndreas Färber static void cs4231a_register_types (void)
717f8ba7846SGerd Hoffmann {
71839bffca2SAnthony Liguori     type_register_static (&cs4231a_info);
71936cd6f6fSPaolo Bonzini     isa_register_soundhw("cs4231a", "CS4231A", cs4231a_init);
720f8ba7846SGerd Hoffmann }
72183f7d43aSAndreas Färber 
72283f7d43aSAndreas Färber type_init (cs4231a_register_types)
723