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" 288a824e4dSEduardo Habkost #include "hw/audio/soundhw.h" 2949ab747fSPaolo Bonzini #include "audio/audio.h" 3064552b6bSMarkus Armbruster #include "hw/irq.h" 3149ab747fSPaolo Bonzini #include "hw/isa/isa.h" 32a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 33d6454270SMarkus Armbruster #include "migration/vmstate.h" 3447b43a1fSPaolo Bonzini #include "gusemu.h" 3547b43a1fSPaolo Bonzini #include "gustate.h" 36db1015e9SEduardo Habkost #include "qom/object.h" 3749ab747fSPaolo Bonzini 3849ab747fSPaolo Bonzini #define dolog(...) AUD_log ("audio", __VA_ARGS__) 3949ab747fSPaolo Bonzini #ifdef DEBUG 4049ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__) 4149ab747fSPaolo Bonzini #else 4249ab747fSPaolo Bonzini #define ldebug(...) 4349ab747fSPaolo Bonzini #endif 4449ab747fSPaolo Bonzini 4511c7549dSAndreas Färber #define TYPE_GUS "gus" 468063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(GUSState, GUS) 4711c7549dSAndreas Färber 48db1015e9SEduardo Habkost struct GUSState { 4949ab747fSPaolo Bonzini ISADevice dev; 5049ab747fSPaolo Bonzini GUSEmuState emu; 5149ab747fSPaolo Bonzini QEMUSoundCard card; 5249ab747fSPaolo Bonzini uint32_t freq; 5349ab747fSPaolo Bonzini uint32_t port; 5449ab747fSPaolo Bonzini int pos, left, shift, irqs; 55135f5ae1SJuan Quintela int16_t *mixbuf; 5649ab747fSPaolo Bonzini uint8_t himem[1024 * 1024 + 32 + 4096]; 5749ab747fSPaolo Bonzini int samples; 5849ab747fSPaolo Bonzini SWVoiceOut *voice; 5949ab747fSPaolo Bonzini int64_t last_ticks; 6049ab747fSPaolo Bonzini qemu_irq pic; 61467be5f2SHervé Poussineau IsaDma *isa_dma; 62e305a165SMarc-André Lureau PortioList portio_list1; 63e305a165SMarc-André Lureau PortioList portio_list2; 64db1015e9SEduardo Habkost }; 6549ab747fSPaolo Bonzini 668307c294SNutan Shinde static uint32_t gus_readb(void *opaque, uint32_t nport) 6749ab747fSPaolo Bonzini { 6849ab747fSPaolo Bonzini GUSState *s = opaque; 6949ab747fSPaolo Bonzini 7049ab747fSPaolo Bonzini return gus_read (&s->emu, nport, 1); 7149ab747fSPaolo Bonzini } 7249ab747fSPaolo Bonzini 738307c294SNutan Shinde static void gus_writeb(void *opaque, uint32_t nport, uint32_t val) 7449ab747fSPaolo Bonzini { 7549ab747fSPaolo Bonzini GUSState *s = opaque; 7649ab747fSPaolo Bonzini 7749ab747fSPaolo Bonzini gus_write (&s->emu, nport, 1, val); 7849ab747fSPaolo Bonzini } 7949ab747fSPaolo Bonzini 8049ab747fSPaolo Bonzini static int write_audio (GUSState *s, int samples) 8149ab747fSPaolo Bonzini { 8249ab747fSPaolo Bonzini int net = 0; 8349ab747fSPaolo Bonzini int pos = s->pos; 8449ab747fSPaolo Bonzini 8549ab747fSPaolo Bonzini while (samples) { 8649ab747fSPaolo Bonzini int nbytes, wbytes, wsampl; 8749ab747fSPaolo Bonzini 8849ab747fSPaolo Bonzini nbytes = samples << s->shift; 8949ab747fSPaolo Bonzini wbytes = AUD_write ( 9049ab747fSPaolo Bonzini s->voice, 9149ab747fSPaolo Bonzini s->mixbuf + (pos << (s->shift - 1)), 9249ab747fSPaolo Bonzini nbytes 9349ab747fSPaolo Bonzini ); 9449ab747fSPaolo Bonzini 9549ab747fSPaolo Bonzini if (wbytes) { 9649ab747fSPaolo Bonzini wsampl = wbytes >> s->shift; 9749ab747fSPaolo Bonzini 9849ab747fSPaolo Bonzini samples -= wsampl; 9949ab747fSPaolo Bonzini pos = (pos + wsampl) % s->samples; 10049ab747fSPaolo Bonzini 10149ab747fSPaolo Bonzini net += wsampl; 10249ab747fSPaolo Bonzini } 10349ab747fSPaolo Bonzini else { 10449ab747fSPaolo Bonzini break; 10549ab747fSPaolo Bonzini } 10649ab747fSPaolo Bonzini } 10749ab747fSPaolo Bonzini 10849ab747fSPaolo Bonzini return net; 10949ab747fSPaolo Bonzini } 11049ab747fSPaolo Bonzini 11149ab747fSPaolo Bonzini static void GUS_callback (void *opaque, int free) 11249ab747fSPaolo Bonzini { 11349ab747fSPaolo Bonzini int samples, to_play, net = 0; 11449ab747fSPaolo Bonzini GUSState *s = opaque; 11549ab747fSPaolo Bonzini 11649ab747fSPaolo Bonzini samples = free >> s->shift; 11758935915SKővágó, Zoltán to_play = MIN (samples, s->left); 11849ab747fSPaolo Bonzini 11949ab747fSPaolo Bonzini while (to_play) { 12049ab747fSPaolo Bonzini int written = write_audio (s, to_play); 12149ab747fSPaolo Bonzini 12249ab747fSPaolo Bonzini if (!written) { 12349ab747fSPaolo Bonzini goto reset; 12449ab747fSPaolo Bonzini } 12549ab747fSPaolo Bonzini 12649ab747fSPaolo Bonzini s->left -= written; 12749ab747fSPaolo Bonzini to_play -= written; 12849ab747fSPaolo Bonzini samples -= written; 12949ab747fSPaolo Bonzini net += written; 13049ab747fSPaolo Bonzini } 13149ab747fSPaolo Bonzini 13258935915SKővágó, Zoltán samples = MIN (samples, s->samples); 13349ab747fSPaolo Bonzini if (samples) { 13449ab747fSPaolo Bonzini gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); 13549ab747fSPaolo Bonzini 13649ab747fSPaolo Bonzini while (samples) { 13749ab747fSPaolo Bonzini int written = write_audio (s, samples); 13849ab747fSPaolo Bonzini if (!written) { 13949ab747fSPaolo Bonzini break; 14049ab747fSPaolo Bonzini } 14149ab747fSPaolo Bonzini samples -= written; 14249ab747fSPaolo Bonzini net += written; 14349ab747fSPaolo Bonzini } 14449ab747fSPaolo Bonzini } 14549ab747fSPaolo Bonzini s->left = samples; 14649ab747fSPaolo Bonzini 14749ab747fSPaolo Bonzini reset: 148c00dc675SLaurent Vivier gus_irqgen (&s->emu, (uint64_t)net * 1000000 / s->freq); 14949ab747fSPaolo Bonzini } 15049ab747fSPaolo Bonzini 15149ab747fSPaolo Bonzini int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) 15249ab747fSPaolo Bonzini { 15349ab747fSPaolo Bonzini GUSState *s = emu->opaque; 15449ab747fSPaolo Bonzini /* qemu_irq_lower (s->pic); */ 15549ab747fSPaolo Bonzini qemu_irq_raise (s->pic); 15649ab747fSPaolo Bonzini s->irqs += n; 15749ab747fSPaolo Bonzini ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs); 15849ab747fSPaolo Bonzini return n; 15949ab747fSPaolo Bonzini } 16049ab747fSPaolo Bonzini 16149ab747fSPaolo Bonzini void GUS_irqclear (GUSEmuState *emu, int hwirq) 16249ab747fSPaolo Bonzini { 16349ab747fSPaolo Bonzini GUSState *s = emu->opaque; 16449ab747fSPaolo Bonzini ldebug ("irqclear %d %d\n", hwirq, s->irqs); 16549ab747fSPaolo Bonzini qemu_irq_lower (s->pic); 16649ab747fSPaolo Bonzini s->irqs -= 1; 16749ab747fSPaolo Bonzini #ifdef IRQ_STORM 16849ab747fSPaolo Bonzini if (s->irqs > 0) { 16949ab747fSPaolo Bonzini qemu_irq_raise (s->pic[hwirq]); 17049ab747fSPaolo Bonzini } 17149ab747fSPaolo Bonzini #endif 17249ab747fSPaolo Bonzini } 17349ab747fSPaolo Bonzini 174467be5f2SHervé Poussineau void GUS_dmarequest (GUSEmuState *emu) 17549ab747fSPaolo Bonzini { 176467be5f2SHervé Poussineau GUSState *s = emu->opaque; 177467be5f2SHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); 17849ab747fSPaolo Bonzini ldebug ("dma request %d\n", der->gusdma); 179467be5f2SHervé Poussineau k->hold_DREQ(s->isa_dma, s->emu.gusdma); 18049ab747fSPaolo Bonzini } 18149ab747fSPaolo Bonzini 18249ab747fSPaolo Bonzini static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) 18349ab747fSPaolo Bonzini { 18449ab747fSPaolo Bonzini GUSState *s = opaque; 185467be5f2SHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); 18649ab747fSPaolo Bonzini char tmpbuf[4096]; 18749ab747fSPaolo Bonzini int pos = dma_pos, mode, left = dma_len - dma_pos; 18849ab747fSPaolo Bonzini 18949ab747fSPaolo Bonzini ldebug ("read DMA %#x %d\n", dma_pos, dma_len); 190467be5f2SHervé Poussineau mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma); 19149ab747fSPaolo Bonzini while (left) { 19258935915SKővágó, Zoltán int to_copy = MIN ((size_t) left, sizeof (tmpbuf)); 19349ab747fSPaolo Bonzini int copied; 19449ab747fSPaolo Bonzini 19549ab747fSPaolo Bonzini ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); 196467be5f2SHervé Poussineau copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy); 19749ab747fSPaolo Bonzini gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); 19849ab747fSPaolo Bonzini left -= copied; 19949ab747fSPaolo Bonzini pos += copied; 20049ab747fSPaolo Bonzini } 20149ab747fSPaolo Bonzini 2022ab5bf67SGonglei if (((mode >> 4) & 1) == 0) { 203467be5f2SHervé Poussineau k->release_DREQ(s->isa_dma, s->emu.gusdma); 20449ab747fSPaolo Bonzini } 20549ab747fSPaolo Bonzini return dma_len; 20649ab747fSPaolo Bonzini } 20749ab747fSPaolo Bonzini 20849ab747fSPaolo Bonzini static const VMStateDescription vmstate_gus = { 20949ab747fSPaolo Bonzini .name = "gus", 21049ab747fSPaolo Bonzini .version_id = 2, 21149ab747fSPaolo Bonzini .minimum_version_id = 2, 212*856a6fe4SRichard Henderson .fields = (const VMStateField[]) { 21349ab747fSPaolo Bonzini VMSTATE_INT32 (pos, GUSState), 21449ab747fSPaolo Bonzini VMSTATE_INT32 (left, GUSState), 21549ab747fSPaolo Bonzini VMSTATE_INT32 (shift, GUSState), 21649ab747fSPaolo Bonzini VMSTATE_INT32 (irqs, GUSState), 21749ab747fSPaolo Bonzini VMSTATE_INT32 (samples, GUSState), 21849ab747fSPaolo Bonzini VMSTATE_INT64 (last_ticks, GUSState), 21949ab747fSPaolo Bonzini VMSTATE_BUFFER (himem, GUSState), 22049ab747fSPaolo Bonzini VMSTATE_END_OF_LIST () 22149ab747fSPaolo Bonzini } 22249ab747fSPaolo Bonzini }; 22349ab747fSPaolo Bonzini 22449ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list1[] = { 22549ab747fSPaolo Bonzini {0x000, 1, 1, .write = gus_writeb }, 22649ab747fSPaolo Bonzini {0x006, 10, 1, .read = gus_readb, .write = gus_writeb }, 22749ab747fSPaolo Bonzini {0x100, 8, 1, .read = gus_readb, .write = gus_writeb }, 22849ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 22949ab747fSPaolo Bonzini }; 23049ab747fSPaolo Bonzini 23149ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list2[] = { 23254da54e5SPaolo Bonzini {0, 2, 1, .read = gus_readb }, 23349ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 23449ab747fSPaolo Bonzini }; 23549ab747fSPaolo Bonzini 236db895a1eSAndreas Färber static void gus_realizefn (DeviceState *dev, Error **errp) 23749ab747fSPaolo Bonzini { 238db895a1eSAndreas Färber ISADevice *d = ISA_DEVICE(dev); 2398e7db8abSPhilippe Mathieu-Daudé ISABus *bus = isa_bus_from_device(d); 24011c7549dSAndreas Färber GUSState *s = GUS (dev); 241467be5f2SHervé Poussineau IsaDmaClass *k; 24249ab747fSPaolo Bonzini struct audsettings as; 24349ab747fSPaolo Bonzini 244cb94ff5fSMartin Kletzander if (!AUD_register_card ("gus", &s->card, errp)) { 245cb94ff5fSMartin Kletzander return; 246cb94ff5fSMartin Kletzander } 247cb94ff5fSMartin Kletzander 2488e7db8abSPhilippe Mathieu-Daudé s->isa_dma = isa_bus_get_dma(bus, s->emu.gusdma); 249c9073238SThomas Huth if (!s->isa_dma) { 250c9073238SThomas Huth error_setg(errp, "ISA controller does not support DMA"); 251c9073238SThomas Huth return; 252c9073238SThomas Huth } 253c9073238SThomas Huth 25449ab747fSPaolo Bonzini as.freq = s->freq; 25549ab747fSPaolo Bonzini as.nchannels = 2; 25685bc5852SKővágó, Zoltán as.fmt = AUDIO_FORMAT_S16; 2572f097e19SPhilippe Mathieu-Daudé as.endianness = AUDIO_HOST_ENDIANNESS; 25849ab747fSPaolo Bonzini 25949ab747fSPaolo Bonzini s->voice = AUD_open_out ( 26049ab747fSPaolo Bonzini &s->card, 26149ab747fSPaolo Bonzini NULL, 26249ab747fSPaolo Bonzini "gus", 26349ab747fSPaolo Bonzini s, 26449ab747fSPaolo Bonzini GUS_callback, 26549ab747fSPaolo Bonzini &as 26649ab747fSPaolo Bonzini ); 26749ab747fSPaolo Bonzini 26849ab747fSPaolo Bonzini if (!s->voice) { 26949ab747fSPaolo Bonzini AUD_remove_card (&s->card); 270db895a1eSAndreas Färber error_setg(errp, "No voice"); 271db895a1eSAndreas Färber return; 27249ab747fSPaolo Bonzini } 27349ab747fSPaolo Bonzini 27449ab747fSPaolo Bonzini s->shift = 2; 27549ab747fSPaolo Bonzini s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; 27649ab747fSPaolo Bonzini s->mixbuf = g_malloc0 (s->samples << s->shift); 27749ab747fSPaolo Bonzini 278e305a165SMarc-André Lureau isa_register_portio_list(d, &s->portio_list1, s->port, 279e305a165SMarc-André Lureau gus_portio_list1, s, "gus"); 280e305a165SMarc-André Lureau isa_register_portio_list(d, &s->portio_list2, (s->port + 0x100) & 0xf00, 28149ab747fSPaolo Bonzini gus_portio_list2, s, "gus"); 28249ab747fSPaolo Bonzini 283467be5f2SHervé Poussineau k = ISADMA_GET_CLASS(s->isa_dma); 284467be5f2SHervé Poussineau k->register_channel(s->isa_dma, s->emu.gusdma, GUS_read_DMA, s); 28549ab747fSPaolo Bonzini s->emu.himemaddr = s->himem; 28649ab747fSPaolo Bonzini s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; 28749ab747fSPaolo Bonzini s->emu.opaque = s; 2888e7db8abSPhilippe Mathieu-Daudé s->pic = isa_bus_get_irq(bus, s->emu.gusirq); 28949ab747fSPaolo Bonzini 29049ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1); 29149ab747fSPaolo Bonzini } 29249ab747fSPaolo Bonzini 29349ab747fSPaolo Bonzini static Property gus_properties[] = { 29488e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(GUSState, card), 29549ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), 296c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), 29749ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), 29849ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), 29949ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST (), 30049ab747fSPaolo Bonzini }; 30149ab747fSPaolo Bonzini 30249ab747fSPaolo Bonzini static void gus_class_initfn (ObjectClass *klass, void *data) 30349ab747fSPaolo Bonzini { 30449ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS (klass); 305db895a1eSAndreas Färber 306db895a1eSAndreas Färber dc->realize = gus_realizefn; 307125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 30849ab747fSPaolo Bonzini dc->desc = "Gravis Ultrasound GF1"; 30949ab747fSPaolo Bonzini dc->vmsd = &vmstate_gus; 3104f67d30bSMarc-André Lureau device_class_set_props(dc, gus_properties); 31149ab747fSPaolo Bonzini } 31249ab747fSPaolo Bonzini 31349ab747fSPaolo Bonzini static const TypeInfo gus_info = { 31411c7549dSAndreas Färber .name = TYPE_GUS, 31549ab747fSPaolo Bonzini .parent = TYPE_ISA_DEVICE, 31649ab747fSPaolo Bonzini .instance_size = sizeof (GUSState), 31749ab747fSPaolo Bonzini .class_init = gus_class_initfn, 31849ab747fSPaolo Bonzini }; 31949ab747fSPaolo Bonzini 32049ab747fSPaolo Bonzini static void gus_register_types (void) 32149ab747fSPaolo Bonzini { 32249ab747fSPaolo Bonzini type_register_static (&gus_info); 323ba541176SGerd Hoffmann deprecated_register_soundhw("gus", "Gravis Ultrasound GF1", 1, TYPE_GUS); 32449ab747fSPaolo Bonzini } 32549ab747fSPaolo Bonzini 32649ab747fSPaolo Bonzini type_init (gus_register_types) 327