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 */ 240b8fa32fSMarkus Armbruster 256086a565SPeter Maydell #include "qemu/osdep.h" 26da34e65cSMarkus Armbruster #include "qapi/error.h" 270b8fa32fSMarkus Armbruster #include "qemu/module.h" 2849ab747fSPaolo Bonzini #include "hw/hw.h" 298a824e4dSEduardo Habkost #include "hw/audio/soundhw.h" 3049ab747fSPaolo Bonzini #include "audio/audio.h" 31*64552b6bSMarkus Armbruster #include "hw/irq.h" 3249ab747fSPaolo Bonzini #include "hw/isa/isa.h" 3347b43a1fSPaolo Bonzini #include "gusemu.h" 3447b43a1fSPaolo Bonzini #include "gustate.h" 3549ab747fSPaolo Bonzini 3649ab747fSPaolo Bonzini #define dolog(...) AUD_log ("audio", __VA_ARGS__) 3749ab747fSPaolo Bonzini #ifdef DEBUG 3849ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__) 3949ab747fSPaolo Bonzini #else 4049ab747fSPaolo Bonzini #define ldebug(...) 4149ab747fSPaolo Bonzini #endif 4249ab747fSPaolo Bonzini 4349ab747fSPaolo Bonzini #ifdef HOST_WORDS_BIGENDIAN 4449ab747fSPaolo Bonzini #define GUS_ENDIANNESS 1 4549ab747fSPaolo Bonzini #else 4649ab747fSPaolo Bonzini #define GUS_ENDIANNESS 0 4749ab747fSPaolo Bonzini #endif 4849ab747fSPaolo Bonzini 4911c7549dSAndreas Färber #define TYPE_GUS "gus" 5011c7549dSAndreas Färber #define GUS(obj) OBJECT_CHECK (GUSState, (obj), TYPE_GUS) 5111c7549dSAndreas Färber 5249ab747fSPaolo Bonzini typedef struct GUSState { 5349ab747fSPaolo Bonzini ISADevice dev; 5449ab747fSPaolo Bonzini GUSEmuState emu; 5549ab747fSPaolo Bonzini QEMUSoundCard card; 5649ab747fSPaolo Bonzini uint32_t freq; 5749ab747fSPaolo Bonzini uint32_t port; 5849ab747fSPaolo Bonzini int pos, left, shift, irqs; 59135f5ae1SJuan Quintela int16_t *mixbuf; 6049ab747fSPaolo Bonzini uint8_t himem[1024 * 1024 + 32 + 4096]; 6149ab747fSPaolo Bonzini int samples; 6249ab747fSPaolo Bonzini SWVoiceOut *voice; 6349ab747fSPaolo Bonzini int64_t last_ticks; 6449ab747fSPaolo Bonzini qemu_irq pic; 65467be5f2SHervé Poussineau IsaDma *isa_dma; 66e305a165SMarc-André Lureau PortioList portio_list1; 67e305a165SMarc-André Lureau PortioList portio_list2; 6849ab747fSPaolo Bonzini } GUSState; 6949ab747fSPaolo Bonzini 708307c294SNutan Shinde static uint32_t gus_readb(void *opaque, uint32_t nport) 7149ab747fSPaolo Bonzini { 7249ab747fSPaolo Bonzini GUSState *s = opaque; 7349ab747fSPaolo Bonzini 7449ab747fSPaolo Bonzini return gus_read (&s->emu, nport, 1); 7549ab747fSPaolo Bonzini } 7649ab747fSPaolo Bonzini 778307c294SNutan Shinde static void gus_writeb(void *opaque, uint32_t nport, uint32_t val) 7849ab747fSPaolo Bonzini { 7949ab747fSPaolo Bonzini GUSState *s = opaque; 8049ab747fSPaolo Bonzini 8149ab747fSPaolo Bonzini gus_write (&s->emu, nport, 1, val); 8249ab747fSPaolo Bonzini } 8349ab747fSPaolo Bonzini 8449ab747fSPaolo Bonzini static int write_audio (GUSState *s, int samples) 8549ab747fSPaolo Bonzini { 8649ab747fSPaolo Bonzini int net = 0; 8749ab747fSPaolo Bonzini int pos = s->pos; 8849ab747fSPaolo Bonzini 8949ab747fSPaolo Bonzini while (samples) { 9049ab747fSPaolo Bonzini int nbytes, wbytes, wsampl; 9149ab747fSPaolo Bonzini 9249ab747fSPaolo Bonzini nbytes = samples << s->shift; 9349ab747fSPaolo Bonzini wbytes = AUD_write ( 9449ab747fSPaolo Bonzini s->voice, 9549ab747fSPaolo Bonzini s->mixbuf + (pos << (s->shift - 1)), 9649ab747fSPaolo Bonzini nbytes 9749ab747fSPaolo Bonzini ); 9849ab747fSPaolo Bonzini 9949ab747fSPaolo Bonzini if (wbytes) { 10049ab747fSPaolo Bonzini wsampl = wbytes >> s->shift; 10149ab747fSPaolo Bonzini 10249ab747fSPaolo Bonzini samples -= wsampl; 10349ab747fSPaolo Bonzini pos = (pos + wsampl) % s->samples; 10449ab747fSPaolo Bonzini 10549ab747fSPaolo Bonzini net += wsampl; 10649ab747fSPaolo Bonzini } 10749ab747fSPaolo Bonzini else { 10849ab747fSPaolo Bonzini break; 10949ab747fSPaolo Bonzini } 11049ab747fSPaolo Bonzini } 11149ab747fSPaolo Bonzini 11249ab747fSPaolo Bonzini return net; 11349ab747fSPaolo Bonzini } 11449ab747fSPaolo Bonzini 11549ab747fSPaolo Bonzini static void GUS_callback (void *opaque, int free) 11649ab747fSPaolo Bonzini { 11749ab747fSPaolo Bonzini int samples, to_play, net = 0; 11849ab747fSPaolo Bonzini GUSState *s = opaque; 11949ab747fSPaolo Bonzini 12049ab747fSPaolo Bonzini samples = free >> s->shift; 12149ab747fSPaolo Bonzini to_play = audio_MIN (samples, s->left); 12249ab747fSPaolo Bonzini 12349ab747fSPaolo Bonzini while (to_play) { 12449ab747fSPaolo Bonzini int written = write_audio (s, to_play); 12549ab747fSPaolo Bonzini 12649ab747fSPaolo Bonzini if (!written) { 12749ab747fSPaolo Bonzini goto reset; 12849ab747fSPaolo Bonzini } 12949ab747fSPaolo Bonzini 13049ab747fSPaolo Bonzini s->left -= written; 13149ab747fSPaolo Bonzini to_play -= written; 13249ab747fSPaolo Bonzini samples -= written; 13349ab747fSPaolo Bonzini net += written; 13449ab747fSPaolo Bonzini } 13549ab747fSPaolo Bonzini 13649ab747fSPaolo Bonzini samples = audio_MIN (samples, s->samples); 13749ab747fSPaolo Bonzini if (samples) { 13849ab747fSPaolo Bonzini gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); 13949ab747fSPaolo Bonzini 14049ab747fSPaolo Bonzini while (samples) { 14149ab747fSPaolo Bonzini int written = write_audio (s, samples); 14249ab747fSPaolo Bonzini if (!written) { 14349ab747fSPaolo Bonzini break; 14449ab747fSPaolo Bonzini } 14549ab747fSPaolo Bonzini samples -= written; 14649ab747fSPaolo Bonzini net += written; 14749ab747fSPaolo Bonzini } 14849ab747fSPaolo Bonzini } 14949ab747fSPaolo Bonzini s->left = samples; 15049ab747fSPaolo Bonzini 15149ab747fSPaolo Bonzini reset: 152c00dc675SLaurent Vivier gus_irqgen (&s->emu, (uint64_t)net * 1000000 / s->freq); 15349ab747fSPaolo Bonzini } 15449ab747fSPaolo Bonzini 15549ab747fSPaolo Bonzini int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) 15649ab747fSPaolo Bonzini { 15749ab747fSPaolo Bonzini GUSState *s = emu->opaque; 15849ab747fSPaolo Bonzini /* qemu_irq_lower (s->pic); */ 15949ab747fSPaolo Bonzini qemu_irq_raise (s->pic); 16049ab747fSPaolo Bonzini s->irqs += n; 16149ab747fSPaolo Bonzini ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs); 16249ab747fSPaolo Bonzini return n; 16349ab747fSPaolo Bonzini } 16449ab747fSPaolo Bonzini 16549ab747fSPaolo Bonzini void GUS_irqclear (GUSEmuState *emu, int hwirq) 16649ab747fSPaolo Bonzini { 16749ab747fSPaolo Bonzini GUSState *s = emu->opaque; 16849ab747fSPaolo Bonzini ldebug ("irqclear %d %d\n", hwirq, s->irqs); 16949ab747fSPaolo Bonzini qemu_irq_lower (s->pic); 17049ab747fSPaolo Bonzini s->irqs -= 1; 17149ab747fSPaolo Bonzini #ifdef IRQ_STORM 17249ab747fSPaolo Bonzini if (s->irqs > 0) { 17349ab747fSPaolo Bonzini qemu_irq_raise (s->pic[hwirq]); 17449ab747fSPaolo Bonzini } 17549ab747fSPaolo Bonzini #endif 17649ab747fSPaolo Bonzini } 17749ab747fSPaolo Bonzini 178467be5f2SHervé Poussineau void GUS_dmarequest (GUSEmuState *emu) 17949ab747fSPaolo Bonzini { 180467be5f2SHervé Poussineau GUSState *s = emu->opaque; 181467be5f2SHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); 18249ab747fSPaolo Bonzini ldebug ("dma request %d\n", der->gusdma); 183467be5f2SHervé Poussineau k->hold_DREQ(s->isa_dma, s->emu.gusdma); 18449ab747fSPaolo Bonzini } 18549ab747fSPaolo Bonzini 18649ab747fSPaolo Bonzini static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) 18749ab747fSPaolo Bonzini { 18849ab747fSPaolo Bonzini GUSState *s = opaque; 189467be5f2SHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); 19049ab747fSPaolo Bonzini char tmpbuf[4096]; 19149ab747fSPaolo Bonzini int pos = dma_pos, mode, left = dma_len - dma_pos; 19249ab747fSPaolo Bonzini 19349ab747fSPaolo Bonzini ldebug ("read DMA %#x %d\n", dma_pos, dma_len); 194467be5f2SHervé Poussineau mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma); 19549ab747fSPaolo Bonzini while (left) { 19649ab747fSPaolo Bonzini int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); 19749ab747fSPaolo Bonzini int copied; 19849ab747fSPaolo Bonzini 19949ab747fSPaolo Bonzini ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); 200467be5f2SHervé Poussineau copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy); 20149ab747fSPaolo Bonzini gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); 20249ab747fSPaolo Bonzini left -= copied; 20349ab747fSPaolo Bonzini pos += copied; 20449ab747fSPaolo Bonzini } 20549ab747fSPaolo Bonzini 2062ab5bf67SGonglei if (((mode >> 4) & 1) == 0) { 207467be5f2SHervé Poussineau k->release_DREQ(s->isa_dma, s->emu.gusdma); 20849ab747fSPaolo Bonzini } 20949ab747fSPaolo Bonzini return dma_len; 21049ab747fSPaolo Bonzini } 21149ab747fSPaolo Bonzini 21249ab747fSPaolo Bonzini static const VMStateDescription vmstate_gus = { 21349ab747fSPaolo Bonzini .name = "gus", 21449ab747fSPaolo Bonzini .version_id = 2, 21549ab747fSPaolo Bonzini .minimum_version_id = 2, 21649ab747fSPaolo Bonzini .fields = (VMStateField[]) { 21749ab747fSPaolo Bonzini VMSTATE_INT32 (pos, GUSState), 21849ab747fSPaolo Bonzini VMSTATE_INT32 (left, GUSState), 21949ab747fSPaolo Bonzini VMSTATE_INT32 (shift, GUSState), 22049ab747fSPaolo Bonzini VMSTATE_INT32 (irqs, GUSState), 22149ab747fSPaolo Bonzini VMSTATE_INT32 (samples, GUSState), 22249ab747fSPaolo Bonzini VMSTATE_INT64 (last_ticks, GUSState), 22349ab747fSPaolo Bonzini VMSTATE_BUFFER (himem, GUSState), 22449ab747fSPaolo Bonzini VMSTATE_END_OF_LIST () 22549ab747fSPaolo Bonzini } 22649ab747fSPaolo Bonzini }; 22749ab747fSPaolo Bonzini 22849ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list1[] = { 22949ab747fSPaolo Bonzini {0x000, 1, 1, .write = gus_writeb }, 23049ab747fSPaolo Bonzini {0x006, 10, 1, .read = gus_readb, .write = gus_writeb }, 23149ab747fSPaolo Bonzini {0x100, 8, 1, .read = gus_readb, .write = gus_writeb }, 23249ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 23349ab747fSPaolo Bonzini }; 23449ab747fSPaolo Bonzini 23549ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list2[] = { 23654da54e5SPaolo Bonzini {0, 2, 1, .read = gus_readb }, 23749ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 23849ab747fSPaolo Bonzini }; 23949ab747fSPaolo Bonzini 240db895a1eSAndreas Färber static void gus_realizefn (DeviceState *dev, Error **errp) 24149ab747fSPaolo Bonzini { 242db895a1eSAndreas Färber ISADevice *d = ISA_DEVICE(dev); 24311c7549dSAndreas Färber GUSState *s = GUS (dev); 244467be5f2SHervé Poussineau IsaDmaClass *k; 24549ab747fSPaolo Bonzini struct audsettings as; 24649ab747fSPaolo Bonzini 247c9073238SThomas Huth s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->emu.gusdma); 248c9073238SThomas Huth if (!s->isa_dma) { 249c9073238SThomas Huth error_setg(errp, "ISA controller does not support DMA"); 250c9073238SThomas Huth return; 251c9073238SThomas Huth } 252c9073238SThomas Huth 25349ab747fSPaolo Bonzini AUD_register_card ("gus", &s->card); 25449ab747fSPaolo Bonzini 25549ab747fSPaolo Bonzini as.freq = s->freq; 25649ab747fSPaolo Bonzini as.nchannels = 2; 25785bc5852SKővágó, Zoltán as.fmt = AUDIO_FORMAT_S16; 25849ab747fSPaolo Bonzini as.endianness = GUS_ENDIANNESS; 25949ab747fSPaolo Bonzini 26049ab747fSPaolo Bonzini s->voice = AUD_open_out ( 26149ab747fSPaolo Bonzini &s->card, 26249ab747fSPaolo Bonzini NULL, 26349ab747fSPaolo Bonzini "gus", 26449ab747fSPaolo Bonzini s, 26549ab747fSPaolo Bonzini GUS_callback, 26649ab747fSPaolo Bonzini &as 26749ab747fSPaolo Bonzini ); 26849ab747fSPaolo Bonzini 26949ab747fSPaolo Bonzini if (!s->voice) { 27049ab747fSPaolo Bonzini AUD_remove_card (&s->card); 271db895a1eSAndreas Färber error_setg(errp, "No voice"); 272db895a1eSAndreas Färber return; 27349ab747fSPaolo Bonzini } 27449ab747fSPaolo Bonzini 27549ab747fSPaolo Bonzini s->shift = 2; 27649ab747fSPaolo Bonzini s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; 27749ab747fSPaolo Bonzini s->mixbuf = g_malloc0 (s->samples << s->shift); 27849ab747fSPaolo Bonzini 279e305a165SMarc-André Lureau isa_register_portio_list(d, &s->portio_list1, s->port, 280e305a165SMarc-André Lureau gus_portio_list1, s, "gus"); 281e305a165SMarc-André Lureau isa_register_portio_list(d, &s->portio_list2, (s->port + 0x100) & 0xf00, 28249ab747fSPaolo Bonzini gus_portio_list2, s, "gus"); 28349ab747fSPaolo Bonzini 284467be5f2SHervé Poussineau k = ISADMA_GET_CLASS(s->isa_dma); 285467be5f2SHervé Poussineau k->register_channel(s->isa_dma, 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; 289db895a1eSAndreas Färber isa_init_irq (d, &s->pic, s->emu.gusirq); 29049ab747fSPaolo Bonzini 29149ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1); 29249ab747fSPaolo Bonzini } 29349ab747fSPaolo Bonzini 29436cd6f6fSPaolo Bonzini static int GUS_init (ISABus *bus) 29549ab747fSPaolo Bonzini { 29611c7549dSAndreas Färber isa_create_simple (bus, TYPE_GUS); 29749ab747fSPaolo Bonzini return 0; 29849ab747fSPaolo Bonzini } 29949ab747fSPaolo Bonzini 30049ab747fSPaolo Bonzini static Property gus_properties[] = { 30149ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), 302c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), 30349ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), 30449ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), 30549ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST (), 30649ab747fSPaolo Bonzini }; 30749ab747fSPaolo Bonzini 30849ab747fSPaolo Bonzini static void gus_class_initfn (ObjectClass *klass, void *data) 30949ab747fSPaolo Bonzini { 31049ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS (klass); 311db895a1eSAndreas Färber 312db895a1eSAndreas Färber dc->realize = gus_realizefn; 313125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 31449ab747fSPaolo Bonzini dc->desc = "Gravis Ultrasound GF1"; 31549ab747fSPaolo Bonzini dc->vmsd = &vmstate_gus; 31649ab747fSPaolo Bonzini dc->props = gus_properties; 31749ab747fSPaolo Bonzini } 31849ab747fSPaolo Bonzini 31949ab747fSPaolo Bonzini static const TypeInfo gus_info = { 32011c7549dSAndreas Färber .name = TYPE_GUS, 32149ab747fSPaolo Bonzini .parent = TYPE_ISA_DEVICE, 32249ab747fSPaolo Bonzini .instance_size = sizeof (GUSState), 32349ab747fSPaolo Bonzini .class_init = gus_class_initfn, 32449ab747fSPaolo Bonzini }; 32549ab747fSPaolo Bonzini 32649ab747fSPaolo Bonzini static void gus_register_types (void) 32749ab747fSPaolo Bonzini { 32849ab747fSPaolo Bonzini type_register_static (&gus_info); 32936cd6f6fSPaolo Bonzini isa_register_soundhw("gus", "Gravis Ultrasound GF1", GUS_init); 33049ab747fSPaolo Bonzini } 33149ab747fSPaolo Bonzini 33249ab747fSPaolo Bonzini type_init (gus_register_types) 333