xref: /openbmc/qemu/hw/audio/sb16.c (revision 0c1eccd368af8805ec0fb11e6cf25d0684d37328)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU Soundblaster 16 emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003-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"
268a824e4dSEduardo Habkost #include "hw/audio/soundhw.h"
2749ab747fSPaolo Bonzini #include "audio/audio.h"
2864552b6bSMarkus Armbruster #include "hw/irq.h"
2949ab747fSPaolo Bonzini #include "hw/isa/isa.h"
30a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
3249ab747fSPaolo Bonzini #include "qemu/timer.h"
3349ab747fSPaolo Bonzini #include "qemu/host-utils.h"
348ec660b8SJohn Arbuckle #include "qemu/log.h"
350b8fa32fSMarkus Armbruster #include "qemu/module.h"
368ec660b8SJohn Arbuckle #include "qapi/error.h"
37db1015e9SEduardo Habkost #include "qom/object.h"
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini #define dolog(...) AUD_log ("sb16", __VA_ARGS__)
4049ab747fSPaolo Bonzini 
4149ab747fSPaolo Bonzini /* #define DEBUG */
4249ab747fSPaolo Bonzini /* #define DEBUG_SB16_MOST */
4349ab747fSPaolo Bonzini 
4449ab747fSPaolo Bonzini #ifdef DEBUG
4549ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__)
4649ab747fSPaolo Bonzini #else
4749ab747fSPaolo Bonzini #define ldebug(...)
4849ab747fSPaolo Bonzini #endif
4949ab747fSPaolo Bonzini 
5049ab747fSPaolo Bonzini static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
5149ab747fSPaolo Bonzini 
52399f05a6SAndreas Färber #define TYPE_SB16 "sb16"
538063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(SB16State, SB16)
54399f05a6SAndreas Färber 
55db1015e9SEduardo Habkost struct SB16State {
56399f05a6SAndreas Färber     ISADevice parent_obj;
57399f05a6SAndreas Färber 
5849ab747fSPaolo Bonzini     QEMUSoundCard card;
5949ab747fSPaolo Bonzini     qemu_irq pic;
6049ab747fSPaolo Bonzini     uint32_t irq;
6149ab747fSPaolo Bonzini     uint32_t dma;
6249ab747fSPaolo Bonzini     uint32_t hdma;
6349ab747fSPaolo Bonzini     uint32_t port;
6449ab747fSPaolo Bonzini     uint32_t ver;
65f203c16eSHervé Poussineau     IsaDma *isa_dma;
66f203c16eSHervé Poussineau     IsaDma *isa_hdma;
6749ab747fSPaolo Bonzini 
6849ab747fSPaolo Bonzini     int in_index;
6949ab747fSPaolo Bonzini     int out_data_len;
7049ab747fSPaolo Bonzini     int fmt_stereo;
7149ab747fSPaolo Bonzini     int fmt_signed;
7249ab747fSPaolo Bonzini     int fmt_bits;
7385bc5852SKővágó, Zoltán     AudioFormat fmt;
7449ab747fSPaolo Bonzini     int dma_auto;
7549ab747fSPaolo Bonzini     int block_size;
7649ab747fSPaolo Bonzini     int fifo;
7749ab747fSPaolo Bonzini     int freq;
7849ab747fSPaolo Bonzini     int time_const;
7949ab747fSPaolo Bonzini     int speaker;
8049ab747fSPaolo Bonzini     int needed_bytes;
8149ab747fSPaolo Bonzini     int cmd;
8249ab747fSPaolo Bonzini     int use_hdma;
8349ab747fSPaolo Bonzini     int highspeed;
8449ab747fSPaolo Bonzini     int can_write;
8549ab747fSPaolo Bonzini 
8649ab747fSPaolo Bonzini     int v2x6;
8749ab747fSPaolo Bonzini 
8849ab747fSPaolo Bonzini     uint8_t csp_param;
8949ab747fSPaolo Bonzini     uint8_t csp_value;
9049ab747fSPaolo Bonzini     uint8_t csp_mode;
9149ab747fSPaolo Bonzini     uint8_t csp_regs[256];
9249ab747fSPaolo Bonzini     uint8_t csp_index;
9349ab747fSPaolo Bonzini     uint8_t csp_reg83[4];
9449ab747fSPaolo Bonzini     int csp_reg83r;
9549ab747fSPaolo Bonzini     int csp_reg83w;
9649ab747fSPaolo Bonzini 
9749ab747fSPaolo Bonzini     uint8_t in2_data[10];
9849ab747fSPaolo Bonzini     uint8_t out_data[50];
9949ab747fSPaolo Bonzini     uint8_t test_reg;
10049ab747fSPaolo Bonzini     uint8_t last_read_byte;
10149ab747fSPaolo Bonzini     int nzero;
10249ab747fSPaolo Bonzini 
10349ab747fSPaolo Bonzini     int left_till_irq;
10449ab747fSPaolo Bonzini 
10549ab747fSPaolo Bonzini     int dma_running;
10649ab747fSPaolo Bonzini     int bytes_per_second;
10749ab747fSPaolo Bonzini     int align;
10849ab747fSPaolo Bonzini     int audio_free;
10949ab747fSPaolo Bonzini     SWVoiceOut *voice;
11049ab747fSPaolo Bonzini 
11149ab747fSPaolo Bonzini     QEMUTimer *aux_ts;
11249ab747fSPaolo Bonzini     /* mixer state */
11349ab747fSPaolo Bonzini     int mixer_nreg;
11449ab747fSPaolo Bonzini     uint8_t mixer_regs[256];
115e305a165SMarc-André Lureau     PortioList portio_list;
116db1015e9SEduardo Habkost };
11749ab747fSPaolo Bonzini 
118a2cd86a9SPhilippe Mathieu-Daudé #define SAMPLE_RATE_MIN 5000
119a2cd86a9SPhilippe Mathieu-Daudé #define SAMPLE_RATE_MAX 45000
120a2cd86a9SPhilippe Mathieu-Daudé 
12149ab747fSPaolo Bonzini static void SB_audio_callback (void *opaque, int free);
12249ab747fSPaolo Bonzini 
magic_of_irq(int irq)12349ab747fSPaolo Bonzini static int magic_of_irq (int irq)
12449ab747fSPaolo Bonzini {
12549ab747fSPaolo Bonzini     switch (irq) {
12649ab747fSPaolo Bonzini     case 5:
12749ab747fSPaolo Bonzini         return 2;
12849ab747fSPaolo Bonzini     case 7:
12949ab747fSPaolo Bonzini         return 4;
13049ab747fSPaolo Bonzini     case 9:
13149ab747fSPaolo Bonzini         return 1;
13249ab747fSPaolo Bonzini     case 10:
13349ab747fSPaolo Bonzini         return 8;
13449ab747fSPaolo Bonzini     default:
1358ec660b8SJohn Arbuckle         qemu_log_mask(LOG_GUEST_ERROR, "bad irq %d\n", irq);
13649ab747fSPaolo Bonzini         return 2;
13749ab747fSPaolo Bonzini     }
13849ab747fSPaolo Bonzini }
13949ab747fSPaolo Bonzini 
irq_of_magic(int magic)14049ab747fSPaolo Bonzini static int irq_of_magic (int magic)
14149ab747fSPaolo Bonzini {
14249ab747fSPaolo Bonzini     switch (magic) {
14349ab747fSPaolo Bonzini     case 1:
14449ab747fSPaolo Bonzini         return 9;
14549ab747fSPaolo Bonzini     case 2:
14649ab747fSPaolo Bonzini         return 5;
14749ab747fSPaolo Bonzini     case 4:
14849ab747fSPaolo Bonzini         return 7;
14949ab747fSPaolo Bonzini     case 8:
15049ab747fSPaolo Bonzini         return 10;
15149ab747fSPaolo Bonzini     default:
1528ec660b8SJohn Arbuckle         qemu_log_mask(LOG_GUEST_ERROR, "bad irq magic %d\n", magic);
15349ab747fSPaolo Bonzini         return -1;
15449ab747fSPaolo Bonzini     }
15549ab747fSPaolo Bonzini }
15649ab747fSPaolo Bonzini 
15749ab747fSPaolo Bonzini #if 0
15849ab747fSPaolo Bonzini static void log_dsp (SB16State *dsp)
15949ab747fSPaolo Bonzini {
16049ab747fSPaolo Bonzini     ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
16149ab747fSPaolo Bonzini             dsp->fmt_stereo ? "Stereo" : "Mono",
16249ab747fSPaolo Bonzini             dsp->fmt_signed ? "Signed" : "Unsigned",
16349ab747fSPaolo Bonzini             dsp->fmt_bits,
16449ab747fSPaolo Bonzini             dsp->dma_auto ? "Auto" : "Single",
16549ab747fSPaolo Bonzini             dsp->block_size,
16649ab747fSPaolo Bonzini             dsp->freq,
16749ab747fSPaolo Bonzini             dsp->time_const,
16849ab747fSPaolo Bonzini             dsp->speaker);
16949ab747fSPaolo Bonzini }
17049ab747fSPaolo Bonzini #endif
17149ab747fSPaolo Bonzini 
speaker(SB16State * s,int on)17249ab747fSPaolo Bonzini static void speaker (SB16State *s, int on)
17349ab747fSPaolo Bonzini {
17449ab747fSPaolo Bonzini     s->speaker = on;
17549ab747fSPaolo Bonzini     /* AUD_enable (s->voice, on); */
17649ab747fSPaolo Bonzini }
17749ab747fSPaolo Bonzini 
control(SB16State * s,int hold)17849ab747fSPaolo Bonzini static void control (SB16State *s, int hold)
17949ab747fSPaolo Bonzini {
18049ab747fSPaolo Bonzini     int dma = s->use_hdma ? s->hdma : s->dma;
181f203c16eSHervé Poussineau     IsaDma *isa_dma = s->use_hdma ? s->isa_hdma : s->isa_dma;
182f203c16eSHervé Poussineau     IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma);
18349ab747fSPaolo Bonzini     s->dma_running = hold;
18449ab747fSPaolo Bonzini 
18549ab747fSPaolo Bonzini     ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
18649ab747fSPaolo Bonzini 
18749ab747fSPaolo Bonzini     if (hold) {
188f203c16eSHervé Poussineau         k->hold_DREQ(isa_dma, dma);
18949ab747fSPaolo Bonzini         AUD_set_active_out (s->voice, 1);
19049ab747fSPaolo Bonzini     }
19149ab747fSPaolo Bonzini     else {
192f203c16eSHervé Poussineau         k->release_DREQ(isa_dma, dma);
19349ab747fSPaolo Bonzini         AUD_set_active_out (s->voice, 0);
19449ab747fSPaolo Bonzini     }
19549ab747fSPaolo Bonzini }
19649ab747fSPaolo Bonzini 
aux_timer(void * opaque)19749ab747fSPaolo Bonzini static void aux_timer (void *opaque)
19849ab747fSPaolo Bonzini {
19949ab747fSPaolo Bonzini     SB16State *s = opaque;
20049ab747fSPaolo Bonzini     s->can_write = 1;
20149ab747fSPaolo Bonzini     qemu_irq_raise (s->pic);
20249ab747fSPaolo Bonzini }
20349ab747fSPaolo Bonzini 
20449ab747fSPaolo Bonzini #define DMA8_AUTO 1
20549ab747fSPaolo Bonzini #define DMA8_HIGH 2
20649ab747fSPaolo Bonzini 
continue_dma8(SB16State * s)20749ab747fSPaolo Bonzini static void continue_dma8 (SB16State *s)
20849ab747fSPaolo Bonzini {
20949ab747fSPaolo Bonzini     if (s->freq > 0) {
21049ab747fSPaolo Bonzini         struct audsettings as;
21149ab747fSPaolo Bonzini 
21249ab747fSPaolo Bonzini         s->audio_free = 0;
21349ab747fSPaolo Bonzini 
21449ab747fSPaolo Bonzini         as.freq = s->freq;
21549ab747fSPaolo Bonzini         as.nchannels = 1 << s->fmt_stereo;
21649ab747fSPaolo Bonzini         as.fmt = s->fmt;
21749ab747fSPaolo Bonzini         as.endianness = 0;
21849ab747fSPaolo Bonzini 
21949ab747fSPaolo Bonzini         s->voice = AUD_open_out (
22049ab747fSPaolo Bonzini             &s->card,
22149ab747fSPaolo Bonzini             s->voice,
22249ab747fSPaolo Bonzini             "sb16",
22349ab747fSPaolo Bonzini             s,
22449ab747fSPaolo Bonzini             SB_audio_callback,
22549ab747fSPaolo Bonzini             &as
22649ab747fSPaolo Bonzini             );
22749ab747fSPaolo Bonzini     }
22849ab747fSPaolo Bonzini 
22949ab747fSPaolo Bonzini     control (s, 1);
23049ab747fSPaolo Bonzini }
23149ab747fSPaolo Bonzini 
restrict_sampling_rate(int freq)23260e543f5SQiang Liu static inline int restrict_sampling_rate(int freq)
23360e543f5SQiang Liu {
23460e543f5SQiang Liu     if (freq < SAMPLE_RATE_MIN) {
23560e543f5SQiang Liu         qemu_log_mask(LOG_GUEST_ERROR,
23660e543f5SQiang Liu                       "sampling range too low: %d, increasing to %u\n",
23760e543f5SQiang Liu                       freq, SAMPLE_RATE_MIN);
23860e543f5SQiang Liu         return SAMPLE_RATE_MIN;
23960e543f5SQiang Liu     } else if (freq > SAMPLE_RATE_MAX) {
24060e543f5SQiang Liu         qemu_log_mask(LOG_GUEST_ERROR,
24160e543f5SQiang Liu                       "sampling range too high: %d, decreasing to %u\n",
24260e543f5SQiang Liu                       freq, SAMPLE_RATE_MAX);
24360e543f5SQiang Liu         return SAMPLE_RATE_MAX;
24460e543f5SQiang Liu     } else {
24560e543f5SQiang Liu         return freq;
24660e543f5SQiang Liu     }
24760e543f5SQiang Liu }
24860e543f5SQiang Liu 
dma_cmd8(SB16State * s,int mask,int dma_len)24949ab747fSPaolo Bonzini static void dma_cmd8 (SB16State *s, int mask, int dma_len)
25049ab747fSPaolo Bonzini {
25185bc5852SKővágó, Zoltán     s->fmt = AUDIO_FORMAT_U8;
25249ab747fSPaolo Bonzini     s->use_hdma = 0;
25349ab747fSPaolo Bonzini     s->fmt_bits = 8;
25449ab747fSPaolo Bonzini     s->fmt_signed = 0;
25549ab747fSPaolo Bonzini     s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
25649ab747fSPaolo Bonzini     if (-1 == s->time_const) {
25749ab747fSPaolo Bonzini         if (s->freq <= 0)
25849ab747fSPaolo Bonzini             s->freq = 11025;
25949ab747fSPaolo Bonzini     }
26049ab747fSPaolo Bonzini     else {
26149ab747fSPaolo Bonzini         int tmp = (256 - s->time_const);
26249ab747fSPaolo Bonzini         s->freq = (1000000 + (tmp / 2)) / tmp;
26349ab747fSPaolo Bonzini     }
26460e543f5SQiang Liu     s->freq = restrict_sampling_rate(s->freq);
26549ab747fSPaolo Bonzini 
26649ab747fSPaolo Bonzini     if (dma_len != -1) {
26749ab747fSPaolo Bonzini         s->block_size = dma_len << s->fmt_stereo;
26849ab747fSPaolo Bonzini     }
26949ab747fSPaolo Bonzini     else {
27049ab747fSPaolo Bonzini         /* This is apparently the only way to make both Act1/PL
27149ab747fSPaolo Bonzini            and SecondReality/FC work
27249ab747fSPaolo Bonzini 
27349ab747fSPaolo Bonzini            Act1 sets block size via command 0x48 and it's an odd number
27449ab747fSPaolo Bonzini            SR does the same with even number
27549ab747fSPaolo Bonzini            Both use stereo, and Creatives own documentation states that
27649ab747fSPaolo Bonzini            0x48 sets block size in bytes less one.. go figure */
27749ab747fSPaolo Bonzini         s->block_size &= ~s->fmt_stereo;
27849ab747fSPaolo Bonzini     }
27949ab747fSPaolo Bonzini 
28049ab747fSPaolo Bonzini     s->freq >>= s->fmt_stereo;
28149ab747fSPaolo Bonzini     s->left_till_irq = s->block_size;
28249ab747fSPaolo Bonzini     s->bytes_per_second = (s->freq << s->fmt_stereo);
28349ab747fSPaolo Bonzini     /* s->highspeed = (mask & DMA8_HIGH) != 0; */
28449ab747fSPaolo Bonzini     s->dma_auto = (mask & DMA8_AUTO) != 0;
28549ab747fSPaolo Bonzini     s->align = (1 << s->fmt_stereo) - 1;
28649ab747fSPaolo Bonzini 
28749ab747fSPaolo Bonzini     if (s->block_size & s->align) {
2888ec660b8SJohn Arbuckle         qemu_log_mask(LOG_GUEST_ERROR, "warning: misaligned block size %d,"
2898ec660b8SJohn Arbuckle                       " alignment %d\n", s->block_size, s->align + 1);
29049ab747fSPaolo Bonzini     }
29149ab747fSPaolo Bonzini 
29249ab747fSPaolo Bonzini     ldebug ("freq %d, stereo %d, sign %d, bits %d, "
29349ab747fSPaolo Bonzini             "dma %d, auto %d, fifo %d, high %d\n",
29449ab747fSPaolo Bonzini             s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
29549ab747fSPaolo Bonzini             s->block_size, s->dma_auto, s->fifo, s->highspeed);
29649ab747fSPaolo Bonzini 
29749ab747fSPaolo Bonzini     continue_dma8 (s);
29849ab747fSPaolo Bonzini     speaker (s, 1);
29949ab747fSPaolo Bonzini }
30049ab747fSPaolo Bonzini 
dma_cmd(SB16State * s,uint8_t cmd,uint8_t d0,int dma_len)30149ab747fSPaolo Bonzini static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
30249ab747fSPaolo Bonzini {
30349ab747fSPaolo Bonzini     s->use_hdma = cmd < 0xc0;
30449ab747fSPaolo Bonzini     s->fifo = (cmd >> 1) & 1;
30549ab747fSPaolo Bonzini     s->dma_auto = (cmd >> 2) & 1;
30649ab747fSPaolo Bonzini     s->fmt_signed = (d0 >> 4) & 1;
30749ab747fSPaolo Bonzini     s->fmt_stereo = (d0 >> 5) & 1;
30849ab747fSPaolo Bonzini 
30949ab747fSPaolo Bonzini     switch (cmd >> 4) {
31049ab747fSPaolo Bonzini     case 11:
31149ab747fSPaolo Bonzini         s->fmt_bits = 16;
31249ab747fSPaolo Bonzini         break;
31349ab747fSPaolo Bonzini 
31449ab747fSPaolo Bonzini     case 12:
31549ab747fSPaolo Bonzini         s->fmt_bits = 8;
31649ab747fSPaolo Bonzini         break;
31749ab747fSPaolo Bonzini     }
31849ab747fSPaolo Bonzini 
31949ab747fSPaolo Bonzini     if (-1 != s->time_const) {
32049ab747fSPaolo Bonzini #if 1
32149ab747fSPaolo Bonzini         int tmp = 256 - s->time_const;
32249ab747fSPaolo Bonzini         s->freq = (1000000 + (tmp / 2)) / tmp;
32349ab747fSPaolo Bonzini #else
32449ab747fSPaolo Bonzini         /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
32549ab747fSPaolo Bonzini         s->freq = 1000000 / ((255 - s->time_const));
32649ab747fSPaolo Bonzini #endif
32749ab747fSPaolo Bonzini         s->time_const = -1;
32849ab747fSPaolo Bonzini     }
32949ab747fSPaolo Bonzini 
33049ab747fSPaolo Bonzini     s->block_size = dma_len + 1;
33149ab747fSPaolo Bonzini     s->block_size <<= (s->fmt_bits == 16);
33249ab747fSPaolo Bonzini     if (!s->dma_auto) {
33349ab747fSPaolo Bonzini         /* It is clear that for DOOM and auto-init this value
33449ab747fSPaolo Bonzini            shouldn't take stereo into account, while Miles Sound Systems
33549ab747fSPaolo Bonzini            setsound.exe with single transfer mode wouldn't work without it
33649ab747fSPaolo Bonzini            wonders of SB16 yet again */
33749ab747fSPaolo Bonzini         s->block_size <<= s->fmt_stereo;
33849ab747fSPaolo Bonzini     }
33949ab747fSPaolo Bonzini 
34049ab747fSPaolo Bonzini     ldebug ("freq %d, stereo %d, sign %d, bits %d, "
34149ab747fSPaolo Bonzini             "dma %d, auto %d, fifo %d, high %d\n",
34249ab747fSPaolo Bonzini             s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
34349ab747fSPaolo Bonzini             s->block_size, s->dma_auto, s->fifo, s->highspeed);
34449ab747fSPaolo Bonzini 
34549ab747fSPaolo Bonzini     if (16 == s->fmt_bits) {
34649ab747fSPaolo Bonzini         if (s->fmt_signed) {
34785bc5852SKővágó, Zoltán             s->fmt = AUDIO_FORMAT_S16;
34849ab747fSPaolo Bonzini         }
34949ab747fSPaolo Bonzini         else {
35085bc5852SKővágó, Zoltán             s->fmt = AUDIO_FORMAT_U16;
35149ab747fSPaolo Bonzini         }
35249ab747fSPaolo Bonzini     }
35349ab747fSPaolo Bonzini     else {
35449ab747fSPaolo Bonzini         if (s->fmt_signed) {
35585bc5852SKővágó, Zoltán             s->fmt = AUDIO_FORMAT_S8;
35649ab747fSPaolo Bonzini         }
35749ab747fSPaolo Bonzini         else {
35885bc5852SKővágó, Zoltán             s->fmt = AUDIO_FORMAT_U8;
35949ab747fSPaolo Bonzini         }
36049ab747fSPaolo Bonzini     }
36149ab747fSPaolo Bonzini 
36249ab747fSPaolo Bonzini     s->left_till_irq = s->block_size;
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini     s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
36549ab747fSPaolo Bonzini     s->highspeed = 0;
36649ab747fSPaolo Bonzini     s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
36749ab747fSPaolo Bonzini     if (s->block_size & s->align) {
3688ec660b8SJohn Arbuckle         qemu_log_mask(LOG_GUEST_ERROR, "warning: misaligned block size %d,"
3698ec660b8SJohn Arbuckle                       " alignment %d\n", s->block_size, s->align + 1);
37049ab747fSPaolo Bonzini     }
37149ab747fSPaolo Bonzini 
37249ab747fSPaolo Bonzini     if (s->freq) {
37349ab747fSPaolo Bonzini         struct audsettings as;
37449ab747fSPaolo Bonzini 
37549ab747fSPaolo Bonzini         s->audio_free = 0;
37649ab747fSPaolo Bonzini 
37749ab747fSPaolo Bonzini         as.freq = s->freq;
37849ab747fSPaolo Bonzini         as.nchannels = 1 << s->fmt_stereo;
37949ab747fSPaolo Bonzini         as.fmt = s->fmt;
38049ab747fSPaolo Bonzini         as.endianness = 0;
38149ab747fSPaolo Bonzini 
38249ab747fSPaolo Bonzini         s->voice = AUD_open_out (
38349ab747fSPaolo Bonzini             &s->card,
38449ab747fSPaolo Bonzini             s->voice,
38549ab747fSPaolo Bonzini             "sb16",
38649ab747fSPaolo Bonzini             s,
38749ab747fSPaolo Bonzini             SB_audio_callback,
38849ab747fSPaolo Bonzini             &as
38949ab747fSPaolo Bonzini             );
39049ab747fSPaolo Bonzini     }
39149ab747fSPaolo Bonzini 
39249ab747fSPaolo Bonzini     control (s, 1);
39349ab747fSPaolo Bonzini     speaker (s, 1);
39449ab747fSPaolo Bonzini }
39549ab747fSPaolo Bonzini 
dsp_out_data(SB16State * s,uint8_t val)39649ab747fSPaolo Bonzini static inline void dsp_out_data (SB16State *s, uint8_t val)
39749ab747fSPaolo Bonzini {
39849ab747fSPaolo Bonzini     ldebug ("outdata %#x\n", val);
39949ab747fSPaolo Bonzini     if ((size_t) s->out_data_len < sizeof (s->out_data)) {
40049ab747fSPaolo Bonzini         s->out_data[s->out_data_len++] = val;
40149ab747fSPaolo Bonzini     }
40249ab747fSPaolo Bonzini }
40349ab747fSPaolo Bonzini 
dsp_get_data(SB16State * s)40449ab747fSPaolo Bonzini static inline uint8_t dsp_get_data (SB16State *s)
40549ab747fSPaolo Bonzini {
40649ab747fSPaolo Bonzini     if (s->in_index) {
40749ab747fSPaolo Bonzini         return s->in2_data[--s->in_index];
40849ab747fSPaolo Bonzini     }
40949ab747fSPaolo Bonzini     else {
41049ab747fSPaolo Bonzini         dolog ("buffer underflow\n");
41149ab747fSPaolo Bonzini         return 0;
41249ab747fSPaolo Bonzini     }
41349ab747fSPaolo Bonzini }
41449ab747fSPaolo Bonzini 
command(SB16State * s,uint8_t cmd)41549ab747fSPaolo Bonzini static void command (SB16State *s, uint8_t cmd)
41649ab747fSPaolo Bonzini {
41749ab747fSPaolo Bonzini     ldebug ("command %#x\n", cmd);
41849ab747fSPaolo Bonzini 
41949ab747fSPaolo Bonzini     if (cmd > 0xaf && cmd < 0xd0) {
42049ab747fSPaolo Bonzini         if (cmd & 8) {
4218ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "ADC not yet supported (command %#x)\n",
4228ec660b8SJohn Arbuckle                           cmd);
42349ab747fSPaolo Bonzini         }
42449ab747fSPaolo Bonzini 
42549ab747fSPaolo Bonzini         switch (cmd >> 4) {
42649ab747fSPaolo Bonzini         case 11:
42749ab747fSPaolo Bonzini         case 12:
42849ab747fSPaolo Bonzini             break;
42949ab747fSPaolo Bonzini         default:
4308ec660b8SJohn Arbuckle             qemu_log_mask(LOG_GUEST_ERROR, "%#x wrong bits\n", cmd);
43149ab747fSPaolo Bonzini         }
43249ab747fSPaolo Bonzini         s->needed_bytes = 3;
43349ab747fSPaolo Bonzini     }
43449ab747fSPaolo Bonzini     else {
43549ab747fSPaolo Bonzini         s->needed_bytes = 0;
43649ab747fSPaolo Bonzini 
43749ab747fSPaolo Bonzini         switch (cmd) {
43849ab747fSPaolo Bonzini         case 0x03:
43949ab747fSPaolo Bonzini             dsp_out_data (s, 0x10); /* s->csp_param); */
44049ab747fSPaolo Bonzini             goto warn;
44149ab747fSPaolo Bonzini 
44249ab747fSPaolo Bonzini         case 0x04:
44349ab747fSPaolo Bonzini             s->needed_bytes = 1;
44449ab747fSPaolo Bonzini             goto warn;
44549ab747fSPaolo Bonzini 
44649ab747fSPaolo Bonzini         case 0x05:
44749ab747fSPaolo Bonzini             s->needed_bytes = 2;
44849ab747fSPaolo Bonzini             goto warn;
44949ab747fSPaolo Bonzini 
45049ab747fSPaolo Bonzini         case 0x08:
45149ab747fSPaolo Bonzini             /* __asm__ ("int3"); */
45249ab747fSPaolo Bonzini             goto warn;
45349ab747fSPaolo Bonzini 
45449ab747fSPaolo Bonzini         case 0x0e:
45549ab747fSPaolo Bonzini             s->needed_bytes = 2;
45649ab747fSPaolo Bonzini             goto warn;
45749ab747fSPaolo Bonzini 
45849ab747fSPaolo Bonzini         case 0x09:
45949ab747fSPaolo Bonzini             dsp_out_data (s, 0xf8);
46049ab747fSPaolo Bonzini             goto warn;
46149ab747fSPaolo Bonzini 
46249ab747fSPaolo Bonzini         case 0x0f:
46349ab747fSPaolo Bonzini             s->needed_bytes = 1;
46449ab747fSPaolo Bonzini             goto warn;
46549ab747fSPaolo Bonzini 
46649ab747fSPaolo Bonzini         case 0x10:
46749ab747fSPaolo Bonzini             s->needed_bytes = 1;
46849ab747fSPaolo Bonzini             goto warn;
46949ab747fSPaolo Bonzini 
47049ab747fSPaolo Bonzini         case 0x14:
47149ab747fSPaolo Bonzini             s->needed_bytes = 2;
47249ab747fSPaolo Bonzini             s->block_size = 0;
47349ab747fSPaolo Bonzini             break;
47449ab747fSPaolo Bonzini 
47549ab747fSPaolo Bonzini         case 0x1c:              /* Auto-Initialize DMA DAC, 8-bit */
47649ab747fSPaolo Bonzini             dma_cmd8 (s, DMA8_AUTO, -1);
47749ab747fSPaolo Bonzini             break;
47849ab747fSPaolo Bonzini 
47949ab747fSPaolo Bonzini         case 0x20:              /* Direct ADC, Juice/PL */
48049ab747fSPaolo Bonzini             dsp_out_data (s, 0xff);
48149ab747fSPaolo Bonzini             goto warn;
48249ab747fSPaolo Bonzini 
48349ab747fSPaolo Bonzini         case 0x35:
4848ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "0x35 - MIDI command not implemented\n");
48549ab747fSPaolo Bonzini             break;
48649ab747fSPaolo Bonzini 
48749ab747fSPaolo Bonzini         case 0x40:
48849ab747fSPaolo Bonzini             s->freq = -1;
48949ab747fSPaolo Bonzini             s->time_const = -1;
49049ab747fSPaolo Bonzini             s->needed_bytes = 1;
49149ab747fSPaolo Bonzini             break;
49249ab747fSPaolo Bonzini 
49349ab747fSPaolo Bonzini         case 0x41:
49449ab747fSPaolo Bonzini             s->freq = -1;
49549ab747fSPaolo Bonzini             s->time_const = -1;
49649ab747fSPaolo Bonzini             s->needed_bytes = 2;
49749ab747fSPaolo Bonzini             break;
49849ab747fSPaolo Bonzini 
49949ab747fSPaolo Bonzini         case 0x42:
50049ab747fSPaolo Bonzini             s->freq = -1;
50149ab747fSPaolo Bonzini             s->time_const = -1;
50249ab747fSPaolo Bonzini             s->needed_bytes = 2;
50349ab747fSPaolo Bonzini             goto warn;
50449ab747fSPaolo Bonzini 
50549ab747fSPaolo Bonzini         case 0x45:
50649ab747fSPaolo Bonzini             dsp_out_data (s, 0xaa);
50749ab747fSPaolo Bonzini             goto warn;
50849ab747fSPaolo Bonzini 
50949ab747fSPaolo Bonzini         case 0x47:                /* Continue Auto-Initialize DMA 16bit */
51049ab747fSPaolo Bonzini             break;
51149ab747fSPaolo Bonzini 
51249ab747fSPaolo Bonzini         case 0x48:
51349ab747fSPaolo Bonzini             s->needed_bytes = 2;
51449ab747fSPaolo Bonzini             break;
51549ab747fSPaolo Bonzini 
51649ab747fSPaolo Bonzini         case 0x74:
51749ab747fSPaolo Bonzini             s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
5188ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "0x75 - DMA DAC, 4-bit ADPCM not"
5198ec660b8SJohn Arbuckle                           " implemented\n");
52049ab747fSPaolo Bonzini             break;
52149ab747fSPaolo Bonzini 
52249ab747fSPaolo Bonzini         case 0x75:              /* DMA DAC, 4-bit ADPCM Reference */
52349ab747fSPaolo Bonzini             s->needed_bytes = 2;
5248ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 4-bit ADPCM Reference not"
5258ec660b8SJohn Arbuckle                           " implemented\n");
52649ab747fSPaolo Bonzini             break;
52749ab747fSPaolo Bonzini 
52849ab747fSPaolo Bonzini         case 0x76:              /* DMA DAC, 2.6-bit ADPCM */
52949ab747fSPaolo Bonzini             s->needed_bytes = 2;
5308ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 2.6-bit ADPCM not"
5318ec660b8SJohn Arbuckle                           " implemented\n");
53249ab747fSPaolo Bonzini             break;
53349ab747fSPaolo Bonzini 
53449ab747fSPaolo Bonzini         case 0x77:              /* DMA DAC, 2.6-bit ADPCM Reference */
53549ab747fSPaolo Bonzini             s->needed_bytes = 2;
5368ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 2.6-bit ADPCM Reference"
5378ec660b8SJohn Arbuckle                           " not implemented\n");
53849ab747fSPaolo Bonzini             break;
53949ab747fSPaolo Bonzini 
54049ab747fSPaolo Bonzini         case 0x7d:
5418ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "0x7d - Autio-Initialize DMA DAC, 4-bit"
5428ec660b8SJohn Arbuckle                           " ADPCM Reference\n");
5438ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "not implemented\n");
54449ab747fSPaolo Bonzini             break;
54549ab747fSPaolo Bonzini 
54649ab747fSPaolo Bonzini         case 0x7f:
5478ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "0x7d - Autio-Initialize DMA DAC, 2.6-bit"
5488ec660b8SJohn Arbuckle                           " ADPCM Reference\n");
5498ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "not implemented\n");
55049ab747fSPaolo Bonzini             break;
55149ab747fSPaolo Bonzini 
55249ab747fSPaolo Bonzini         case 0x80:
55349ab747fSPaolo Bonzini             s->needed_bytes = 2;
55449ab747fSPaolo Bonzini             break;
55549ab747fSPaolo Bonzini 
55649ab747fSPaolo Bonzini         case 0x90:
55749ab747fSPaolo Bonzini         case 0x91:
55849ab747fSPaolo Bonzini             dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1);
55949ab747fSPaolo Bonzini             break;
56049ab747fSPaolo Bonzini 
56149ab747fSPaolo Bonzini         case 0xd0:              /* halt DMA operation. 8bit */
56249ab747fSPaolo Bonzini             control (s, 0);
56349ab747fSPaolo Bonzini             break;
56449ab747fSPaolo Bonzini 
56549ab747fSPaolo Bonzini         case 0xd1:              /* speaker on */
56649ab747fSPaolo Bonzini             speaker (s, 1);
56749ab747fSPaolo Bonzini             break;
56849ab747fSPaolo Bonzini 
56949ab747fSPaolo Bonzini         case 0xd3:              /* speaker off */
57049ab747fSPaolo Bonzini             speaker (s, 0);
57149ab747fSPaolo Bonzini             break;
57249ab747fSPaolo Bonzini 
57349ab747fSPaolo Bonzini         case 0xd4:              /* continue DMA operation. 8bit */
57449ab747fSPaolo Bonzini             /* KQ6 (or maybe Sierras audblst.drv in general) resets
57549ab747fSPaolo Bonzini                the frequency between halt/continue */
57649ab747fSPaolo Bonzini             continue_dma8 (s);
57749ab747fSPaolo Bonzini             break;
57849ab747fSPaolo Bonzini 
57949ab747fSPaolo Bonzini         case 0xd5:              /* halt DMA operation. 16bit */
58049ab747fSPaolo Bonzini             control (s, 0);
58149ab747fSPaolo Bonzini             break;
58249ab747fSPaolo Bonzini 
58349ab747fSPaolo Bonzini         case 0xd6:              /* continue DMA operation. 16bit */
58449ab747fSPaolo Bonzini             control (s, 1);
58549ab747fSPaolo Bonzini             break;
58649ab747fSPaolo Bonzini 
58749ab747fSPaolo Bonzini         case 0xd9:              /* exit auto-init DMA after this block. 16bit */
58849ab747fSPaolo Bonzini             s->dma_auto = 0;
58949ab747fSPaolo Bonzini             break;
59049ab747fSPaolo Bonzini 
59149ab747fSPaolo Bonzini         case 0xda:              /* exit auto-init DMA after this block. 8bit */
59249ab747fSPaolo Bonzini             s->dma_auto = 0;
59349ab747fSPaolo Bonzini             break;
59449ab747fSPaolo Bonzini 
59549ab747fSPaolo Bonzini         case 0xe0:              /* DSP identification */
59649ab747fSPaolo Bonzini             s->needed_bytes = 1;
59749ab747fSPaolo Bonzini             break;
59849ab747fSPaolo Bonzini 
59949ab747fSPaolo Bonzini         case 0xe1:
60049ab747fSPaolo Bonzini             dsp_out_data (s, s->ver & 0xff);
60149ab747fSPaolo Bonzini             dsp_out_data (s, s->ver >> 8);
60249ab747fSPaolo Bonzini             break;
60349ab747fSPaolo Bonzini 
60449ab747fSPaolo Bonzini         case 0xe2:
60549ab747fSPaolo Bonzini             s->needed_bytes = 1;
60649ab747fSPaolo Bonzini             goto warn;
60749ab747fSPaolo Bonzini 
60849ab747fSPaolo Bonzini         case 0xe3:
60949ab747fSPaolo Bonzini             {
61049ab747fSPaolo Bonzini                 int i;
61149ab747fSPaolo Bonzini                 for (i = sizeof (e3) - 1; i >= 0; --i)
61249ab747fSPaolo Bonzini                     dsp_out_data (s, e3[i]);
61349ab747fSPaolo Bonzini             }
61449ab747fSPaolo Bonzini             break;
61549ab747fSPaolo Bonzini 
61649ab747fSPaolo Bonzini         case 0xe4:              /* write test reg */
61749ab747fSPaolo Bonzini             s->needed_bytes = 1;
61849ab747fSPaolo Bonzini             break;
61949ab747fSPaolo Bonzini 
62049ab747fSPaolo Bonzini         case 0xe7:
6218ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "Attempt to probe for ESS (0xe7)?\n");
62249ab747fSPaolo Bonzini             break;
62349ab747fSPaolo Bonzini 
62449ab747fSPaolo Bonzini         case 0xe8:              /* read test reg */
62549ab747fSPaolo Bonzini             dsp_out_data (s, s->test_reg);
62649ab747fSPaolo Bonzini             break;
62749ab747fSPaolo Bonzini 
62849ab747fSPaolo Bonzini         case 0xf2:
62949ab747fSPaolo Bonzini         case 0xf3:
63049ab747fSPaolo Bonzini             dsp_out_data (s, 0xaa);
63149ab747fSPaolo Bonzini             s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
63249ab747fSPaolo Bonzini             qemu_irq_raise (s->pic);
63349ab747fSPaolo Bonzini             break;
63449ab747fSPaolo Bonzini 
63549ab747fSPaolo Bonzini         case 0xf9:
63649ab747fSPaolo Bonzini             s->needed_bytes = 1;
63749ab747fSPaolo Bonzini             goto warn;
63849ab747fSPaolo Bonzini 
63949ab747fSPaolo Bonzini         case 0xfa:
64049ab747fSPaolo Bonzini             dsp_out_data (s, 0);
64149ab747fSPaolo Bonzini             goto warn;
64249ab747fSPaolo Bonzini 
64349ab747fSPaolo Bonzini         case 0xfc:              /* FIXME */
64449ab747fSPaolo Bonzini             dsp_out_data (s, 0);
64549ab747fSPaolo Bonzini             goto warn;
64649ab747fSPaolo Bonzini 
64749ab747fSPaolo Bonzini         default:
6488ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "Unrecognized command %#x\n", cmd);
64949ab747fSPaolo Bonzini             break;
65049ab747fSPaolo Bonzini         }
65149ab747fSPaolo Bonzini     }
65249ab747fSPaolo Bonzini 
65349ab747fSPaolo Bonzini     if (!s->needed_bytes) {
65449ab747fSPaolo Bonzini         ldebug ("\n");
65549ab747fSPaolo Bonzini     }
65649ab747fSPaolo Bonzini 
65749ab747fSPaolo Bonzini  exit:
65849ab747fSPaolo Bonzini     if (!s->needed_bytes) {
65949ab747fSPaolo Bonzini         s->cmd = -1;
66049ab747fSPaolo Bonzini     }
66149ab747fSPaolo Bonzini     else {
66249ab747fSPaolo Bonzini         s->cmd = cmd;
66349ab747fSPaolo Bonzini     }
66449ab747fSPaolo Bonzini     return;
66549ab747fSPaolo Bonzini 
66649ab747fSPaolo Bonzini  warn:
6678ec660b8SJohn Arbuckle     qemu_log_mask(LOG_UNIMP, "warning: command %#x,%d is not truly understood"
6688ec660b8SJohn Arbuckle                   " yet\n", cmd, s->needed_bytes);
66949ab747fSPaolo Bonzini     goto exit;
67049ab747fSPaolo Bonzini 
67149ab747fSPaolo Bonzini }
67249ab747fSPaolo Bonzini 
dsp_get_lohi(SB16State * s)67349ab747fSPaolo Bonzini static uint16_t dsp_get_lohi (SB16State *s)
67449ab747fSPaolo Bonzini {
67549ab747fSPaolo Bonzini     uint8_t hi = dsp_get_data (s);
67649ab747fSPaolo Bonzini     uint8_t lo = dsp_get_data (s);
67749ab747fSPaolo Bonzini     return (hi << 8) | lo;
67849ab747fSPaolo Bonzini }
67949ab747fSPaolo Bonzini 
dsp_get_hilo(SB16State * s)68049ab747fSPaolo Bonzini static uint16_t dsp_get_hilo (SB16State *s)
68149ab747fSPaolo Bonzini {
68249ab747fSPaolo Bonzini     uint8_t lo = dsp_get_data (s);
68349ab747fSPaolo Bonzini     uint8_t hi = dsp_get_data (s);
68449ab747fSPaolo Bonzini     return (hi << 8) | lo;
68549ab747fSPaolo Bonzini }
68649ab747fSPaolo Bonzini 
complete(SB16State * s)68749ab747fSPaolo Bonzini static void complete (SB16State *s)
68849ab747fSPaolo Bonzini {
68949ab747fSPaolo Bonzini     int d0, d1, d2;
69049ab747fSPaolo Bonzini     ldebug ("complete command %#x, in_index %d, needed_bytes %d\n",
69149ab747fSPaolo Bonzini             s->cmd, s->in_index, s->needed_bytes);
69249ab747fSPaolo Bonzini 
69349ab747fSPaolo Bonzini     if (s->cmd > 0xaf && s->cmd < 0xd0) {
69449ab747fSPaolo Bonzini         d2 = dsp_get_data (s);
69549ab747fSPaolo Bonzini         d1 = dsp_get_data (s);
69649ab747fSPaolo Bonzini         d0 = dsp_get_data (s);
69749ab747fSPaolo Bonzini 
69849ab747fSPaolo Bonzini         if (s->cmd & 8) {
69949ab747fSPaolo Bonzini             dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
70049ab747fSPaolo Bonzini                    s->cmd, d0, d1, d2);
70149ab747fSPaolo Bonzini         }
70249ab747fSPaolo Bonzini         else {
70349ab747fSPaolo Bonzini             ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
70449ab747fSPaolo Bonzini                     s->cmd, d0, d1, d2);
70549ab747fSPaolo Bonzini             dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
70649ab747fSPaolo Bonzini         }
70749ab747fSPaolo Bonzini     }
70849ab747fSPaolo Bonzini     else {
70949ab747fSPaolo Bonzini         switch (s->cmd) {
71049ab747fSPaolo Bonzini         case 0x04:
71149ab747fSPaolo Bonzini             s->csp_mode = dsp_get_data (s);
71249ab747fSPaolo Bonzini             s->csp_reg83r = 0;
71349ab747fSPaolo Bonzini             s->csp_reg83w = 0;
71449ab747fSPaolo Bonzini             ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode);
71549ab747fSPaolo Bonzini             break;
71649ab747fSPaolo Bonzini 
71749ab747fSPaolo Bonzini         case 0x05:
71849ab747fSPaolo Bonzini             s->csp_param = dsp_get_data (s);
71949ab747fSPaolo Bonzini             s->csp_value = dsp_get_data (s);
72049ab747fSPaolo Bonzini             ldebug ("CSP command 0x05: param=%#x value=%#x\n",
72149ab747fSPaolo Bonzini                     s->csp_param,
72249ab747fSPaolo Bonzini                     s->csp_value);
72349ab747fSPaolo Bonzini             break;
72449ab747fSPaolo Bonzini 
72549ab747fSPaolo Bonzini         case 0x0e:
72649ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
72749ab747fSPaolo Bonzini             d1 = dsp_get_data (s);
72849ab747fSPaolo Bonzini             ldebug ("write CSP register %d <- %#x\n", d1, d0);
72949ab747fSPaolo Bonzini             if (d1 == 0x83) {
73049ab747fSPaolo Bonzini                 ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0);
73149ab747fSPaolo Bonzini                 s->csp_reg83[s->csp_reg83r % 4] = d0;
73249ab747fSPaolo Bonzini                 s->csp_reg83r += 1;
73349ab747fSPaolo Bonzini             }
73449ab747fSPaolo Bonzini             else {
73549ab747fSPaolo Bonzini                 s->csp_regs[d1] = d0;
73649ab747fSPaolo Bonzini             }
73749ab747fSPaolo Bonzini             break;
73849ab747fSPaolo Bonzini 
73949ab747fSPaolo Bonzini         case 0x0f:
74049ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
74149ab747fSPaolo Bonzini             ldebug ("read CSP register %#x -> %#x, mode=%#x\n",
74249ab747fSPaolo Bonzini                     d0, s->csp_regs[d0], s->csp_mode);
74349ab747fSPaolo Bonzini             if (d0 == 0x83) {
74449ab747fSPaolo Bonzini                 ldebug ("0x83[%d] -> %#x\n",
74549ab747fSPaolo Bonzini                         s->csp_reg83w,
74649ab747fSPaolo Bonzini                         s->csp_reg83[s->csp_reg83w % 4]);
74749ab747fSPaolo Bonzini                 dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
74849ab747fSPaolo Bonzini                 s->csp_reg83w += 1;
74949ab747fSPaolo Bonzini             }
75049ab747fSPaolo Bonzini             else {
75149ab747fSPaolo Bonzini                 dsp_out_data (s, s->csp_regs[d0]);
75249ab747fSPaolo Bonzini             }
75349ab747fSPaolo Bonzini             break;
75449ab747fSPaolo Bonzini 
75549ab747fSPaolo Bonzini         case 0x10:
75649ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
75749ab747fSPaolo Bonzini             dolog ("cmd 0x10 d0=%#x\n", d0);
75849ab747fSPaolo Bonzini             break;
75949ab747fSPaolo Bonzini 
76049ab747fSPaolo Bonzini         case 0x14:
76149ab747fSPaolo Bonzini             dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
76249ab747fSPaolo Bonzini             break;
76349ab747fSPaolo Bonzini 
76449ab747fSPaolo Bonzini         case 0x40:
76549ab747fSPaolo Bonzini             s->time_const = dsp_get_data (s);
76649ab747fSPaolo Bonzini             ldebug ("set time const %d\n", s->time_const);
76749ab747fSPaolo Bonzini             break;
76849ab747fSPaolo Bonzini 
76949ab747fSPaolo Bonzini         case 0x41:
770edd7541bSPaolo Bonzini         case 0x42:
771edd7541bSPaolo Bonzini             /*
772edd7541bSPaolo Bonzini              * 0x41 is documented as setting the output sample rate,
773edd7541bSPaolo Bonzini              * and 0x42 the input sample rate, but in fact SB16 hardware
774edd7541bSPaolo Bonzini              * seems to have only a single sample rate under the hood,
775edd7541bSPaolo Bonzini              * and FT2 sets output freq with this (go figure).  Compare:
776edd7541bSPaolo Bonzini              * http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#SamplingRate
777edd7541bSPaolo Bonzini              */
77860e543f5SQiang Liu             s->freq = restrict_sampling_rate(dsp_get_hilo(s));
77949ab747fSPaolo Bonzini             ldebug ("set freq %d\n", s->freq);
78049ab747fSPaolo Bonzini             break;
78149ab747fSPaolo Bonzini 
78249ab747fSPaolo Bonzini         case 0x48:
78349ab747fSPaolo Bonzini             s->block_size = dsp_get_lohi (s) + 1;
78449ab747fSPaolo Bonzini             ldebug ("set dma block len %d\n", s->block_size);
78549ab747fSPaolo Bonzini             break;
78649ab747fSPaolo Bonzini 
78749ab747fSPaolo Bonzini         case 0x74:
78849ab747fSPaolo Bonzini         case 0x75:
78949ab747fSPaolo Bonzini         case 0x76:
79049ab747fSPaolo Bonzini         case 0x77:
79149ab747fSPaolo Bonzini             /* ADPCM stuff, ignore */
79249ab747fSPaolo Bonzini             break;
79349ab747fSPaolo Bonzini 
79449ab747fSPaolo Bonzini         case 0x80:
79549ab747fSPaolo Bonzini             {
79649ab747fSPaolo Bonzini                 int freq, samples, bytes;
79749ab747fSPaolo Bonzini                 int64_t ticks;
79849ab747fSPaolo Bonzini 
79949ab747fSPaolo Bonzini                 freq = s->freq > 0 ? s->freq : 11025;
80049ab747fSPaolo Bonzini                 samples = dsp_get_lohi (s) + 1;
80149ab747fSPaolo Bonzini                 bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
80273bcb24dSRutuja Shah                 ticks = muldiv64(bytes, NANOSECONDS_PER_SECOND, freq);
80373bcb24dSRutuja Shah                 if (ticks < NANOSECONDS_PER_SECOND / 1024) {
80449ab747fSPaolo Bonzini                     qemu_irq_raise (s->pic);
80549ab747fSPaolo Bonzini                 }
80649ab747fSPaolo Bonzini                 else {
80749ab747fSPaolo Bonzini                     if (s->aux_ts) {
808bc72ad67SAlex Bligh                         timer_mod (
80949ab747fSPaolo Bonzini                             s->aux_ts,
810bc72ad67SAlex Bligh                             qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks
81149ab747fSPaolo Bonzini                             );
81249ab747fSPaolo Bonzini                     }
81349ab747fSPaolo Bonzini                 }
81449ab747fSPaolo Bonzini                 ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks);
81549ab747fSPaolo Bonzini             }
81649ab747fSPaolo Bonzini             break;
81749ab747fSPaolo Bonzini 
81849ab747fSPaolo Bonzini         case 0xe0:
81949ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
82049ab747fSPaolo Bonzini             s->out_data_len = 0;
82149ab747fSPaolo Bonzini             ldebug ("E0 data = %#x\n", d0);
82249ab747fSPaolo Bonzini             dsp_out_data (s, ~d0);
82349ab747fSPaolo Bonzini             break;
82449ab747fSPaolo Bonzini 
82549ab747fSPaolo Bonzini         case 0xe2:
82649ab747fSPaolo Bonzini #ifdef DEBUG
82749ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
82849ab747fSPaolo Bonzini             dolog ("E2 = %#x\n", d0);
82949ab747fSPaolo Bonzini #endif
83049ab747fSPaolo Bonzini             break;
83149ab747fSPaolo Bonzini 
83249ab747fSPaolo Bonzini         case 0xe4:
83349ab747fSPaolo Bonzini             s->test_reg = dsp_get_data (s);
83449ab747fSPaolo Bonzini             break;
83549ab747fSPaolo Bonzini 
83649ab747fSPaolo Bonzini         case 0xf9:
83749ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
83849ab747fSPaolo Bonzini             ldebug ("command 0xf9 with %#x\n", d0);
83949ab747fSPaolo Bonzini             switch (d0) {
84049ab747fSPaolo Bonzini             case 0x0e:
84149ab747fSPaolo Bonzini                 dsp_out_data (s, 0xff);
84249ab747fSPaolo Bonzini                 break;
84349ab747fSPaolo Bonzini 
84449ab747fSPaolo Bonzini             case 0x0f:
84549ab747fSPaolo Bonzini                 dsp_out_data (s, 0x07);
84649ab747fSPaolo Bonzini                 break;
84749ab747fSPaolo Bonzini 
84849ab747fSPaolo Bonzini             case 0x37:
84949ab747fSPaolo Bonzini                 dsp_out_data (s, 0x38);
85049ab747fSPaolo Bonzini                 break;
85149ab747fSPaolo Bonzini 
85249ab747fSPaolo Bonzini             default:
85349ab747fSPaolo Bonzini                 dsp_out_data (s, 0x00);
85449ab747fSPaolo Bonzini                 break;
85549ab747fSPaolo Bonzini             }
85649ab747fSPaolo Bonzini             break;
85749ab747fSPaolo Bonzini 
85849ab747fSPaolo Bonzini         default:
8598ec660b8SJohn Arbuckle             qemu_log_mask(LOG_UNIMP, "complete: unrecognized command %#x\n",
8608ec660b8SJohn Arbuckle                           s->cmd);
86149ab747fSPaolo Bonzini             return;
86249ab747fSPaolo Bonzini         }
86349ab747fSPaolo Bonzini     }
86449ab747fSPaolo Bonzini 
86549ab747fSPaolo Bonzini     ldebug ("\n");
86649ab747fSPaolo Bonzini     s->cmd = -1;
86749ab747fSPaolo Bonzini }
86849ab747fSPaolo Bonzini 
legacy_reset(SB16State * s)86949ab747fSPaolo Bonzini static void legacy_reset (SB16State *s)
87049ab747fSPaolo Bonzini {
87149ab747fSPaolo Bonzini     struct audsettings as;
87249ab747fSPaolo Bonzini 
87349ab747fSPaolo Bonzini     s->freq = 11025;
87449ab747fSPaolo Bonzini     s->fmt_signed = 0;
87549ab747fSPaolo Bonzini     s->fmt_bits = 8;
87649ab747fSPaolo Bonzini     s->fmt_stereo = 0;
87749ab747fSPaolo Bonzini 
87849ab747fSPaolo Bonzini     as.freq = s->freq;
87949ab747fSPaolo Bonzini     as.nchannels = 1;
88085bc5852SKővágó, Zoltán     as.fmt = AUDIO_FORMAT_U8;
88149ab747fSPaolo Bonzini     as.endianness = 0;
88249ab747fSPaolo Bonzini 
88349ab747fSPaolo Bonzini     s->voice = AUD_open_out (
88449ab747fSPaolo Bonzini         &s->card,
88549ab747fSPaolo Bonzini         s->voice,
88649ab747fSPaolo Bonzini         "sb16",
88749ab747fSPaolo Bonzini         s,
88849ab747fSPaolo Bonzini         SB_audio_callback,
88949ab747fSPaolo Bonzini         &as
89049ab747fSPaolo Bonzini         );
89149ab747fSPaolo Bonzini 
89249ab747fSPaolo Bonzini     /* Not sure about that... */
89349ab747fSPaolo Bonzini     /* AUD_set_active_out (s->voice, 1); */
89449ab747fSPaolo Bonzini }
89549ab747fSPaolo Bonzini 
reset(SB16State * s)89649ab747fSPaolo Bonzini static void reset (SB16State *s)
89749ab747fSPaolo Bonzini {
89849ab747fSPaolo Bonzini     qemu_irq_lower (s->pic);
89949ab747fSPaolo Bonzini     if (s->dma_auto) {
90049ab747fSPaolo Bonzini         qemu_irq_raise (s->pic);
90149ab747fSPaolo Bonzini         qemu_irq_lower (s->pic);
90249ab747fSPaolo Bonzini     }
90349ab747fSPaolo Bonzini 
90449ab747fSPaolo Bonzini     s->mixer_regs[0x82] = 0;
90549ab747fSPaolo Bonzini     s->dma_auto = 0;
90649ab747fSPaolo Bonzini     s->in_index = 0;
90749ab747fSPaolo Bonzini     s->out_data_len = 0;
90849ab747fSPaolo Bonzini     s->left_till_irq = 0;
90949ab747fSPaolo Bonzini     s->needed_bytes = 0;
91049ab747fSPaolo Bonzini     s->block_size = -1;
91149ab747fSPaolo Bonzini     s->nzero = 0;
91249ab747fSPaolo Bonzini     s->highspeed = 0;
91349ab747fSPaolo Bonzini     s->v2x6 = 0;
91449ab747fSPaolo Bonzini     s->cmd = -1;
91549ab747fSPaolo Bonzini 
91649ab747fSPaolo Bonzini     dsp_out_data (s, 0xaa);
91749ab747fSPaolo Bonzini     speaker (s, 0);
91849ab747fSPaolo Bonzini     control (s, 0);
91949ab747fSPaolo Bonzini     legacy_reset (s);
92049ab747fSPaolo Bonzini }
92149ab747fSPaolo Bonzini 
dsp_write(void * opaque,uint32_t nport,uint32_t val)9228307c294SNutan Shinde static void dsp_write(void *opaque, uint32_t nport, uint32_t val)
92349ab747fSPaolo Bonzini {
92449ab747fSPaolo Bonzini     SB16State *s = opaque;
92549ab747fSPaolo Bonzini     int iport;
92649ab747fSPaolo Bonzini 
92749ab747fSPaolo Bonzini     iport = nport - s->port;
92849ab747fSPaolo Bonzini 
92949ab747fSPaolo Bonzini     ldebug ("write %#x <- %#x\n", nport, val);
93049ab747fSPaolo Bonzini     switch (iport) {
93149ab747fSPaolo Bonzini     case 0x06:
93249ab747fSPaolo Bonzini         switch (val) {
93349ab747fSPaolo Bonzini         case 0x00:
93449ab747fSPaolo Bonzini             if (s->v2x6 == 1) {
93549ab747fSPaolo Bonzini                 reset (s);
93649ab747fSPaolo Bonzini             }
93749ab747fSPaolo Bonzini             s->v2x6 = 0;
93849ab747fSPaolo Bonzini             break;
93949ab747fSPaolo Bonzini 
94049ab747fSPaolo Bonzini         case 0x01:
94149ab747fSPaolo Bonzini         case 0x03:              /* FreeBSD kludge */
94249ab747fSPaolo Bonzini             s->v2x6 = 1;
94349ab747fSPaolo Bonzini             break;
94449ab747fSPaolo Bonzini 
94549ab747fSPaolo Bonzini         case 0xc6:
94649ab747fSPaolo Bonzini             s->v2x6 = 0;        /* Prince of Persia, csp.sys, diagnose.exe */
94749ab747fSPaolo Bonzini             break;
94849ab747fSPaolo Bonzini 
94949ab747fSPaolo Bonzini         case 0xb8:              /* Panic */
95049ab747fSPaolo Bonzini             reset (s);
95149ab747fSPaolo Bonzini             break;
95249ab747fSPaolo Bonzini 
95349ab747fSPaolo Bonzini         case 0x39:
95449ab747fSPaolo Bonzini             dsp_out_data (s, 0x38);
95549ab747fSPaolo Bonzini             reset (s);
95649ab747fSPaolo Bonzini             s->v2x6 = 0x39;
95749ab747fSPaolo Bonzini             break;
95849ab747fSPaolo Bonzini 
95949ab747fSPaolo Bonzini         default:
96049ab747fSPaolo Bonzini             s->v2x6 = val;
96149ab747fSPaolo Bonzini             break;
96249ab747fSPaolo Bonzini         }
96349ab747fSPaolo Bonzini         break;
96449ab747fSPaolo Bonzini 
96549ab747fSPaolo Bonzini     case 0x0c:                  /* write data or command | write status */
96649ab747fSPaolo Bonzini /*         if (s->highspeed) */
96749ab747fSPaolo Bonzini /*             break; */
96849ab747fSPaolo Bonzini 
9692ab5bf67SGonglei         if (s->needed_bytes == 0) {
97049ab747fSPaolo Bonzini             command (s, val);
97149ab747fSPaolo Bonzini #if 0
97249ab747fSPaolo Bonzini             if (0 == s->needed_bytes) {
97349ab747fSPaolo Bonzini                 log_dsp (s);
97449ab747fSPaolo Bonzini             }
97549ab747fSPaolo Bonzini #endif
97649ab747fSPaolo Bonzini         }
97749ab747fSPaolo Bonzini         else {
97849ab747fSPaolo Bonzini             if (s->in_index == sizeof (s->in2_data)) {
97949ab747fSPaolo Bonzini                 dolog ("in data overrun\n");
98049ab747fSPaolo Bonzini             }
98149ab747fSPaolo Bonzini             else {
98249ab747fSPaolo Bonzini                 s->in2_data[s->in_index++] = val;
98349ab747fSPaolo Bonzini                 if (s->in_index == s->needed_bytes) {
98449ab747fSPaolo Bonzini                     s->needed_bytes = 0;
98549ab747fSPaolo Bonzini                     complete (s);
98649ab747fSPaolo Bonzini #if 0
98749ab747fSPaolo Bonzini                     log_dsp (s);
98849ab747fSPaolo Bonzini #endif
98949ab747fSPaolo Bonzini                 }
99049ab747fSPaolo Bonzini             }
99149ab747fSPaolo Bonzini         }
99249ab747fSPaolo Bonzini         break;
99349ab747fSPaolo Bonzini 
99449ab747fSPaolo Bonzini     default:
99549ab747fSPaolo Bonzini         ldebug ("(nport=%#x, val=%#x)\n", nport, val);
99649ab747fSPaolo Bonzini         break;
99749ab747fSPaolo Bonzini     }
99849ab747fSPaolo Bonzini }
99949ab747fSPaolo Bonzini 
dsp_read(void * opaque,uint32_t nport)10008307c294SNutan Shinde static uint32_t dsp_read(void *opaque, uint32_t nport)
100149ab747fSPaolo Bonzini {
100249ab747fSPaolo Bonzini     SB16State *s = opaque;
100349ab747fSPaolo Bonzini     int iport, retval, ack = 0;
100449ab747fSPaolo Bonzini 
100549ab747fSPaolo Bonzini     iport = nport - s->port;
100649ab747fSPaolo Bonzini 
100749ab747fSPaolo Bonzini     switch (iport) {
100849ab747fSPaolo Bonzini     case 0x06:                  /* reset */
100949ab747fSPaolo Bonzini         retval = 0xff;
101049ab747fSPaolo Bonzini         break;
101149ab747fSPaolo Bonzini 
101249ab747fSPaolo Bonzini     case 0x0a:                  /* read data */
101349ab747fSPaolo Bonzini         if (s->out_data_len) {
101449ab747fSPaolo Bonzini             retval = s->out_data[--s->out_data_len];
101549ab747fSPaolo Bonzini             s->last_read_byte = retval;
101649ab747fSPaolo Bonzini         }
101749ab747fSPaolo Bonzini         else {
101849ab747fSPaolo Bonzini             if (s->cmd != -1) {
101949ab747fSPaolo Bonzini                 dolog ("empty output buffer for command %#x\n",
102049ab747fSPaolo Bonzini                        s->cmd);
102149ab747fSPaolo Bonzini             }
102249ab747fSPaolo Bonzini             retval = s->last_read_byte;
102349ab747fSPaolo Bonzini             /* goto error; */
102449ab747fSPaolo Bonzini         }
102549ab747fSPaolo Bonzini         break;
102649ab747fSPaolo Bonzini 
102749ab747fSPaolo Bonzini     case 0x0c:                  /* 0 can write */
102849ab747fSPaolo Bonzini         retval = s->can_write ? 0 : 0x80;
102949ab747fSPaolo Bonzini         break;
103049ab747fSPaolo Bonzini 
103149ab747fSPaolo Bonzini     case 0x0d:                  /* timer interrupt clear */
103249ab747fSPaolo Bonzini         /* dolog ("timer interrupt clear\n"); */
103349ab747fSPaolo Bonzini         retval = 0;
103449ab747fSPaolo Bonzini         break;
103549ab747fSPaolo Bonzini 
103649ab747fSPaolo Bonzini     case 0x0e:                  /* data available status | irq 8 ack */
103749ab747fSPaolo Bonzini         retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
103849ab747fSPaolo Bonzini         if (s->mixer_regs[0x82] & 1) {
103949ab747fSPaolo Bonzini             ack = 1;
10409939375cSPaolo Bonzini             s->mixer_regs[0x82] &= ~1;
104149ab747fSPaolo Bonzini             qemu_irq_lower (s->pic);
104249ab747fSPaolo Bonzini         }
104349ab747fSPaolo Bonzini         break;
104449ab747fSPaolo Bonzini 
104549ab747fSPaolo Bonzini     case 0x0f:                  /* irq 16 ack */
104649ab747fSPaolo Bonzini         retval = 0xff;
104749ab747fSPaolo Bonzini         if (s->mixer_regs[0x82] & 2) {
104849ab747fSPaolo Bonzini             ack = 1;
10499939375cSPaolo Bonzini             s->mixer_regs[0x82] &= ~2;
105049ab747fSPaolo Bonzini             qemu_irq_lower (s->pic);
105149ab747fSPaolo Bonzini         }
105249ab747fSPaolo Bonzini         break;
105349ab747fSPaolo Bonzini 
105449ab747fSPaolo Bonzini     default:
105549ab747fSPaolo Bonzini         goto error;
105649ab747fSPaolo Bonzini     }
105749ab747fSPaolo Bonzini 
105849ab747fSPaolo Bonzini     if (!ack) {
105949ab747fSPaolo Bonzini         ldebug ("read %#x -> %#x\n", nport, retval);
106049ab747fSPaolo Bonzini     }
106149ab747fSPaolo Bonzini 
106249ab747fSPaolo Bonzini     return retval;
106349ab747fSPaolo Bonzini 
106449ab747fSPaolo Bonzini  error:
106549ab747fSPaolo Bonzini     dolog ("warning: dsp_read %#x error\n", nport);
106649ab747fSPaolo Bonzini     return 0xff;
106749ab747fSPaolo Bonzini }
106849ab747fSPaolo Bonzini 
reset_mixer(SB16State * s)106949ab747fSPaolo Bonzini static void reset_mixer (SB16State *s)
107049ab747fSPaolo Bonzini {
107149ab747fSPaolo Bonzini     int i;
107249ab747fSPaolo Bonzini 
107349ab747fSPaolo Bonzini     memset (s->mixer_regs, 0xff, 0x7f);
107449ab747fSPaolo Bonzini     memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
107549ab747fSPaolo Bonzini 
107649ab747fSPaolo Bonzini     s->mixer_regs[0x02] = 4;    /* master volume 3bits */
107749ab747fSPaolo Bonzini     s->mixer_regs[0x06] = 4;    /* MIDI volume 3bits */
107849ab747fSPaolo Bonzini     s->mixer_regs[0x08] = 0;    /* CD volume 3bits */
107949ab747fSPaolo Bonzini     s->mixer_regs[0x0a] = 0;    /* voice volume 2bits */
108049ab747fSPaolo Bonzini 
108149ab747fSPaolo Bonzini     /* d5=input filt, d3=lowpass filt, d1,d2=input source */
108249ab747fSPaolo Bonzini     s->mixer_regs[0x0c] = 0;
108349ab747fSPaolo Bonzini 
108449ab747fSPaolo Bonzini     /* d5=output filt, d1=stereo switch */
108549ab747fSPaolo Bonzini     s->mixer_regs[0x0e] = 0;
108649ab747fSPaolo Bonzini 
108749ab747fSPaolo Bonzini     /* voice volume L d5,d7, R d1,d3 */
108849ab747fSPaolo Bonzini     s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
108949ab747fSPaolo Bonzini     /* master ... */
109049ab747fSPaolo Bonzini     s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
109149ab747fSPaolo Bonzini     /* MIDI ... */
109249ab747fSPaolo Bonzini     s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
109349ab747fSPaolo Bonzini 
109449ab747fSPaolo Bonzini     for (i = 0x30; i < 0x48; i++) {
109549ab747fSPaolo Bonzini         s->mixer_regs[i] = 0x20;
109649ab747fSPaolo Bonzini     }
109749ab747fSPaolo Bonzini }
109849ab747fSPaolo Bonzini 
mixer_write_indexb(void * opaque,uint32_t nport,uint32_t val)10998307c294SNutan Shinde static void mixer_write_indexb(void *opaque, uint32_t nport, uint32_t val)
110049ab747fSPaolo Bonzini {
110149ab747fSPaolo Bonzini     SB16State *s = opaque;
110249ab747fSPaolo Bonzini     (void) nport;
110349ab747fSPaolo Bonzini     s->mixer_nreg = val;
110449ab747fSPaolo Bonzini }
110549ab747fSPaolo Bonzini 
mixer_write_datab(void * opaque,uint32_t nport,uint32_t val)11068307c294SNutan Shinde static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val)
110749ab747fSPaolo Bonzini {
110849ab747fSPaolo Bonzini     SB16State *s = opaque;
110949ab747fSPaolo Bonzini 
111049ab747fSPaolo Bonzini     (void) nport;
111149ab747fSPaolo Bonzini     ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
111249ab747fSPaolo Bonzini 
111349ab747fSPaolo Bonzini     switch (s->mixer_nreg) {
111449ab747fSPaolo Bonzini     case 0x00:
111549ab747fSPaolo Bonzini         reset_mixer (s);
111649ab747fSPaolo Bonzini         break;
111749ab747fSPaolo Bonzini 
111849ab747fSPaolo Bonzini     case 0x80:
111949ab747fSPaolo Bonzini         {
112049ab747fSPaolo Bonzini             int irq = irq_of_magic (val);
112149ab747fSPaolo Bonzini             ldebug ("setting irq to %d (val=%#x)\n", irq, val);
112249ab747fSPaolo Bonzini             if (irq > 0) {
112349ab747fSPaolo Bonzini                 s->irq = irq;
112449ab747fSPaolo Bonzini             }
112549ab747fSPaolo Bonzini         }
112649ab747fSPaolo Bonzini         break;
112749ab747fSPaolo Bonzini 
112849ab747fSPaolo Bonzini     case 0x81:
112949ab747fSPaolo Bonzini         {
113049ab747fSPaolo Bonzini             int dma, hdma;
113149ab747fSPaolo Bonzini 
113249ab747fSPaolo Bonzini             dma = ctz32 (val & 0xf);
113349ab747fSPaolo Bonzini             hdma = ctz32 (val & 0xf0);
113449ab747fSPaolo Bonzini             if (dma != s->dma || hdma != s->hdma) {
11358ec660b8SJohn Arbuckle                 qemu_log_mask(LOG_GUEST_ERROR, "attempt to change DMA 8bit"
11368ec660b8SJohn Arbuckle                               " %d(%d), 16bit %d(%d) (val=%#x)\n", dma, s->dma,
11378ec660b8SJohn Arbuckle                               hdma, s->hdma, val);
113849ab747fSPaolo Bonzini             }
113949ab747fSPaolo Bonzini #if 0
114049ab747fSPaolo Bonzini             s->dma = dma;
114149ab747fSPaolo Bonzini             s->hdma = hdma;
114249ab747fSPaolo Bonzini #endif
114349ab747fSPaolo Bonzini         }
114449ab747fSPaolo Bonzini         break;
114549ab747fSPaolo Bonzini 
114649ab747fSPaolo Bonzini     case 0x82:
11478ec660b8SJohn Arbuckle         qemu_log_mask(LOG_GUEST_ERROR, "attempt to write into IRQ status"
11488ec660b8SJohn Arbuckle                       " register (val=%#x)\n", val);
114949ab747fSPaolo Bonzini         return;
115049ab747fSPaolo Bonzini 
115149ab747fSPaolo Bonzini     default:
115249ab747fSPaolo Bonzini         if (s->mixer_nreg >= 0x80) {
115349ab747fSPaolo Bonzini             ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
115449ab747fSPaolo Bonzini         }
115549ab747fSPaolo Bonzini         break;
115649ab747fSPaolo Bonzini     }
115749ab747fSPaolo Bonzini 
115849ab747fSPaolo Bonzini     s->mixer_regs[s->mixer_nreg] = val;
115949ab747fSPaolo Bonzini }
116049ab747fSPaolo Bonzini 
mixer_read(void * opaque,uint32_t nport)11618307c294SNutan Shinde static uint32_t mixer_read(void *opaque, uint32_t nport)
116249ab747fSPaolo Bonzini {
116349ab747fSPaolo Bonzini     SB16State *s = opaque;
116449ab747fSPaolo Bonzini 
116549ab747fSPaolo Bonzini     (void) nport;
116649ab747fSPaolo Bonzini #ifndef DEBUG_SB16_MOST
116749ab747fSPaolo Bonzini     if (s->mixer_nreg != 0x82) {
116849ab747fSPaolo Bonzini         ldebug ("mixer_read[%#x] -> %#x\n",
116949ab747fSPaolo Bonzini                 s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
117049ab747fSPaolo Bonzini     }
117149ab747fSPaolo Bonzini #else
117249ab747fSPaolo Bonzini     ldebug ("mixer_read[%#x] -> %#x\n",
117349ab747fSPaolo Bonzini             s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
117449ab747fSPaolo Bonzini #endif
117549ab747fSPaolo Bonzini     return s->mixer_regs[s->mixer_nreg];
117649ab747fSPaolo Bonzini }
117749ab747fSPaolo Bonzini 
write_audio(SB16State * s,int nchan,int dma_pos,int dma_len,int len)117849ab747fSPaolo Bonzini static int write_audio (SB16State *s, int nchan, int dma_pos,
117949ab747fSPaolo Bonzini                         int dma_len, int len)
118049ab747fSPaolo Bonzini {
1181f203c16eSHervé Poussineau     IsaDma *isa_dma = nchan == s->dma ? s->isa_dma : s->isa_hdma;
1182f203c16eSHervé Poussineau     IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma);
118349ab747fSPaolo Bonzini     int temp, net;
118449ab747fSPaolo Bonzini     uint8_t tmpbuf[4096];
118549ab747fSPaolo Bonzini 
118649ab747fSPaolo Bonzini     temp = len;
118749ab747fSPaolo Bonzini     net = 0;
118849ab747fSPaolo Bonzini 
118949ab747fSPaolo Bonzini     while (temp) {
119049ab747fSPaolo Bonzini         int left = dma_len - dma_pos;
119149ab747fSPaolo Bonzini         int copied;
119249ab747fSPaolo Bonzini         size_t to_copy;
119349ab747fSPaolo Bonzini 
119458935915SKővágó, Zoltán         to_copy = MIN (temp, left);
119549ab747fSPaolo Bonzini         if (to_copy > sizeof (tmpbuf)) {
119649ab747fSPaolo Bonzini             to_copy = sizeof (tmpbuf);
119749ab747fSPaolo Bonzini         }
119849ab747fSPaolo Bonzini 
1199f203c16eSHervé Poussineau         copied = k->read_memory(isa_dma, nchan, tmpbuf, dma_pos, to_copy);
120049ab747fSPaolo Bonzini         copied = AUD_write (s->voice, tmpbuf, copied);
120149ab747fSPaolo Bonzini 
120249ab747fSPaolo Bonzini         temp -= copied;
120349ab747fSPaolo Bonzini         dma_pos = (dma_pos + copied) % dma_len;
120449ab747fSPaolo Bonzini         net += copied;
120549ab747fSPaolo Bonzini 
120649ab747fSPaolo Bonzini         if (!copied) {
120749ab747fSPaolo Bonzini             break;
120849ab747fSPaolo Bonzini         }
120949ab747fSPaolo Bonzini     }
121049ab747fSPaolo Bonzini 
121149ab747fSPaolo Bonzini     return net;
121249ab747fSPaolo Bonzini }
121349ab747fSPaolo Bonzini 
SB_read_DMA(void * opaque,int nchan,int dma_pos,int dma_len)121449ab747fSPaolo Bonzini static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
121549ab747fSPaolo Bonzini {
121649ab747fSPaolo Bonzini     SB16State *s = opaque;
121749ab747fSPaolo Bonzini     int till, copy, written, free;
121849ab747fSPaolo Bonzini 
121949ab747fSPaolo Bonzini     if (s->block_size <= 0) {
12208ec660b8SJohn Arbuckle         qemu_log_mask(LOG_GUEST_ERROR, "invalid block size=%d nchan=%d"
12218ec660b8SJohn Arbuckle                       " dma_pos=%d dma_len=%d\n", s->block_size, nchan,
12228ec660b8SJohn Arbuckle                       dma_pos, dma_len);
122349ab747fSPaolo Bonzini         return dma_pos;
122449ab747fSPaolo Bonzini     }
122549ab747fSPaolo Bonzini 
122649ab747fSPaolo Bonzini     if (s->left_till_irq < 0) {
122749ab747fSPaolo Bonzini         s->left_till_irq = s->block_size;
122849ab747fSPaolo Bonzini     }
122949ab747fSPaolo Bonzini 
123049ab747fSPaolo Bonzini     if (s->voice) {
123149ab747fSPaolo Bonzini         free = s->audio_free & ~s->align;
123249ab747fSPaolo Bonzini         if ((free <= 0) || !dma_len) {
123349ab747fSPaolo Bonzini             return dma_pos;
123449ab747fSPaolo Bonzini         }
123549ab747fSPaolo Bonzini     }
123649ab747fSPaolo Bonzini     else {
123749ab747fSPaolo Bonzini         free = dma_len;
123849ab747fSPaolo Bonzini     }
123949ab747fSPaolo Bonzini 
124049ab747fSPaolo Bonzini     copy = free;
124149ab747fSPaolo Bonzini     till = s->left_till_irq;
124249ab747fSPaolo Bonzini 
124349ab747fSPaolo Bonzini #ifdef DEBUG_SB16_MOST
124449ab747fSPaolo Bonzini     dolog ("pos:%06d %d till:%d len:%d\n",
124549ab747fSPaolo Bonzini            dma_pos, free, till, dma_len);
124649ab747fSPaolo Bonzini #endif
124749ab747fSPaolo Bonzini 
124849ab747fSPaolo Bonzini     if (till <= copy) {
12492ab5bf67SGonglei         if (s->dma_auto == 0) {
125049ab747fSPaolo Bonzini             copy = till;
125149ab747fSPaolo Bonzini         }
125249ab747fSPaolo Bonzini     }
125349ab747fSPaolo Bonzini 
125449ab747fSPaolo Bonzini     written = write_audio (s, nchan, dma_pos, dma_len, copy);
125549ab747fSPaolo Bonzini     dma_pos = (dma_pos + written) % dma_len;
125649ab747fSPaolo Bonzini     s->left_till_irq -= written;
125749ab747fSPaolo Bonzini 
125849ab747fSPaolo Bonzini     if (s->left_till_irq <= 0) {
125949ab747fSPaolo Bonzini         s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
126049ab747fSPaolo Bonzini         qemu_irq_raise (s->pic);
12612ab5bf67SGonglei         if (s->dma_auto == 0) {
126249ab747fSPaolo Bonzini             control (s, 0);
126349ab747fSPaolo Bonzini             speaker (s, 0);
126449ab747fSPaolo Bonzini         }
126549ab747fSPaolo Bonzini     }
126649ab747fSPaolo Bonzini 
126749ab747fSPaolo Bonzini #ifdef DEBUG_SB16_MOST
126849ab747fSPaolo Bonzini     ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
126949ab747fSPaolo Bonzini             dma_pos, free, dma_len, s->left_till_irq, copy, written,
127049ab747fSPaolo Bonzini             s->block_size);
127149ab747fSPaolo Bonzini #endif
127249ab747fSPaolo Bonzini 
127349ab747fSPaolo Bonzini     while (s->left_till_irq <= 0) {
127449ab747fSPaolo Bonzini         s->left_till_irq = s->block_size + s->left_till_irq;
127549ab747fSPaolo Bonzini     }
127649ab747fSPaolo Bonzini 
127749ab747fSPaolo Bonzini     return dma_pos;
127849ab747fSPaolo Bonzini }
127949ab747fSPaolo Bonzini 
SB_audio_callback(void * opaque,int free)128049ab747fSPaolo Bonzini static void SB_audio_callback (void *opaque, int free)
128149ab747fSPaolo Bonzini {
128249ab747fSPaolo Bonzini     SB16State *s = opaque;
128349ab747fSPaolo Bonzini     s->audio_free = free;
128449ab747fSPaolo Bonzini }
128549ab747fSPaolo Bonzini 
sb16_post_load(void * opaque,int version_id)128649ab747fSPaolo Bonzini static int sb16_post_load (void *opaque, int version_id)
128749ab747fSPaolo Bonzini {
128849ab747fSPaolo Bonzini     SB16State *s = opaque;
128949ab747fSPaolo Bonzini 
129049ab747fSPaolo Bonzini     if (s->voice) {
129149ab747fSPaolo Bonzini         AUD_close_out (&s->card, s->voice);
129249ab747fSPaolo Bonzini         s->voice = NULL;
129349ab747fSPaolo Bonzini     }
129449ab747fSPaolo Bonzini 
129549ab747fSPaolo Bonzini     if (s->dma_running) {
129649ab747fSPaolo Bonzini         if (s->freq) {
129749ab747fSPaolo Bonzini             struct audsettings as;
129849ab747fSPaolo Bonzini 
129949ab747fSPaolo Bonzini             s->audio_free = 0;
130049ab747fSPaolo Bonzini 
130149ab747fSPaolo Bonzini             as.freq = s->freq;
130249ab747fSPaolo Bonzini             as.nchannels = 1 << s->fmt_stereo;
130349ab747fSPaolo Bonzini             as.fmt = s->fmt;
130449ab747fSPaolo Bonzini             as.endianness = 0;
130549ab747fSPaolo Bonzini 
130649ab747fSPaolo Bonzini             s->voice = AUD_open_out (
130749ab747fSPaolo Bonzini                 &s->card,
130849ab747fSPaolo Bonzini                 s->voice,
130949ab747fSPaolo Bonzini                 "sb16",
131049ab747fSPaolo Bonzini                 s,
131149ab747fSPaolo Bonzini                 SB_audio_callback,
131249ab747fSPaolo Bonzini                 &as
131349ab747fSPaolo Bonzini                 );
131449ab747fSPaolo Bonzini         }
131549ab747fSPaolo Bonzini 
131649ab747fSPaolo Bonzini         control (s, 1);
131749ab747fSPaolo Bonzini         speaker (s, s->speaker);
131849ab747fSPaolo Bonzini     }
131949ab747fSPaolo Bonzini     return 0;
132049ab747fSPaolo Bonzini }
132149ab747fSPaolo Bonzini 
132249ab747fSPaolo Bonzini static const VMStateDescription vmstate_sb16 = {
132349ab747fSPaolo Bonzini     .name = "sb16",
132449ab747fSPaolo Bonzini     .version_id = 1,
132549ab747fSPaolo Bonzini     .minimum_version_id = 1,
132649ab747fSPaolo Bonzini     .post_load = sb16_post_load,
1327856a6fe4SRichard Henderson     .fields = (const VMStateField[]) {
1328*fa293f81SPhilippe Mathieu-Daudé         VMSTATE_UNUSED(  4 /* irq */
1329*fa293f81SPhilippe Mathieu-Daudé                        + 4 /* dma */
1330*fa293f81SPhilippe Mathieu-Daudé                        + 4 /* hdma */
1331*fa293f81SPhilippe Mathieu-Daudé                        + 4 /* port */
1332*fa293f81SPhilippe Mathieu-Daudé                        + 4 /* ver */),
133349ab747fSPaolo Bonzini         VMSTATE_INT32 (in_index, SB16State),
133449ab747fSPaolo Bonzini         VMSTATE_INT32 (out_data_len, SB16State),
133549ab747fSPaolo Bonzini         VMSTATE_INT32 (fmt_stereo, SB16State),
133649ab747fSPaolo Bonzini         VMSTATE_INT32 (fmt_signed, SB16State),
133749ab747fSPaolo Bonzini         VMSTATE_INT32 (fmt_bits, SB16State),
133849ab747fSPaolo Bonzini         VMSTATE_UINT32 (fmt, SB16State),
133949ab747fSPaolo Bonzini         VMSTATE_INT32 (dma_auto, SB16State),
134049ab747fSPaolo Bonzini         VMSTATE_INT32 (block_size, SB16State),
134149ab747fSPaolo Bonzini         VMSTATE_INT32 (fifo, SB16State),
134249ab747fSPaolo Bonzini         VMSTATE_INT32 (freq, SB16State),
134349ab747fSPaolo Bonzini         VMSTATE_INT32 (time_const, SB16State),
134449ab747fSPaolo Bonzini         VMSTATE_INT32 (speaker, SB16State),
134549ab747fSPaolo Bonzini         VMSTATE_INT32 (needed_bytes, SB16State),
134649ab747fSPaolo Bonzini         VMSTATE_INT32 (cmd, SB16State),
134749ab747fSPaolo Bonzini         VMSTATE_INT32 (use_hdma, SB16State),
134849ab747fSPaolo Bonzini         VMSTATE_INT32 (highspeed, SB16State),
134949ab747fSPaolo Bonzini         VMSTATE_INT32 (can_write, SB16State),
135049ab747fSPaolo Bonzini         VMSTATE_INT32 (v2x6, SB16State),
135149ab747fSPaolo Bonzini 
135249ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_param, SB16State),
135349ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_value, SB16State),
135449ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_mode, SB16State),
135549ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_param, SB16State),
135649ab747fSPaolo Bonzini         VMSTATE_BUFFER (csp_regs, SB16State),
135749ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_index, SB16State),
135849ab747fSPaolo Bonzini         VMSTATE_BUFFER (csp_reg83, SB16State),
135949ab747fSPaolo Bonzini         VMSTATE_INT32 (csp_reg83r, SB16State),
136049ab747fSPaolo Bonzini         VMSTATE_INT32 (csp_reg83w, SB16State),
136149ab747fSPaolo Bonzini 
136249ab747fSPaolo Bonzini         VMSTATE_BUFFER (in2_data, SB16State),
136349ab747fSPaolo Bonzini         VMSTATE_BUFFER (out_data, SB16State),
136449ab747fSPaolo Bonzini         VMSTATE_UINT8 (test_reg, SB16State),
136549ab747fSPaolo Bonzini         VMSTATE_UINT8 (last_read_byte, SB16State),
136649ab747fSPaolo Bonzini 
136749ab747fSPaolo Bonzini         VMSTATE_INT32 (nzero, SB16State),
136849ab747fSPaolo Bonzini         VMSTATE_INT32 (left_till_irq, SB16State),
136949ab747fSPaolo Bonzini         VMSTATE_INT32 (dma_running, SB16State),
137049ab747fSPaolo Bonzini         VMSTATE_INT32 (bytes_per_second, SB16State),
137149ab747fSPaolo Bonzini         VMSTATE_INT32 (align, SB16State),
137249ab747fSPaolo Bonzini 
137349ab747fSPaolo Bonzini         VMSTATE_INT32 (mixer_nreg, SB16State),
137449ab747fSPaolo Bonzini         VMSTATE_BUFFER (mixer_regs, SB16State),
137549ab747fSPaolo Bonzini 
137649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST ()
137749ab747fSPaolo Bonzini     }
137849ab747fSPaolo Bonzini };
137949ab747fSPaolo Bonzini 
138049ab747fSPaolo Bonzini static const MemoryRegionPortio sb16_ioport_list[] = {
138149ab747fSPaolo Bonzini     {  4, 1, 1, .write = mixer_write_indexb },
138249ab747fSPaolo Bonzini     {  5, 1, 1, .read = mixer_read, .write = mixer_write_datab },
138349ab747fSPaolo Bonzini     {  6, 1, 1, .read = dsp_read, .write = dsp_write },
138449ab747fSPaolo Bonzini     { 10, 1, 1, .read = dsp_read },
138549ab747fSPaolo Bonzini     { 12, 1, 1, .write = dsp_write },
138649ab747fSPaolo Bonzini     { 12, 4, 1, .read = dsp_read },
138749ab747fSPaolo Bonzini     PORTIO_END_OF_LIST (),
138849ab747fSPaolo Bonzini };
138949ab747fSPaolo Bonzini 
139049ab747fSPaolo Bonzini 
sb16_initfn(Object * obj)1391db895a1eSAndreas Färber static void sb16_initfn (Object *obj)
139249ab747fSPaolo Bonzini {
1393db895a1eSAndreas Färber     SB16State *s = SB16 (obj);
139449ab747fSPaolo Bonzini 
139549ab747fSPaolo Bonzini     s->cmd = -1;
1396db895a1eSAndreas Färber }
1397db895a1eSAndreas Färber 
sb16_realizefn(DeviceState * dev,Error ** errp)1398db895a1eSAndreas Färber static void sb16_realizefn (DeviceState *dev, Error **errp)
1399db895a1eSAndreas Färber {
1400db895a1eSAndreas Färber     ISADevice *isadev = ISA_DEVICE (dev);
14018e7db8abSPhilippe Mathieu-Daudé     ISABus *bus = isa_bus_from_device(isadev);
1402db895a1eSAndreas Färber     SB16State *s = SB16 (dev);
1403f203c16eSHervé Poussineau     IsaDmaClass *k;
1404db895a1eSAndreas Färber 
1405cb94ff5fSMartin Kletzander     if (!AUD_register_card ("sb16", &s->card, errp)) {
1406cb94ff5fSMartin Kletzander         return;
1407cb94ff5fSMartin Kletzander     }
1408cb94ff5fSMartin Kletzander 
14098e7db8abSPhilippe Mathieu-Daudé     s->isa_hdma = isa_bus_get_dma(bus, s->hdma);
14108e7db8abSPhilippe Mathieu-Daudé     s->isa_dma = isa_bus_get_dma(bus, s->dma);
1411c9073238SThomas Huth     if (!s->isa_dma || !s->isa_hdma) {
1412c9073238SThomas Huth         error_setg(errp, "ISA controller does not support DMA");
1413c9073238SThomas Huth         return;
1414c9073238SThomas Huth     }
1415c9073238SThomas Huth 
14168e7db8abSPhilippe Mathieu-Daudé     s->pic = isa_bus_get_irq(bus, s->irq);
141749ab747fSPaolo Bonzini 
141849ab747fSPaolo Bonzini     s->mixer_regs[0x80] = magic_of_irq (s->irq);
141949ab747fSPaolo Bonzini     s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
142049ab747fSPaolo Bonzini     s->mixer_regs[0x82] = 2 << 5;
142149ab747fSPaolo Bonzini 
142249ab747fSPaolo Bonzini     s->csp_regs[5] = 1;
142349ab747fSPaolo Bonzini     s->csp_regs[9] = 0xf8;
142449ab747fSPaolo Bonzini 
142549ab747fSPaolo Bonzini     reset_mixer (s);
1426bc72ad67SAlex Bligh     s->aux_ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, aux_timer, s);
142749ab747fSPaolo Bonzini     if (!s->aux_ts) {
14288ec660b8SJohn Arbuckle         error_setg(errp, "warning: Could not create auxiliary timer");
142949ab747fSPaolo Bonzini     }
143049ab747fSPaolo Bonzini 
1431e305a165SMarc-André Lureau     isa_register_portio_list(isadev, &s->portio_list, s->port,
1432e305a165SMarc-André Lureau                              sb16_ioport_list, s, "sb16");
143349ab747fSPaolo Bonzini 
1434f203c16eSHervé Poussineau     k = ISADMA_GET_CLASS(s->isa_hdma);
1435f203c16eSHervé Poussineau     k->register_channel(s->isa_hdma, s->hdma, SB_read_DMA, s);
1436f203c16eSHervé Poussineau 
1437f203c16eSHervé Poussineau     k = ISADMA_GET_CLASS(s->isa_dma);
1438f203c16eSHervé Poussineau     k->register_channel(s->isa_dma, s->dma, SB_read_DMA, s);
1439f203c16eSHervé Poussineau 
144049ab747fSPaolo Bonzini     s->can_write = 1;
144149ab747fSPaolo Bonzini }
144249ab747fSPaolo Bonzini 
144349ab747fSPaolo Bonzini static Property sb16_properties[] = {
144488e47b9aSKővágó, Zoltán     DEFINE_AUDIO_PROPERTIES(SB16State, card),
1445c7bcc85dSPaolo Bonzini     DEFINE_PROP_UINT32 ("version", SB16State, ver,  0x0405), /* 4.5 */
1446c7bcc85dSPaolo Bonzini     DEFINE_PROP_UINT32 ("iobase",  SB16State, port, 0x220),
144749ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("irq",     SB16State, irq,  5),
144849ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("dma",     SB16State, dma,  1),
144949ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("dma16",   SB16State, hdma, 5),
145049ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST (),
145149ab747fSPaolo Bonzini };
145249ab747fSPaolo Bonzini 
sb16_class_initfn(ObjectClass * klass,void * data)145349ab747fSPaolo Bonzini static void sb16_class_initfn (ObjectClass *klass, void *data)
145449ab747fSPaolo Bonzini {
145549ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS (klass);
1456db895a1eSAndreas Färber 
1457db895a1eSAndreas Färber     dc->realize = sb16_realizefn;
1458125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
145949ab747fSPaolo Bonzini     dc->desc = "Creative Sound Blaster 16";
146049ab747fSPaolo Bonzini     dc->vmsd = &vmstate_sb16;
14614f67d30bSMarc-André Lureau     device_class_set_props(dc, sb16_properties);
146249ab747fSPaolo Bonzini }
146349ab747fSPaolo Bonzini 
146449ab747fSPaolo Bonzini static const TypeInfo sb16_info = {
1465399f05a6SAndreas Färber     .name          = TYPE_SB16,
146649ab747fSPaolo Bonzini     .parent        = TYPE_ISA_DEVICE,
146749ab747fSPaolo Bonzini     .instance_size = sizeof (SB16State),
1468db895a1eSAndreas Färber     .instance_init = sb16_initfn,
146949ab747fSPaolo Bonzini     .class_init    = sb16_class_initfn,
147049ab747fSPaolo Bonzini };
147149ab747fSPaolo Bonzini 
sb16_register_types(void)147249ab747fSPaolo Bonzini static void sb16_register_types (void)
147349ab747fSPaolo Bonzini {
147449ab747fSPaolo Bonzini     type_register_static (&sb16_info);
14754b96159eSGerd Hoffmann     deprecated_register_soundhw("sb16", "Creative Sound Blaster 16",
14764b96159eSGerd Hoffmann                                 1, TYPE_SB16);
147749ab747fSPaolo Bonzini }
147849ab747fSPaolo Bonzini 
147949ab747fSPaolo Bonzini type_init (sb16_register_types)
1480