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