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