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