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