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