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