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