149ab747fSPaolo Bonzini /* 249ab747fSPaolo Bonzini * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz 349ab747fSPaolo Bonzini * 449ab747fSPaolo Bonzini * Copyright (c) 2002-2005 Vassili Karpov (malc) 549ab747fSPaolo Bonzini * 649ab747fSPaolo Bonzini * Permission is hereby granted, free of charge, to any person obtaining a copy 749ab747fSPaolo Bonzini * of this software and associated documentation files (the "Software"), to deal 849ab747fSPaolo Bonzini * in the Software without restriction, including without limitation the rights 949ab747fSPaolo Bonzini * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1049ab747fSPaolo Bonzini * copies of the Software, and to permit persons to whom the Software is 1149ab747fSPaolo Bonzini * furnished to do so, subject to the following conditions: 1249ab747fSPaolo Bonzini * 1349ab747fSPaolo Bonzini * The above copyright notice and this permission notice shall be included in 1449ab747fSPaolo Bonzini * all copies or substantial portions of the Software. 1549ab747fSPaolo Bonzini * 1649ab747fSPaolo Bonzini * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1749ab747fSPaolo Bonzini * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1849ab747fSPaolo Bonzini * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1949ab747fSPaolo Bonzini * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2049ab747fSPaolo Bonzini * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2149ab747fSPaolo Bonzini * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2249ab747fSPaolo Bonzini * THE SOFTWARE. 2349ab747fSPaolo Bonzini */ 2449ab747fSPaolo Bonzini #include "hw/hw.h" 2549ab747fSPaolo Bonzini #include "hw/audio/audio.h" 2649ab747fSPaolo Bonzini #include "audio/audio.h" 2749ab747fSPaolo Bonzini #include "hw/isa/isa.h" 28*47b43a1fSPaolo Bonzini #include "gusemu.h" 29*47b43a1fSPaolo Bonzini #include "gustate.h" 3049ab747fSPaolo Bonzini 3149ab747fSPaolo Bonzini #define dolog(...) AUD_log ("audio", __VA_ARGS__) 3249ab747fSPaolo Bonzini #ifdef DEBUG 3349ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__) 3449ab747fSPaolo Bonzini #else 3549ab747fSPaolo Bonzini #define ldebug(...) 3649ab747fSPaolo Bonzini #endif 3749ab747fSPaolo Bonzini 3849ab747fSPaolo Bonzini #ifdef HOST_WORDS_BIGENDIAN 3949ab747fSPaolo Bonzini #define GUS_ENDIANNESS 1 4049ab747fSPaolo Bonzini #else 4149ab747fSPaolo Bonzini #define GUS_ENDIANNESS 0 4249ab747fSPaolo Bonzini #endif 4349ab747fSPaolo Bonzini 4449ab747fSPaolo Bonzini #define IO_READ_PROTO(name) \ 4549ab747fSPaolo Bonzini static uint32_t name (void *opaque, uint32_t nport) 4649ab747fSPaolo Bonzini #define IO_WRITE_PROTO(name) \ 4749ab747fSPaolo Bonzini static void name (void *opaque, uint32_t nport, uint32_t val) 4849ab747fSPaolo Bonzini 4949ab747fSPaolo Bonzini typedef struct GUSState { 5049ab747fSPaolo Bonzini ISADevice dev; 5149ab747fSPaolo Bonzini GUSEmuState emu; 5249ab747fSPaolo Bonzini QEMUSoundCard card; 5349ab747fSPaolo Bonzini uint32_t freq; 5449ab747fSPaolo Bonzini uint32_t port; 5549ab747fSPaolo Bonzini int pos, left, shift, irqs; 5649ab747fSPaolo Bonzini GUSsample *mixbuf; 5749ab747fSPaolo Bonzini uint8_t himem[1024 * 1024 + 32 + 4096]; 5849ab747fSPaolo Bonzini int samples; 5949ab747fSPaolo Bonzini SWVoiceOut *voice; 6049ab747fSPaolo Bonzini int64_t last_ticks; 6149ab747fSPaolo Bonzini qemu_irq pic; 6249ab747fSPaolo Bonzini } GUSState; 6349ab747fSPaolo Bonzini 6449ab747fSPaolo Bonzini IO_READ_PROTO (gus_readb) 6549ab747fSPaolo Bonzini { 6649ab747fSPaolo Bonzini GUSState *s = opaque; 6749ab747fSPaolo Bonzini 6849ab747fSPaolo Bonzini return gus_read (&s->emu, nport, 1); 6949ab747fSPaolo Bonzini } 7049ab747fSPaolo Bonzini 7149ab747fSPaolo Bonzini IO_READ_PROTO (gus_readw) 7249ab747fSPaolo Bonzini { 7349ab747fSPaolo Bonzini GUSState *s = opaque; 7449ab747fSPaolo Bonzini 7549ab747fSPaolo Bonzini return gus_read (&s->emu, nport, 2); 7649ab747fSPaolo Bonzini } 7749ab747fSPaolo Bonzini 7849ab747fSPaolo Bonzini IO_WRITE_PROTO (gus_writeb) 7949ab747fSPaolo Bonzini { 8049ab747fSPaolo Bonzini GUSState *s = opaque; 8149ab747fSPaolo Bonzini 8249ab747fSPaolo Bonzini gus_write (&s->emu, nport, 1, val); 8349ab747fSPaolo Bonzini } 8449ab747fSPaolo Bonzini 8549ab747fSPaolo Bonzini IO_WRITE_PROTO (gus_writew) 8649ab747fSPaolo Bonzini { 8749ab747fSPaolo Bonzini GUSState *s = opaque; 8849ab747fSPaolo Bonzini 8949ab747fSPaolo Bonzini gus_write (&s->emu, nport, 2, val); 9049ab747fSPaolo Bonzini } 9149ab747fSPaolo Bonzini 9249ab747fSPaolo Bonzini static int write_audio (GUSState *s, int samples) 9349ab747fSPaolo Bonzini { 9449ab747fSPaolo Bonzini int net = 0; 9549ab747fSPaolo Bonzini int pos = s->pos; 9649ab747fSPaolo Bonzini 9749ab747fSPaolo Bonzini while (samples) { 9849ab747fSPaolo Bonzini int nbytes, wbytes, wsampl; 9949ab747fSPaolo Bonzini 10049ab747fSPaolo Bonzini nbytes = samples << s->shift; 10149ab747fSPaolo Bonzini wbytes = AUD_write ( 10249ab747fSPaolo Bonzini s->voice, 10349ab747fSPaolo Bonzini s->mixbuf + (pos << (s->shift - 1)), 10449ab747fSPaolo Bonzini nbytes 10549ab747fSPaolo Bonzini ); 10649ab747fSPaolo Bonzini 10749ab747fSPaolo Bonzini if (wbytes) { 10849ab747fSPaolo Bonzini wsampl = wbytes >> s->shift; 10949ab747fSPaolo Bonzini 11049ab747fSPaolo Bonzini samples -= wsampl; 11149ab747fSPaolo Bonzini pos = (pos + wsampl) % s->samples; 11249ab747fSPaolo Bonzini 11349ab747fSPaolo Bonzini net += wsampl; 11449ab747fSPaolo Bonzini } 11549ab747fSPaolo Bonzini else { 11649ab747fSPaolo Bonzini break; 11749ab747fSPaolo Bonzini } 11849ab747fSPaolo Bonzini } 11949ab747fSPaolo Bonzini 12049ab747fSPaolo Bonzini return net; 12149ab747fSPaolo Bonzini } 12249ab747fSPaolo Bonzini 12349ab747fSPaolo Bonzini static void GUS_callback (void *opaque, int free) 12449ab747fSPaolo Bonzini { 12549ab747fSPaolo Bonzini int samples, to_play, net = 0; 12649ab747fSPaolo Bonzini GUSState *s = opaque; 12749ab747fSPaolo Bonzini 12849ab747fSPaolo Bonzini samples = free >> s->shift; 12949ab747fSPaolo Bonzini to_play = audio_MIN (samples, s->left); 13049ab747fSPaolo Bonzini 13149ab747fSPaolo Bonzini while (to_play) { 13249ab747fSPaolo Bonzini int written = write_audio (s, to_play); 13349ab747fSPaolo Bonzini 13449ab747fSPaolo Bonzini if (!written) { 13549ab747fSPaolo Bonzini goto reset; 13649ab747fSPaolo Bonzini } 13749ab747fSPaolo Bonzini 13849ab747fSPaolo Bonzini s->left -= written; 13949ab747fSPaolo Bonzini to_play -= written; 14049ab747fSPaolo Bonzini samples -= written; 14149ab747fSPaolo Bonzini net += written; 14249ab747fSPaolo Bonzini } 14349ab747fSPaolo Bonzini 14449ab747fSPaolo Bonzini samples = audio_MIN (samples, s->samples); 14549ab747fSPaolo Bonzini if (samples) { 14649ab747fSPaolo Bonzini gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); 14749ab747fSPaolo Bonzini 14849ab747fSPaolo Bonzini while (samples) { 14949ab747fSPaolo Bonzini int written = write_audio (s, samples); 15049ab747fSPaolo Bonzini if (!written) { 15149ab747fSPaolo Bonzini break; 15249ab747fSPaolo Bonzini } 15349ab747fSPaolo Bonzini samples -= written; 15449ab747fSPaolo Bonzini net += written; 15549ab747fSPaolo Bonzini } 15649ab747fSPaolo Bonzini } 15749ab747fSPaolo Bonzini s->left = samples; 15849ab747fSPaolo Bonzini 15949ab747fSPaolo Bonzini reset: 16049ab747fSPaolo Bonzini gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq)); 16149ab747fSPaolo Bonzini } 16249ab747fSPaolo Bonzini 16349ab747fSPaolo Bonzini int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) 16449ab747fSPaolo Bonzini { 16549ab747fSPaolo Bonzini GUSState *s = emu->opaque; 16649ab747fSPaolo Bonzini /* qemu_irq_lower (s->pic); */ 16749ab747fSPaolo Bonzini qemu_irq_raise (s->pic); 16849ab747fSPaolo Bonzini s->irqs += n; 16949ab747fSPaolo Bonzini ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs); 17049ab747fSPaolo Bonzini return n; 17149ab747fSPaolo Bonzini } 17249ab747fSPaolo Bonzini 17349ab747fSPaolo Bonzini void GUS_irqclear (GUSEmuState *emu, int hwirq) 17449ab747fSPaolo Bonzini { 17549ab747fSPaolo Bonzini GUSState *s = emu->opaque; 17649ab747fSPaolo Bonzini ldebug ("irqclear %d %d\n", hwirq, s->irqs); 17749ab747fSPaolo Bonzini qemu_irq_lower (s->pic); 17849ab747fSPaolo Bonzini s->irqs -= 1; 17949ab747fSPaolo Bonzini #ifdef IRQ_STORM 18049ab747fSPaolo Bonzini if (s->irqs > 0) { 18149ab747fSPaolo Bonzini qemu_irq_raise (s->pic[hwirq]); 18249ab747fSPaolo Bonzini } 18349ab747fSPaolo Bonzini #endif 18449ab747fSPaolo Bonzini } 18549ab747fSPaolo Bonzini 18649ab747fSPaolo Bonzini void GUS_dmarequest (GUSEmuState *der) 18749ab747fSPaolo Bonzini { 18849ab747fSPaolo Bonzini /* GUSState *s = (GUSState *) der; */ 18949ab747fSPaolo Bonzini ldebug ("dma request %d\n", der->gusdma); 19049ab747fSPaolo Bonzini DMA_hold_DREQ (der->gusdma); 19149ab747fSPaolo Bonzini } 19249ab747fSPaolo Bonzini 19349ab747fSPaolo Bonzini static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) 19449ab747fSPaolo Bonzini { 19549ab747fSPaolo Bonzini GUSState *s = opaque; 19649ab747fSPaolo Bonzini char tmpbuf[4096]; 19749ab747fSPaolo Bonzini int pos = dma_pos, mode, left = dma_len - dma_pos; 19849ab747fSPaolo Bonzini 19949ab747fSPaolo Bonzini ldebug ("read DMA %#x %d\n", dma_pos, dma_len); 20049ab747fSPaolo Bonzini mode = DMA_get_channel_mode (s->emu.gusdma); 20149ab747fSPaolo Bonzini while (left) { 20249ab747fSPaolo Bonzini int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); 20349ab747fSPaolo Bonzini int copied; 20449ab747fSPaolo Bonzini 20549ab747fSPaolo Bonzini ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); 20649ab747fSPaolo Bonzini copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy); 20749ab747fSPaolo Bonzini gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); 20849ab747fSPaolo Bonzini left -= copied; 20949ab747fSPaolo Bonzini pos += copied; 21049ab747fSPaolo Bonzini } 21149ab747fSPaolo Bonzini 21249ab747fSPaolo Bonzini if (0 == ((mode >> 4) & 1)) { 21349ab747fSPaolo Bonzini DMA_release_DREQ (s->emu.gusdma); 21449ab747fSPaolo Bonzini } 21549ab747fSPaolo Bonzini return dma_len; 21649ab747fSPaolo Bonzini } 21749ab747fSPaolo Bonzini 21849ab747fSPaolo Bonzini static const VMStateDescription vmstate_gus = { 21949ab747fSPaolo Bonzini .name = "gus", 22049ab747fSPaolo Bonzini .version_id = 2, 22149ab747fSPaolo Bonzini .minimum_version_id = 2, 22249ab747fSPaolo Bonzini .minimum_version_id_old = 2, 22349ab747fSPaolo Bonzini .fields = (VMStateField []) { 22449ab747fSPaolo Bonzini VMSTATE_INT32 (pos, GUSState), 22549ab747fSPaolo Bonzini VMSTATE_INT32 (left, GUSState), 22649ab747fSPaolo Bonzini VMSTATE_INT32 (shift, GUSState), 22749ab747fSPaolo Bonzini VMSTATE_INT32 (irqs, GUSState), 22849ab747fSPaolo Bonzini VMSTATE_INT32 (samples, GUSState), 22949ab747fSPaolo Bonzini VMSTATE_INT64 (last_ticks, GUSState), 23049ab747fSPaolo Bonzini VMSTATE_BUFFER (himem, GUSState), 23149ab747fSPaolo Bonzini VMSTATE_END_OF_LIST () 23249ab747fSPaolo Bonzini } 23349ab747fSPaolo Bonzini }; 23449ab747fSPaolo Bonzini 23549ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list1[] = { 23649ab747fSPaolo Bonzini {0x000, 1, 1, .write = gus_writeb }, 23749ab747fSPaolo Bonzini {0x000, 1, 2, .write = gus_writew }, 23849ab747fSPaolo Bonzini {0x006, 10, 1, .read = gus_readb, .write = gus_writeb }, 23949ab747fSPaolo Bonzini {0x006, 10, 2, .read = gus_readw, .write = gus_writew }, 24049ab747fSPaolo Bonzini {0x100, 8, 1, .read = gus_readb, .write = gus_writeb }, 24149ab747fSPaolo Bonzini {0x100, 8, 2, .read = gus_readw, .write = gus_writew }, 24249ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 24349ab747fSPaolo Bonzini }; 24449ab747fSPaolo Bonzini 24549ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list2[] = { 24649ab747fSPaolo Bonzini {0, 1, 1, .read = gus_readb }, 24749ab747fSPaolo Bonzini {0, 1, 2, .read = gus_readw }, 24849ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 24949ab747fSPaolo Bonzini }; 25049ab747fSPaolo Bonzini 25149ab747fSPaolo Bonzini static int gus_initfn (ISADevice *dev) 25249ab747fSPaolo Bonzini { 25349ab747fSPaolo Bonzini GUSState *s = DO_UPCAST (GUSState, dev, dev); 25449ab747fSPaolo Bonzini struct audsettings as; 25549ab747fSPaolo Bonzini 25649ab747fSPaolo Bonzini AUD_register_card ("gus", &s->card); 25749ab747fSPaolo Bonzini 25849ab747fSPaolo Bonzini as.freq = s->freq; 25949ab747fSPaolo Bonzini as.nchannels = 2; 26049ab747fSPaolo Bonzini as.fmt = AUD_FMT_S16; 26149ab747fSPaolo Bonzini as.endianness = GUS_ENDIANNESS; 26249ab747fSPaolo Bonzini 26349ab747fSPaolo Bonzini s->voice = AUD_open_out ( 26449ab747fSPaolo Bonzini &s->card, 26549ab747fSPaolo Bonzini NULL, 26649ab747fSPaolo Bonzini "gus", 26749ab747fSPaolo Bonzini s, 26849ab747fSPaolo Bonzini GUS_callback, 26949ab747fSPaolo Bonzini &as 27049ab747fSPaolo Bonzini ); 27149ab747fSPaolo Bonzini 27249ab747fSPaolo Bonzini if (!s->voice) { 27349ab747fSPaolo Bonzini AUD_remove_card (&s->card); 27449ab747fSPaolo Bonzini return -1; 27549ab747fSPaolo Bonzini } 27649ab747fSPaolo Bonzini 27749ab747fSPaolo Bonzini s->shift = 2; 27849ab747fSPaolo Bonzini s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; 27949ab747fSPaolo Bonzini s->mixbuf = g_malloc0 (s->samples << s->shift); 28049ab747fSPaolo Bonzini 28149ab747fSPaolo Bonzini isa_register_portio_list (dev, s->port, gus_portio_list1, s, "gus"); 28249ab747fSPaolo Bonzini isa_register_portio_list (dev, (s->port + 0x100) & 0xf00, 28349ab747fSPaolo Bonzini gus_portio_list2, s, "gus"); 28449ab747fSPaolo Bonzini 28549ab747fSPaolo Bonzini DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s); 28649ab747fSPaolo Bonzini s->emu.himemaddr = s->himem; 28749ab747fSPaolo Bonzini s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; 28849ab747fSPaolo Bonzini s->emu.opaque = s; 28949ab747fSPaolo Bonzini isa_init_irq (dev, &s->pic, s->emu.gusirq); 29049ab747fSPaolo Bonzini 29149ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1); 29249ab747fSPaolo Bonzini 29349ab747fSPaolo Bonzini return 0; 29449ab747fSPaolo Bonzini } 29549ab747fSPaolo Bonzini 29649ab747fSPaolo Bonzini int GUS_init (ISABus *bus) 29749ab747fSPaolo Bonzini { 29849ab747fSPaolo Bonzini isa_create_simple (bus, "gus"); 29949ab747fSPaolo Bonzini return 0; 30049ab747fSPaolo Bonzini } 30149ab747fSPaolo Bonzini 30249ab747fSPaolo Bonzini static Property gus_properties[] = { 30349ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), 30449ab747fSPaolo Bonzini DEFINE_PROP_HEX32 ("iobase", GUSState, port, 0x240), 30549ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), 30649ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), 30749ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST (), 30849ab747fSPaolo Bonzini }; 30949ab747fSPaolo Bonzini 31049ab747fSPaolo Bonzini static void gus_class_initfn (ObjectClass *klass, void *data) 31149ab747fSPaolo Bonzini { 31249ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS (klass); 31349ab747fSPaolo Bonzini ISADeviceClass *ic = ISA_DEVICE_CLASS (klass); 31449ab747fSPaolo Bonzini ic->init = gus_initfn; 31549ab747fSPaolo Bonzini dc->desc = "Gravis Ultrasound GF1"; 31649ab747fSPaolo Bonzini dc->vmsd = &vmstate_gus; 31749ab747fSPaolo Bonzini dc->props = gus_properties; 31849ab747fSPaolo Bonzini } 31949ab747fSPaolo Bonzini 32049ab747fSPaolo Bonzini static const TypeInfo gus_info = { 32149ab747fSPaolo Bonzini .name = "gus", 32249ab747fSPaolo Bonzini .parent = TYPE_ISA_DEVICE, 32349ab747fSPaolo Bonzini .instance_size = sizeof (GUSState), 32449ab747fSPaolo Bonzini .class_init = gus_class_initfn, 32549ab747fSPaolo Bonzini }; 32649ab747fSPaolo Bonzini 32749ab747fSPaolo Bonzini static void gus_register_types (void) 32849ab747fSPaolo Bonzini { 32949ab747fSPaolo Bonzini type_register_static (&gus_info); 33049ab747fSPaolo Bonzini } 33149ab747fSPaolo Bonzini 33249ab747fSPaolo Bonzini type_init (gus_register_types) 333