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