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