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" 36*db1015e9SEduardo 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" 46*db1015e9SEduardo Habkost typedef struct GUSState GUSState; 4711c7549dSAndreas Färber #define GUS(obj) OBJECT_CHECK (GUSState, (obj), TYPE_GUS) 4811c7549dSAndreas Färber 49*db1015e9SEduardo Habkost 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; 56135f5ae1SJuan Quintela int16_t *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; 62467be5f2SHervé Poussineau IsaDma *isa_dma; 63e305a165SMarc-André Lureau PortioList portio_list1; 64e305a165SMarc-André Lureau PortioList portio_list2; 65*db1015e9SEduardo Habkost }; 6649ab747fSPaolo Bonzini 678307c294SNutan Shinde static uint32_t gus_readb(void *opaque, uint32_t nport) 6849ab747fSPaolo Bonzini { 6949ab747fSPaolo Bonzini GUSState *s = opaque; 7049ab747fSPaolo Bonzini 7149ab747fSPaolo Bonzini return gus_read (&s->emu, nport, 1); 7249ab747fSPaolo Bonzini } 7349ab747fSPaolo Bonzini 748307c294SNutan Shinde static void gus_writeb(void *opaque, uint32_t nport, uint32_t val) 7549ab747fSPaolo Bonzini { 7649ab747fSPaolo Bonzini GUSState *s = opaque; 7749ab747fSPaolo Bonzini 7849ab747fSPaolo Bonzini gus_write (&s->emu, nport, 1, val); 7949ab747fSPaolo Bonzini } 8049ab747fSPaolo Bonzini 8149ab747fSPaolo Bonzini static int write_audio (GUSState *s, int samples) 8249ab747fSPaolo Bonzini { 8349ab747fSPaolo Bonzini int net = 0; 8449ab747fSPaolo Bonzini int pos = s->pos; 8549ab747fSPaolo Bonzini 8649ab747fSPaolo Bonzini while (samples) { 8749ab747fSPaolo Bonzini int nbytes, wbytes, wsampl; 8849ab747fSPaolo Bonzini 8949ab747fSPaolo Bonzini nbytes = samples << s->shift; 9049ab747fSPaolo Bonzini wbytes = AUD_write ( 9149ab747fSPaolo Bonzini s->voice, 9249ab747fSPaolo Bonzini s->mixbuf + (pos << (s->shift - 1)), 9349ab747fSPaolo Bonzini nbytes 9449ab747fSPaolo Bonzini ); 9549ab747fSPaolo Bonzini 9649ab747fSPaolo Bonzini if (wbytes) { 9749ab747fSPaolo Bonzini wsampl = wbytes >> s->shift; 9849ab747fSPaolo Bonzini 9949ab747fSPaolo Bonzini samples -= wsampl; 10049ab747fSPaolo Bonzini pos = (pos + wsampl) % s->samples; 10149ab747fSPaolo Bonzini 10249ab747fSPaolo Bonzini net += wsampl; 10349ab747fSPaolo Bonzini } 10449ab747fSPaolo Bonzini else { 10549ab747fSPaolo Bonzini break; 10649ab747fSPaolo Bonzini } 10749ab747fSPaolo Bonzini } 10849ab747fSPaolo Bonzini 10949ab747fSPaolo Bonzini return net; 11049ab747fSPaolo Bonzini } 11149ab747fSPaolo Bonzini 11249ab747fSPaolo Bonzini static void GUS_callback (void *opaque, int free) 11349ab747fSPaolo Bonzini { 11449ab747fSPaolo Bonzini int samples, to_play, net = 0; 11549ab747fSPaolo Bonzini GUSState *s = opaque; 11649ab747fSPaolo Bonzini 11749ab747fSPaolo Bonzini samples = free >> s->shift; 11858935915SKővágó, Zoltán to_play = MIN (samples, s->left); 11949ab747fSPaolo Bonzini 12049ab747fSPaolo Bonzini while (to_play) { 12149ab747fSPaolo Bonzini int written = write_audio (s, to_play); 12249ab747fSPaolo Bonzini 12349ab747fSPaolo Bonzini if (!written) { 12449ab747fSPaolo Bonzini goto reset; 12549ab747fSPaolo Bonzini } 12649ab747fSPaolo Bonzini 12749ab747fSPaolo Bonzini s->left -= written; 12849ab747fSPaolo Bonzini to_play -= written; 12949ab747fSPaolo Bonzini samples -= written; 13049ab747fSPaolo Bonzini net += written; 13149ab747fSPaolo Bonzini } 13249ab747fSPaolo Bonzini 13358935915SKővágó, Zoltán samples = MIN (samples, s->samples); 13449ab747fSPaolo Bonzini if (samples) { 13549ab747fSPaolo Bonzini gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); 13649ab747fSPaolo Bonzini 13749ab747fSPaolo Bonzini while (samples) { 13849ab747fSPaolo Bonzini int written = write_audio (s, samples); 13949ab747fSPaolo Bonzini if (!written) { 14049ab747fSPaolo Bonzini break; 14149ab747fSPaolo Bonzini } 14249ab747fSPaolo Bonzini samples -= written; 14349ab747fSPaolo Bonzini net += written; 14449ab747fSPaolo Bonzini } 14549ab747fSPaolo Bonzini } 14649ab747fSPaolo Bonzini s->left = samples; 14749ab747fSPaolo Bonzini 14849ab747fSPaolo Bonzini reset: 149c00dc675SLaurent Vivier gus_irqgen (&s->emu, (uint64_t)net * 1000000 / s->freq); 15049ab747fSPaolo Bonzini } 15149ab747fSPaolo Bonzini 15249ab747fSPaolo Bonzini int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) 15349ab747fSPaolo Bonzini { 15449ab747fSPaolo Bonzini GUSState *s = emu->opaque; 15549ab747fSPaolo Bonzini /* qemu_irq_lower (s->pic); */ 15649ab747fSPaolo Bonzini qemu_irq_raise (s->pic); 15749ab747fSPaolo Bonzini s->irqs += n; 15849ab747fSPaolo Bonzini ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs); 15949ab747fSPaolo Bonzini return n; 16049ab747fSPaolo Bonzini } 16149ab747fSPaolo Bonzini 16249ab747fSPaolo Bonzini void GUS_irqclear (GUSEmuState *emu, int hwirq) 16349ab747fSPaolo Bonzini { 16449ab747fSPaolo Bonzini GUSState *s = emu->opaque; 16549ab747fSPaolo Bonzini ldebug ("irqclear %d %d\n", hwirq, s->irqs); 16649ab747fSPaolo Bonzini qemu_irq_lower (s->pic); 16749ab747fSPaolo Bonzini s->irqs -= 1; 16849ab747fSPaolo Bonzini #ifdef IRQ_STORM 16949ab747fSPaolo Bonzini if (s->irqs > 0) { 17049ab747fSPaolo Bonzini qemu_irq_raise (s->pic[hwirq]); 17149ab747fSPaolo Bonzini } 17249ab747fSPaolo Bonzini #endif 17349ab747fSPaolo Bonzini } 17449ab747fSPaolo Bonzini 175467be5f2SHervé Poussineau void GUS_dmarequest (GUSEmuState *emu) 17649ab747fSPaolo Bonzini { 177467be5f2SHervé Poussineau GUSState *s = emu->opaque; 178467be5f2SHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); 17949ab747fSPaolo Bonzini ldebug ("dma request %d\n", der->gusdma); 180467be5f2SHervé Poussineau k->hold_DREQ(s->isa_dma, s->emu.gusdma); 18149ab747fSPaolo Bonzini } 18249ab747fSPaolo Bonzini 18349ab747fSPaolo Bonzini static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) 18449ab747fSPaolo Bonzini { 18549ab747fSPaolo Bonzini GUSState *s = opaque; 186467be5f2SHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); 18749ab747fSPaolo Bonzini char tmpbuf[4096]; 18849ab747fSPaolo Bonzini int pos = dma_pos, mode, left = dma_len - dma_pos; 18949ab747fSPaolo Bonzini 19049ab747fSPaolo Bonzini ldebug ("read DMA %#x %d\n", dma_pos, dma_len); 191467be5f2SHervé Poussineau mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma); 19249ab747fSPaolo Bonzini while (left) { 19358935915SKővágó, Zoltán int to_copy = MIN ((size_t) left, sizeof (tmpbuf)); 19449ab747fSPaolo Bonzini int copied; 19549ab747fSPaolo Bonzini 19649ab747fSPaolo Bonzini ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); 197467be5f2SHervé Poussineau copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy); 19849ab747fSPaolo Bonzini gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); 19949ab747fSPaolo Bonzini left -= copied; 20049ab747fSPaolo Bonzini pos += copied; 20149ab747fSPaolo Bonzini } 20249ab747fSPaolo Bonzini 2032ab5bf67SGonglei if (((mode >> 4) & 1) == 0) { 204467be5f2SHervé Poussineau k->release_DREQ(s->isa_dma, s->emu.gusdma); 20549ab747fSPaolo Bonzini } 20649ab747fSPaolo Bonzini return dma_len; 20749ab747fSPaolo Bonzini } 20849ab747fSPaolo Bonzini 20949ab747fSPaolo Bonzini static const VMStateDescription vmstate_gus = { 21049ab747fSPaolo Bonzini .name = "gus", 21149ab747fSPaolo Bonzini .version_id = 2, 21249ab747fSPaolo Bonzini .minimum_version_id = 2, 21349ab747fSPaolo Bonzini .fields = (VMStateField[]) { 21449ab747fSPaolo Bonzini VMSTATE_INT32 (pos, GUSState), 21549ab747fSPaolo Bonzini VMSTATE_INT32 (left, GUSState), 21649ab747fSPaolo Bonzini VMSTATE_INT32 (shift, GUSState), 21749ab747fSPaolo Bonzini VMSTATE_INT32 (irqs, GUSState), 21849ab747fSPaolo Bonzini VMSTATE_INT32 (samples, GUSState), 21949ab747fSPaolo Bonzini VMSTATE_INT64 (last_ticks, GUSState), 22049ab747fSPaolo Bonzini VMSTATE_BUFFER (himem, GUSState), 22149ab747fSPaolo Bonzini VMSTATE_END_OF_LIST () 22249ab747fSPaolo Bonzini } 22349ab747fSPaolo Bonzini }; 22449ab747fSPaolo Bonzini 22549ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list1[] = { 22649ab747fSPaolo Bonzini {0x000, 1, 1, .write = gus_writeb }, 22749ab747fSPaolo Bonzini {0x006, 10, 1, .read = gus_readb, .write = gus_writeb }, 22849ab747fSPaolo Bonzini {0x100, 8, 1, .read = gus_readb, .write = gus_writeb }, 22949ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 23049ab747fSPaolo Bonzini }; 23149ab747fSPaolo Bonzini 23249ab747fSPaolo Bonzini static const MemoryRegionPortio gus_portio_list2[] = { 23354da54e5SPaolo Bonzini {0, 2, 1, .read = gus_readb }, 23449ab747fSPaolo Bonzini PORTIO_END_OF_LIST (), 23549ab747fSPaolo Bonzini }; 23649ab747fSPaolo Bonzini 237db895a1eSAndreas Färber static void gus_realizefn (DeviceState *dev, Error **errp) 23849ab747fSPaolo Bonzini { 239db895a1eSAndreas Färber ISADevice *d = ISA_DEVICE(dev); 24011c7549dSAndreas Färber GUSState *s = GUS (dev); 241467be5f2SHervé Poussineau IsaDmaClass *k; 24249ab747fSPaolo Bonzini struct audsettings as; 24349ab747fSPaolo Bonzini 244c9073238SThomas Huth s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->emu.gusdma); 245c9073238SThomas Huth if (!s->isa_dma) { 246c9073238SThomas Huth error_setg(errp, "ISA controller does not support DMA"); 247c9073238SThomas Huth return; 248c9073238SThomas Huth } 249c9073238SThomas Huth 25049ab747fSPaolo Bonzini AUD_register_card ("gus", &s->card); 25149ab747fSPaolo Bonzini 25249ab747fSPaolo Bonzini as.freq = s->freq; 25349ab747fSPaolo Bonzini as.nchannels = 2; 25485bc5852SKővágó, Zoltán as.fmt = AUDIO_FORMAT_S16; 2552f097e19SPhilippe Mathieu-Daudé as.endianness = AUDIO_HOST_ENDIANNESS; 25649ab747fSPaolo Bonzini 25749ab747fSPaolo Bonzini s->voice = AUD_open_out ( 25849ab747fSPaolo Bonzini &s->card, 25949ab747fSPaolo Bonzini NULL, 26049ab747fSPaolo Bonzini "gus", 26149ab747fSPaolo Bonzini s, 26249ab747fSPaolo Bonzini GUS_callback, 26349ab747fSPaolo Bonzini &as 26449ab747fSPaolo Bonzini ); 26549ab747fSPaolo Bonzini 26649ab747fSPaolo Bonzini if (!s->voice) { 26749ab747fSPaolo Bonzini AUD_remove_card (&s->card); 268db895a1eSAndreas Färber error_setg(errp, "No voice"); 269db895a1eSAndreas Färber return; 27049ab747fSPaolo Bonzini } 27149ab747fSPaolo Bonzini 27249ab747fSPaolo Bonzini s->shift = 2; 27349ab747fSPaolo Bonzini s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; 27449ab747fSPaolo Bonzini s->mixbuf = g_malloc0 (s->samples << s->shift); 27549ab747fSPaolo Bonzini 276e305a165SMarc-André Lureau isa_register_portio_list(d, &s->portio_list1, s->port, 277e305a165SMarc-André Lureau gus_portio_list1, s, "gus"); 278e305a165SMarc-André Lureau isa_register_portio_list(d, &s->portio_list2, (s->port + 0x100) & 0xf00, 27949ab747fSPaolo Bonzini gus_portio_list2, s, "gus"); 28049ab747fSPaolo Bonzini 281467be5f2SHervé Poussineau k = ISADMA_GET_CLASS(s->isa_dma); 282467be5f2SHervé Poussineau k->register_channel(s->isa_dma, s->emu.gusdma, GUS_read_DMA, s); 28349ab747fSPaolo Bonzini s->emu.himemaddr = s->himem; 28449ab747fSPaolo Bonzini s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; 28549ab747fSPaolo Bonzini s->emu.opaque = s; 286db895a1eSAndreas Färber isa_init_irq (d, &s->pic, s->emu.gusirq); 28749ab747fSPaolo Bonzini 28849ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1); 28949ab747fSPaolo Bonzini } 29049ab747fSPaolo Bonzini 29149ab747fSPaolo Bonzini static Property gus_properties[] = { 29288e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(GUSState, card), 29349ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), 294c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), 29549ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), 29649ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), 29749ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST (), 29849ab747fSPaolo Bonzini }; 29949ab747fSPaolo Bonzini 30049ab747fSPaolo Bonzini static void gus_class_initfn (ObjectClass *klass, void *data) 30149ab747fSPaolo Bonzini { 30249ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS (klass); 303db895a1eSAndreas Färber 304db895a1eSAndreas Färber dc->realize = gus_realizefn; 305125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 30649ab747fSPaolo Bonzini dc->desc = "Gravis Ultrasound GF1"; 30749ab747fSPaolo Bonzini dc->vmsd = &vmstate_gus; 3084f67d30bSMarc-André Lureau device_class_set_props(dc, gus_properties); 30949ab747fSPaolo Bonzini } 31049ab747fSPaolo Bonzini 31149ab747fSPaolo Bonzini static const TypeInfo gus_info = { 31211c7549dSAndreas Färber .name = TYPE_GUS, 31349ab747fSPaolo Bonzini .parent = TYPE_ISA_DEVICE, 31449ab747fSPaolo Bonzini .instance_size = sizeof (GUSState), 31549ab747fSPaolo Bonzini .class_init = gus_class_initfn, 31649ab747fSPaolo Bonzini }; 31749ab747fSPaolo Bonzini 31849ab747fSPaolo Bonzini static void gus_register_types (void) 31949ab747fSPaolo Bonzini { 32049ab747fSPaolo Bonzini type_register_static (&gus_info); 321ba541176SGerd Hoffmann deprecated_register_soundhw("gus", "Gravis Ultrasound GF1", 1, TYPE_GUS); 32249ab747fSPaolo Bonzini } 32349ab747fSPaolo Bonzini 32449ab747fSPaolo Bonzini type_init (gus_register_types) 325