xref: /openbmc/qemu/hw/audio/gus.c (revision 9d49b1c9edf829e571093088ddff0b73db3110c6)
1  /*
2   * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
3   *
4   * Copyright (c) 2002-2005 Vassili Karpov (malc)
5   *
6   * Permission is hereby granted, free of charge, to any person obtaining a copy
7   * of this software and associated documentation files (the "Software"), to deal
8   * in the Software without restriction, including without limitation the rights
9   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10   * copies of the Software, and to permit persons to whom the Software is
11   * furnished to do so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in
14   * all copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22   * THE SOFTWARE.
23   */
24  
25  #include "qemu/osdep.h"
26  #include "qapi/error.h"
27  #include "qemu/module.h"
28  #include "hw/audio/soundhw.h"
29  #include "audio/audio.h"
30  #include "hw/irq.h"
31  #include "hw/isa/isa.h"
32  #include "hw/qdev-properties.h"
33  #include "migration/vmstate.h"
34  #include "gusemu.h"
35  #include "gustate.h"
36  #include "qom/object.h"
37  
38  #define dolog(...) AUD_log ("audio", __VA_ARGS__)
39  #ifdef DEBUG
40  #define ldebug(...) dolog (__VA_ARGS__)
41  #else
42  #define ldebug(...)
43  #endif
44  
45  #define TYPE_GUS "gus"
46  OBJECT_DECLARE_SIMPLE_TYPE(GUSState, GUS)
47  
48  struct GUSState {
49      ISADevice dev;
50      GUSEmuState emu;
51      QEMUSoundCard card;
52      uint32_t freq;
53      uint32_t port;
54      int pos, left, shift, irqs;
55      int16_t *mixbuf;
56      uint8_t himem[1024 * 1024 + 32 + 4096];
57      int samples;
58      SWVoiceOut *voice;
59      int64_t last_ticks;
60      qemu_irq pic;
61      IsaDma *isa_dma;
62      PortioList portio_list1;
63      PortioList portio_list2;
64  };
65  
66  static uint32_t gus_readb(void *opaque, uint32_t nport)
67  {
68      GUSState *s = opaque;
69  
70      return gus_read (&s->emu, nport, 1);
71  }
72  
73  static void gus_writeb(void *opaque, uint32_t nport, uint32_t val)
74  {
75      GUSState *s = opaque;
76  
77      gus_write (&s->emu, nport, 1, val);
78  }
79  
80  static int write_audio (GUSState *s, int samples)
81  {
82      int net = 0;
83      int pos = s->pos;
84  
85      while (samples) {
86          int nbytes, wbytes, wsampl;
87  
88          nbytes = samples << s->shift;
89          wbytes = AUD_write (
90              s->voice,
91              s->mixbuf + (pos << (s->shift - 1)),
92              nbytes
93              );
94  
95          if (wbytes) {
96              wsampl = wbytes >> s->shift;
97  
98              samples -= wsampl;
99              pos = (pos + wsampl) % s->samples;
100  
101              net += wsampl;
102          }
103          else {
104              break;
105          }
106      }
107  
108      return net;
109  }
110  
111  static void GUS_callback (void *opaque, int free)
112  {
113      int samples, to_play, net = 0;
114      GUSState *s = opaque;
115  
116      samples = free >> s->shift;
117      to_play = MIN (samples, s->left);
118  
119      while (to_play) {
120          int written = write_audio (s, to_play);
121  
122          if (!written) {
123              goto reset;
124          }
125  
126          s->left -= written;
127          to_play -= written;
128          samples -= written;
129          net += written;
130      }
131  
132      samples = MIN (samples, s->samples);
133      if (samples) {
134          gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
135  
136          while (samples) {
137              int written = write_audio (s, samples);
138              if (!written) {
139                  break;
140              }
141              samples -= written;
142              net += written;
143          }
144      }
145      s->left = samples;
146  
147   reset:
148      gus_irqgen (&s->emu, (uint64_t)net * 1000000 / s->freq);
149  }
150  
151  int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n)
152  {
153      GUSState *s = emu->opaque;
154      /* qemu_irq_lower (s->pic); */
155      qemu_irq_raise (s->pic);
156      s->irqs += n;
157      ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
158      return n;
159  }
160  
161  void GUS_irqclear (GUSEmuState *emu, int hwirq)
162  {
163      GUSState *s = emu->opaque;
164      ldebug ("irqclear %d %d\n", hwirq, s->irqs);
165      qemu_irq_lower (s->pic);
166      s->irqs -= 1;
167  #ifdef IRQ_STORM
168      if (s->irqs > 0) {
169          qemu_irq_raise (s->pic[hwirq]);
170      }
171  #endif
172  }
173  
174  void GUS_dmarequest (GUSEmuState *emu)
175  {
176      GUSState *s = emu->opaque;
177      IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
178      ldebug ("dma request %d\n", der->gusdma);
179      k->hold_DREQ(s->isa_dma, s->emu.gusdma);
180  }
181  
182  static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
183  {
184      GUSState *s = opaque;
185      IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
186      char tmpbuf[4096];
187      int pos = dma_pos, mode, left = dma_len - dma_pos;
188  
189      ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
190      mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma);
191      while (left) {
192          int to_copy = MIN ((size_t) left, sizeof (tmpbuf));
193          int copied;
194  
195          ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
196          copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy);
197          gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
198          left -= copied;
199          pos += copied;
200      }
201  
202      if (((mode >> 4) & 1) == 0) {
203          k->release_DREQ(s->isa_dma, s->emu.gusdma);
204      }
205      return dma_len;
206  }
207  
208  static const VMStateDescription vmstate_gus = {
209      .name = "gus",
210      .version_id = 2,
211      .minimum_version_id = 2,
212      .fields = (const VMStateField[]) {
213          VMSTATE_INT32 (pos, GUSState),
214          VMSTATE_INT32 (left, GUSState),
215          VMSTATE_INT32 (shift, GUSState),
216          VMSTATE_INT32 (irqs, GUSState),
217          VMSTATE_INT32 (samples, GUSState),
218          VMSTATE_INT64 (last_ticks, GUSState),
219          VMSTATE_BUFFER (himem, GUSState),
220          VMSTATE_END_OF_LIST ()
221      }
222  };
223  
224  static const MemoryRegionPortio gus_portio_list1[] = {
225      {0x000,  1, 1, .write = gus_writeb },
226      {0x006, 10, 1, .read = gus_readb, .write = gus_writeb },
227      {0x100,  8, 1, .read = gus_readb, .write = gus_writeb },
228      PORTIO_END_OF_LIST (),
229  };
230  
231  static const MemoryRegionPortio gus_portio_list2[] = {
232      {0, 2, 1, .read = gus_readb },
233      PORTIO_END_OF_LIST (),
234  };
235  
236  static void gus_realizefn (DeviceState *dev, Error **errp)
237  {
238      ISADevice *d = ISA_DEVICE(dev);
239      ISABus *bus = isa_bus_from_device(d);
240      GUSState *s = GUS (dev);
241      IsaDmaClass *k;
242      struct audsettings as;
243  
244      if (!AUD_register_card ("gus", &s->card, errp)) {
245          return;
246      }
247  
248      s->isa_dma = isa_bus_get_dma(bus, s->emu.gusdma);
249      if (!s->isa_dma) {
250          error_setg(errp, "ISA controller does not support DMA");
251          return;
252      }
253  
254      as.freq = s->freq;
255      as.nchannels = 2;
256      as.fmt = AUDIO_FORMAT_S16;
257      as.endianness = AUDIO_HOST_ENDIANNESS;
258  
259      s->voice = AUD_open_out (
260          &s->card,
261          NULL,
262          "gus",
263          s,
264          GUS_callback,
265          &as
266          );
267  
268      if (!s->voice) {
269          AUD_remove_card (&s->card);
270          error_setg(errp, "No voice");
271          return;
272      }
273  
274      s->shift = 2;
275      s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift;
276      s->mixbuf = g_malloc0 (s->samples << s->shift);
277  
278      isa_register_portio_list(d, &s->portio_list1, s->port,
279                               gus_portio_list1, s, "gus");
280      isa_register_portio_list(d, &s->portio_list2, (s->port + 0x100) & 0xf00,
281                               gus_portio_list2, s, "gus");
282  
283      k = ISADMA_GET_CLASS(s->isa_dma);
284      k->register_channel(s->isa_dma, s->emu.gusdma, GUS_read_DMA, s);
285      s->emu.himemaddr = s->himem;
286      s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
287      s->emu.opaque = s;
288      s->pic = isa_bus_get_irq(bus, s->emu.gusirq);
289  
290      AUD_set_active_out (s->voice, 1);
291  }
292  
293  static Property gus_properties[] = {
294      DEFINE_AUDIO_PROPERTIES(GUSState, card),
295      DEFINE_PROP_UINT32 ("freq",    GUSState, freq,        44100),
296      DEFINE_PROP_UINT32 ("iobase",  GUSState, port,        0x240),
297      DEFINE_PROP_UINT32 ("irq",     GUSState, emu.gusirq,  7),
298      DEFINE_PROP_UINT32 ("dma",     GUSState, emu.gusdma,  3),
299      DEFINE_PROP_END_OF_LIST (),
300  };
301  
302  static void gus_class_initfn (ObjectClass *klass, void *data)
303  {
304      DeviceClass *dc = DEVICE_CLASS (klass);
305  
306      dc->realize = gus_realizefn;
307      set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
308      dc->desc = "Gravis Ultrasound GF1";
309      dc->vmsd = &vmstate_gus;
310      device_class_set_props(dc, gus_properties);
311  }
312  
313  static const TypeInfo gus_info = {
314      .name          = TYPE_GUS,
315      .parent        = TYPE_ISA_DEVICE,
316      .instance_size = sizeof (GUSState),
317      .class_init    = gus_class_initfn,
318  };
319  
320  static void gus_register_types (void)
321  {
322      type_register_static (&gus_info);
323      deprecated_register_soundhw("gus", "Gravis Ultrasound GF1", 1, TYPE_GUS);
324  }
325  
326  type_init (gus_register_types)
327