summaryrefslogtreecommitdiffstats
path: root/sys/dev/isa/gus.c
diff options
context:
space:
mode:
authorderaadt <deraadt@openbsd.org>1995-10-18 08:37:01 +0000
committerderaadt <deraadt@openbsd.org>1995-10-18 08:37:01 +0000
commitdf930be708d50e9715f173caa26ffe1b7599b157 (patch)
treeaa317e49e28cb999c9cf3db7f00c20903fe6010a /sys/dev/isa/gus.c
downloadwireguard-openbsd-df930be708d50e9715f173caa26ffe1b7599b157.tar.xz
wireguard-openbsd-df930be708d50e9715f173caa26ffe1b7599b157.zip
initial import of NetBSD tree
Diffstat (limited to 'sys/dev/isa/gus.c')
-rw-r--r--sys/dev/isa/gus.c4532
1 files changed, 4532 insertions, 0 deletions
diff --git a/sys/dev/isa/gus.c b/sys/dev/isa/gus.c
new file mode 100644
index 00000000000..7e846f78275
--- /dev/null
+++ b/sys/dev/isa/gus.c
@@ -0,0 +1,4532 @@
+/* $NetBSD: gus.c,v 1.2 1995/07/24 05:54:52 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Ken Hornstein. All rights reserved.
+ * Copyright (c) 1995 John T. Kohl. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ken Hornstein.
+ * 4. The name of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ *
+ * TODO:
+ * . figure out why mixer activity while sound is playing causes problems
+ * (phantom interrupts?)
+ * . figure out a better deinterleave strategy that avoids sucking up
+ * CPU, memory and cache bandwidth. (Maybe a special encoding?
+ * Maybe use the double-speed sampling/hardware deinterleave trick
+ * from the GUS SDK?) A 486/33 isn't quite fast enough to keep
+ * up with 44.1kHz 16-bit stereo output without some drop-outs.
+ * . use CS4231 for 16-bit sampling, for a-law and mu-law playback.
+ * . actually test full-duplex sampling(recording) and playback.
+ */
+
+/*
+ * Gravis UltraSound driver
+ *
+ * For more detailed information, see the GUS developers' kit
+ * available on the net at:
+ *
+ * ftp://freedom.nmsu.edu/pub/ultrasound/gravis/util/
+ * gusdkXXX.zip (developers' kit--get rev 2.22 or later)
+ * See ultrawrd.doc inside--it's MS Word (ick), but it's the bible
+ *
+ */
+
+/*
+ * The GUS Max has a slightly strange set of connections between the CS4231
+ * and the GF1 and the DMA interconnects. It's set up so that the CS4231 can
+ * be playing while the GF1 is loading patches from the system.
+ *
+ * Here's a recreation of the DMA interconnect diagram:
+ *
+ * GF1
+ * +---------+ digital
+ * | | record ASIC
+ * | |--------------+
+ * | | | +--------+
+ * | | play (dram) | +----+ | |
+ * | |--------------(------|-\ | | +-+ |
+ * +---------+ | | >-|----|---|C|--|------ dma chan 1
+ * | +---|-/ | | +-+ |
+ * | | +----+ | | |
+ * | | +----+ | | |
+ * +---------+ +-+ +--(---|-\ | | | |
+ * | | play |8| | | >-|----|----+---|------ dma chan 2
+ * | ---C----|--------|/|------(---|-/ | | |
+ * | ^ |record |1| | +----+ | |
+ * | | | /----|6|------+ +--------+
+ * | ---+----|--/ +-+
+ * +---------+
+ * CS4231 8-to-16 bit bus conversion, if needed
+ *
+ *
+ * "C" is an optional combiner.
+ *
+ */
+
+#include "gus.h"
+#if NGUS > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/fcntl.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <machine/cpu.h>
+#include <machine/pio.h>
+#include <machine/cpufunc.h>
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+#include <dev/mulaw.h>
+
+#include <dev/isa/isavar.h>
+#include <dev/isa/isadmavar.h>
+#include <i386/isa/icu.h>
+
+#include <dev/ic/ics2101reg.h>
+#include <dev/ic/cs4231reg.h>
+#include <dev/ic/ad1848reg.h>
+#include <dev/isa/ics2101var.h>
+#include <dev/isa/ad1848var.h>
+#include "gusreg.h"
+
+/*
+ * Software state of a single "voice" on the GUS
+ */
+
+struct gus_voice {
+
+ /*
+ * Various control bits
+ */
+
+ unsigned char voccntl; /* State of voice control register */
+ unsigned char volcntl; /* State of volume control register */
+ unsigned char pan_pos; /* Position of volume panning (4 bits) */
+ int rate; /* Sample rate of voice being played back */
+
+ /*
+ * Address of the voice data into the GUS's DRAM. 20 bits each
+ */
+
+ u_long start_addr; /* Starting address of voice data loop area */
+ u_long end_addr; /* Ending address of voice data loop */
+ u_long current_addr; /* Beginning address of voice data
+ (start playing here) */
+
+ /*
+ * linear volume values for the GUS's volume ramp. 0-511 (9 bits).
+ * These values must be translated into the logarithmic values using
+ * gus_log_volumes[]
+ */
+
+ int start_volume; /* Starting position of volume ramp */
+ int current_volume; /* Current position of volume on volume ramp */
+ int end_volume; /* Ending position of volume on volume ramp */
+};
+
+/*
+ * Software state of GUS
+ */
+
+struct gus_softc {
+ struct device sc_dev; /* base device */
+ struct isadev sc_id; /* ISA device */
+ void *sc_ih; /* interrupt vector */
+
+ u_short sc_iobase; /* I/O base address */
+ u_short sc_irq; /* IRQ used */
+ u_short sc_drq; /* DMA channel for play */
+ u_short sc_recdrq; /* DMA channel for recording */
+
+ int sc_flags; /* Various flags about the GUS */
+#define GUS_MIXER_INSTALLED 0x01 /* An ICS mixer is installed */
+#define GUS_LOCKED 0x02 /* GUS is busy doing multi-phase DMA */
+#define GUS_CODEC_INSTALLED 0x04 /* CS4231 installed/MAX */
+#define GUS_PLAYING 0x08 /* GUS is playing a voice */
+#define GUS_DMAOUT_ACTIVE 0x10 /* GUS is busy doing audio DMA */
+#define GUS_DMAIN_ACTIVE 0x20 /* GUS is busy sampling */
+#define GUS_OPEN 0x100 /* GUS is open */
+ int sc_dsize; /* Size of GUS DRAM */
+ int sc_voices; /* Number of active voices */
+ u_char sc_revision; /* Board revision of GUS */
+ u_char sc_mixcontrol; /* Value of GUS_MIX_CONTROL register */
+
+ u_long sc_orate; /* Output sampling rate */
+ u_long sc_irate; /* Input sampling rate */
+
+ int sc_encoding; /* Current data encoding type */
+ int sc_precision; /* # of bits of precision */
+ int sc_channels; /* Number of active channels */
+ int sc_blocksize; /* Current blocksize */
+ int sc_chanblocksize; /* Current blocksize for each in-use
+ channel */
+ short sc_nbufs; /* how many on-GUS bufs per-channel */
+ short sc_bufcnt; /* how many need to be played */
+ void *sc_deintr_buf; /* deinterleave buffer for stereo */
+
+ int sc_ogain; /* Output gain control */
+ u_char sc_out_port; /* Current out port (generic only) */
+ u_char sc_in_port; /* keep track of it when no codec */
+
+ void (*sc_dmaoutintr) __P((void*)); /* DMA completion intr handler */
+ void *sc_outarg; /* argument for sc_dmaoutintr() */
+ u_char *sc_dmaoutaddr; /* for isa_dmadone */
+ u_long sc_gusaddr; /* where did we just put it? */
+ int sc_dmaoutcnt; /* for isa_dmadone */
+
+ void (*sc_dmainintr) __P((void*)); /* DMA completion intr handler */
+ void *sc_inarg; /* argument for sc_dmaoutintr() */
+ u_char *sc_dmainaddr; /* for isa_dmadone */
+ int sc_dmaincnt; /* for isa_dmadone */
+
+ struct stereo_dma_intr {
+ void (*intr)__P((void *));
+ void *arg;
+ u_char *buffer;
+ u_long dmabuf;
+ int size;
+ int flags;
+ } sc_stereo;
+
+ /*
+ * State information for linear audio layer
+ */
+
+ int sc_dmabuf; /* Which ring buffer we're DMA'ing to */
+ int sc_playbuf; /* Which ring buffer we're playing */
+
+ /*
+ * Voice information array. All voice-specific information is stored
+ * here
+ */
+
+ struct gus_voice sc_voc[32]; /* Voice data for each voice */
+ union {
+ struct ics2101_softc sc_mixer_u;
+ struct ad1848_softc sc_codec_u;
+ } u;
+#define sc_mixer u.sc_mixer_u
+#define sc_codec u.sc_codec_u
+};
+
+struct ics2101_volume {
+ u_char left;
+ u_char right;
+};
+
+#define HAS_CODEC(sc) ((sc)->sc_flags & GUS_CODEC_INSTALLED)
+#define HAS_MIXER(sc) ((sc)->sc_flags & GUS_MIXER_INSTALLED)
+
+/*
+ * Mixer devices for ICS2101
+ */
+/* MIC IN mute, line in mute, line out mute are first since they can be done
+ even if no ICS mixer. */
+#define GUSICS_MIC_IN_MUTE 0
+#define GUSICS_LINE_IN_MUTE 1
+#define GUSICS_MASTER_MUTE 2
+#define GUSICS_CD_MUTE 3
+#define GUSICS_DAC_MUTE 4
+#define GUSICS_MIC_IN_LVL 5
+#define GUSICS_LINE_IN_LVL 6
+#define GUSICS_CD_LVL 7
+#define GUSICS_DAC_LVL 8
+#define GUSICS_MASTER_LVL 9
+
+#define GUSICS_RECORD_SOURCE 10
+
+/* Classes */
+#define GUSICS_INPUT_CLASS 11
+#define GUSICS_OUTPUT_CLASS 12
+#define GUSICS_RECORD_CLASS 13
+
+/*
+ * Mixer & MUX devices for CS4231
+ */
+#define GUSMAX_MIX_IN 0 /* input to MUX from mixer output */
+#define GUSMAX_MONO_LVL 1 /* mic input to MUX;
+ also mono mixer input */
+#define GUSMAX_DAC_LVL 2 /* input to MUX; also mixer input */
+#define GUSMAX_LINE_IN_LVL 3 /* input to MUX; also mixer input */
+#define GUSMAX_CD_LVL 4 /* mixer input only */
+#define GUSMAX_MONITOR_LVL 5 /* digital mix (?) */
+#define GUSMAX_OUT_LVL 6 /* output level. (?) */
+#define GUSMAX_SPEAKER_LVL 7 /* pseudo-device for mute */
+#define GUSMAX_LINE_IN_MUTE 8 /* pre-mixer */
+#define GUSMAX_DAC_MUTE 9 /* pre-mixer */
+#define GUSMAX_CD_MUTE 10 /* pre-mixer */
+#define GUSMAX_MONO_MUTE 11 /* pre-mixer--microphone/mono */
+#define GUSMAX_MONITOR_MUTE 12 /* post-mixer level/mute */
+#define GUSMAX_SPEAKER_MUTE 13 /* speaker mute */
+
+#define GUSMAX_REC_LVL 14 /* post-MUX gain */
+
+#define GUSMAX_RECORD_SOURCE 15
+
+/* Classes */
+#define GUSMAX_INPUT_CLASS 16
+#define GUSMAX_RECORD_CLASS 17
+#define GUSMAX_MONITOR_CLASS 18
+#define GUSMAX_OUTPUT_CLASS 19
+
+#ifdef AUDIO_DEBUG
+#define GUSPLAYDEBUG /*XXX*/
+extern void Dprintf __P((const char *, ...));
+#define DPRINTF(x) if (gusdebug) Dprintf x
+#define DMAPRINTF(x) if (gusdmadebug) Dprintf x
+int gusdebug = 0;
+int gusdmadebug = 0;
+#else
+#define DPRINTF(x)
+#define DMAPRINTF(x)
+#endif
+int gus_dostereo = 1;
+
+#define NDMARECS 2048
+#ifdef GUSPLAYDEBUG
+int gusstats = 0;
+struct dma_record {
+ struct timeval tv;
+ u_long gusaddr;
+ caddr_t bsdaddr;
+ u_short count;
+ u_char channel;
+ u_char direction;
+} dmarecords[NDMARECS];
+
+int dmarecord_index = 0;
+#endif
+
+/*
+ * local routines
+ */
+
+int gusopen __P((dev_t, int));
+void gusclose __P((void *));
+void gusmax_close __P((void *));
+int gusprobe ()/*__P((struct device *, struct device *, void *))*/;
+void gusattach __P((struct device *, struct device *, void *));
+int gusintr __P((void *));
+int gus_set_in_gain __P((caddr_t, u_int, u_char));
+int gus_get_in_gain __P((caddr_t));
+int gus_set_out_gain __P((caddr_t, u_int, u_char));
+int gus_get_out_gain __P((caddr_t));
+int gus_set_in_sr __P((void *, u_long));
+u_long gus_get_in_sr __P((void *));
+int gusmax_set_in_sr __P((void *, u_long));
+u_long gusmax_get_in_sr __P((void *));
+int gus_set_out_sr __P((void *, u_long));
+u_long gus_get_out_sr __P((void *));
+int gusmax_set_out_sr __P((void *, u_long));
+u_long gusmax_get_out_sr __P((void *));
+int gus_set_encoding __P((void *, u_int));
+int gus_get_encoding __P((void *));
+int gusmax_set_encoding __P((void *, u_int));
+int gusmax_get_encoding __P((void *));
+int gus_set_precision __P((void *, u_int));
+int gus_get_precision __P((void *));
+int gusmax_set_precision __P((void *, u_int));
+int gusmax_get_precision __P((void *));
+int gus_set_channels __P((void *, int));
+int gus_get_channels __P((void *));
+int gusmax_set_channels __P((void *, int));
+int gusmax_get_channels __P((void *));
+int gus_round_blocksize __P((void *, int));
+int gus_set_out_port __P((void *, int));
+int gus_get_out_port __P((void *));
+int gus_set_in_port __P((void *, int));
+int gus_get_in_port __P((void *));
+int gus_commit_settings __P((void *));
+int gus_dma_output __P((void *, void *, int, void (*)(), void *));
+int gus_dma_input __P((void *, void *, int, void (*)(), void *));
+int gus_halt_out_dma __P((void *));
+int gus_halt_in_dma __P((void *));
+int gus_cont_out_dma __P((void *));
+int gus_cont_in_dma __P((void *));
+int gus_speaker_ctl __P((void *, int));
+int gusmax_set_precision __P((void *, u_int));
+int gusmax_get_precision __P((void *));
+int gusmax_round_blocksize __P((void *, int));
+int gusmax_commit_settings __P((void *));
+int gusmax_dma_output __P((void *, void *, int, void (*)(), void *));
+int gusmax_dma_input __P((void *, void *, int, void (*)(), void *));
+int gusmax_halt_out_dma __P((void *));
+int gusmax_halt_in_dma __P((void *));
+int gusmax_cont_out_dma __P((void *));
+int gusmax_cont_in_dma __P((void *));
+int gusmax_speaker_ctl __P((void *, int));
+int gusmax_set_out_port __P((void *, int));
+int gusmax_get_out_port __P((void *));
+int gusmax_set_in_port __P((void *, int));
+int gusmax_get_in_port __P((void *));
+int gus_getdev __P((void *, struct audio_device *));
+
+static void gus_deinterleave __P((struct gus_softc *, void *, int));
+static void gus_expand __P((void *, int, u_char *, int));
+static void gusmax_expand __P((void *, int, u_char *, int));
+
+static int gus_mic_ctl __P((void *, int));
+static int gus_linein_ctl __P((void *, int));
+static int gus_test_iobase __P((int));
+static void guspoke __P((int, long, u_char));
+static void gusdmaout __P((struct gus_softc *, int, u_long, caddr_t, int));
+static void gus_init_cs4231 __P((struct gus_softc *));
+static void gus_init_ics2101 __P((struct gus_softc *));
+
+static void gus_set_chan_addrs __P((struct gus_softc *));
+static void gusreset __P((struct gus_softc *, int));
+static void gus_set_voices __P((struct gus_softc *, int));
+static void gus_set_volume __P((struct gus_softc *, int, int));
+static void gus_set_samprate __P((struct gus_softc *, int, int));
+static void gus_set_recrate __P((struct gus_softc *, u_long));
+static void gus_start_voice __P((struct gus_softc *, int, int)),
+ gus_stop_voice __P((struct gus_softc *, int, int)),
+ gus_set_endaddr __P((struct gus_softc *, int, u_long)),
+ gus_set_curaddr __P((struct gus_softc *, int, u_long));
+static u_long gus_get_curaddr __P((struct gus_softc *, int));
+static int gus_dmaout_intr __P((struct gus_softc *));
+static void gus_dmaout_dointr __P((struct gus_softc *));
+static void gus_dmaout_timeout __P((void *));
+static int gus_dmain_intr __P((struct gus_softc *));
+static int gus_voice_intr __P((struct gus_softc *));
+static void gus_start_playing __P((struct gus_softc *, int));
+static void gus_continue_playing __P((struct gus_softc *, int));
+static u_char guspeek __P((int, u_long));
+static unsigned long convert_to_16bit();
+static int gus_setfd __P((void *, int));
+static int gus_mixer_set_port __P((void *, mixer_ctrl_t *));
+static int gus_mixer_get_port __P((void *, mixer_ctrl_t *));
+static int gusmax_mixer_set_port __P((void *, mixer_ctrl_t *));
+static int gusmax_mixer_get_port __P((void *, mixer_ctrl_t *));
+static int gus_mixer_query_devinfo __P((void *, mixer_devinfo_t *));
+static int gusmax_mixer_query_devinfo __P((void *, mixer_devinfo_t *));
+static int gus_query_encoding __P((void *, struct audio_encoding *));
+
+static void gusics_master_mute __P((struct ics2101_softc *, int));
+static void gusics_dac_mute __P((struct ics2101_softc *, int));
+static void gusics_mic_mute __P((struct ics2101_softc *, int));
+static void gusics_linein_mute __P((struct ics2101_softc *, int));
+static void gusics_cd_mute __P((struct ics2101_softc *, int));
+
+/*
+ * ISA bus driver routines
+ */
+
+struct cfdriver guscd = {
+ NULL, "gus", gusprobe, gusattach, DV_DULL, sizeof(struct gus_softc)
+};
+
+
+/*
+ * A mapping from IRQ/DRQ values to the values used in the GUS's internal
+ * registers. A zero means that the referenced IRQ/DRQ is invalid
+ */
+
+static int gus_irq_map[] = { 0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 };
+static int gus_drq_map[] = { 0, 1, 0, 2, 0, 3, 4, 5 };
+
+/*
+ * A list of valid base addresses for the GUS
+ */
+
+static u_short gus_base_addrs[] = { 0x210, 0x220, 0x230, 0x240, 0x250, 0x260 };
+static int gus_addrs = sizeof(gus_base_addrs) / sizeof(u_short);
+
+/*
+ * Maximum frequency values of the GUS based on the number of currently active
+ * voices. Since the GUS samples a voice every 1.6 us, the maximum frequency
+ * is dependent on the number of active voices. Yes, it is pretty weird.
+ */
+
+static int gus_max_frequency[] = {
+ 44100, /* 14 voices */
+ 41160, /* 15 voices */
+ 38587, /* 16 voices */
+ 36317, /* 17 voices */
+ 34300, /* 18 voices */
+ 32494, /* 19 voices */
+ 30870, /* 20 voices */
+ 29400, /* 21 voices */
+ 28063, /* 22 voices */
+ 26843, /* 23 voices */
+ 25725, /* 24 voices */
+ 24696, /* 25 voices */
+ 23746, /* 26 voices */
+ 22866, /* 27 voices */
+ 22050, /* 28 voices */
+ 21289, /* 29 voices */
+ 20580, /* 30 voices */
+ 19916, /* 31 voices */
+ 19293 /* 32 voices */
+};
+/*
+ * A mapping of linear volume levels to the logarithmic volume values used
+ * by the GF1 chip on the GUS. From GUS SDK vol1.c.
+ */
+
+static unsigned short gus_log_volumes[512] = {
+ 0x0000,
+ 0x0700, 0x07ff, 0x0880, 0x08ff, 0x0940, 0x0980, 0x09c0, 0x09ff, 0x0a20,
+ 0x0a40, 0x0a60, 0x0a80, 0x0aa0, 0x0ac0, 0x0ae0, 0x0aff, 0x0b10, 0x0b20,
+ 0x0b30, 0x0b40, 0x0b50, 0x0b60, 0x0b70, 0x0b80, 0x0b90, 0x0ba0, 0x0bb0,
+ 0x0bc0, 0x0bd0, 0x0be0, 0x0bf0, 0x0bff, 0x0c08, 0x0c10, 0x0c18, 0x0c20,
+ 0x0c28, 0x0c30, 0x0c38, 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68,
+ 0x0c70, 0x0c78, 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0,
+ 0x0cb8, 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8,
+ 0x0cff, 0x0d04, 0x0d08, 0x0d0c, 0x0d10, 0x0d14, 0x0d18, 0x0d1c, 0x0d20,
+ 0x0d24, 0x0d28, 0x0d2c, 0x0d30, 0x0d34, 0x0d38, 0x0d3c, 0x0d40, 0x0d44,
+ 0x0d48, 0x0d4c, 0x0d50, 0x0d54, 0x0d58, 0x0d5c, 0x0d60, 0x0d64, 0x0d68,
+ 0x0d6c, 0x0d70, 0x0d74, 0x0d78, 0x0d7c, 0x0d80, 0x0d84, 0x0d88, 0x0d8c,
+ 0x0d90, 0x0d94, 0x0d98, 0x0d9c, 0x0da0, 0x0da4, 0x0da8, 0x0dac, 0x0db0,
+ 0x0db4, 0x0db8, 0x0dbc, 0x0dc0, 0x0dc4, 0x0dc8, 0x0dcc, 0x0dd0, 0x0dd4,
+ 0x0dd8, 0x0ddc, 0x0de0, 0x0de4, 0x0de8, 0x0dec, 0x0df0, 0x0df4, 0x0df8,
+ 0x0dfc, 0x0dff, 0x0e02, 0x0e04, 0x0e06, 0x0e08, 0x0e0a, 0x0e0c, 0x0e0e,
+ 0x0e10, 0x0e12, 0x0e14, 0x0e16, 0x0e18, 0x0e1a, 0x0e1c, 0x0e1e, 0x0e20,
+ 0x0e22, 0x0e24, 0x0e26, 0x0e28, 0x0e2a, 0x0e2c, 0x0e2e, 0x0e30, 0x0e32,
+ 0x0e34, 0x0e36, 0x0e38, 0x0e3a, 0x0e3c, 0x0e3e, 0x0e40, 0x0e42, 0x0e44,
+ 0x0e46, 0x0e48, 0x0e4a, 0x0e4c, 0x0e4e, 0x0e50, 0x0e52, 0x0e54, 0x0e56,
+ 0x0e58, 0x0e5a, 0x0e5c, 0x0e5e, 0x0e60, 0x0e62, 0x0e64, 0x0e66, 0x0e68,
+ 0x0e6a, 0x0e6c, 0x0e6e, 0x0e70, 0x0e72, 0x0e74, 0x0e76, 0x0e78, 0x0e7a,
+ 0x0e7c, 0x0e7e, 0x0e80, 0x0e82, 0x0e84, 0x0e86, 0x0e88, 0x0e8a, 0x0e8c,
+ 0x0e8e, 0x0e90, 0x0e92, 0x0e94, 0x0e96, 0x0e98, 0x0e9a, 0x0e9c, 0x0e9e,
+ 0x0ea0, 0x0ea2, 0x0ea4, 0x0ea6, 0x0ea8, 0x0eaa, 0x0eac, 0x0eae, 0x0eb0,
+ 0x0eb2, 0x0eb4, 0x0eb6, 0x0eb8, 0x0eba, 0x0ebc, 0x0ebe, 0x0ec0, 0x0ec2,
+ 0x0ec4, 0x0ec6, 0x0ec8, 0x0eca, 0x0ecc, 0x0ece, 0x0ed0, 0x0ed2, 0x0ed4,
+ 0x0ed6, 0x0ed8, 0x0eda, 0x0edc, 0x0ede, 0x0ee0, 0x0ee2, 0x0ee4, 0x0ee6,
+ 0x0ee8, 0x0eea, 0x0eec, 0x0eee, 0x0ef0, 0x0ef2, 0x0ef4, 0x0ef6, 0x0ef8,
+ 0x0efa, 0x0efc, 0x0efe, 0x0eff, 0x0f01, 0x0f02, 0x0f03, 0x0f04, 0x0f05,
+ 0x0f06, 0x0f07, 0x0f08, 0x0f09, 0x0f0a, 0x0f0b, 0x0f0c, 0x0f0d, 0x0f0e,
+ 0x0f0f, 0x0f10, 0x0f11, 0x0f12, 0x0f13, 0x0f14, 0x0f15, 0x0f16, 0x0f17,
+ 0x0f18, 0x0f19, 0x0f1a, 0x0f1b, 0x0f1c, 0x0f1d, 0x0f1e, 0x0f1f, 0x0f20,
+ 0x0f21, 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29,
+ 0x0f2a, 0x0f2b, 0x0f2c, 0x0f2d, 0x0f2e, 0x0f2f, 0x0f30, 0x0f31, 0x0f32,
+ 0x0f33, 0x0f34, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3a, 0x0f3b,
+ 0x0f3c, 0x0f3d, 0x0f3e, 0x0f3f, 0x0f40, 0x0f41, 0x0f42, 0x0f43, 0x0f44,
+ 0x0f45, 0x0f46, 0x0f47, 0x0f48, 0x0f49, 0x0f4a, 0x0f4b, 0x0f4c, 0x0f4d,
+ 0x0f4e, 0x0f4f, 0x0f50, 0x0f51, 0x0f52, 0x0f53, 0x0f54, 0x0f55, 0x0f56,
+ 0x0f57, 0x0f58, 0x0f59, 0x0f5a, 0x0f5b, 0x0f5c, 0x0f5d, 0x0f5e, 0x0f5f,
+ 0x0f60, 0x0f61, 0x0f62, 0x0f63, 0x0f64, 0x0f65, 0x0f66, 0x0f67, 0x0f68,
+ 0x0f69, 0x0f6a, 0x0f6b, 0x0f6c, 0x0f6d, 0x0f6e, 0x0f6f, 0x0f70, 0x0f71,
+ 0x0f72, 0x0f73, 0x0f74, 0x0f75, 0x0f76, 0x0f77, 0x0f78, 0x0f79, 0x0f7a,
+ 0x0f7b, 0x0f7c, 0x0f7d, 0x0f7e, 0x0f7f, 0x0f80, 0x0f81, 0x0f82, 0x0f83,
+ 0x0f84, 0x0f85, 0x0f86, 0x0f87, 0x0f88, 0x0f89, 0x0f8a, 0x0f8b, 0x0f8c,
+ 0x0f8d, 0x0f8e, 0x0f8f, 0x0f90, 0x0f91, 0x0f92, 0x0f93, 0x0f94, 0x0f95,
+ 0x0f96, 0x0f97, 0x0f98, 0x0f99, 0x0f9a, 0x0f9b, 0x0f9c, 0x0f9d, 0x0f9e,
+ 0x0f9f, 0x0fa0, 0x0fa1, 0x0fa2, 0x0fa3, 0x0fa4, 0x0fa5, 0x0fa6, 0x0fa7,
+ 0x0fa8, 0x0fa9, 0x0faa, 0x0fab, 0x0fac, 0x0fad, 0x0fae, 0x0faf, 0x0fb0,
+ 0x0fb1, 0x0fb2, 0x0fb3, 0x0fb4, 0x0fb5, 0x0fb6, 0x0fb7, 0x0fb8, 0x0fb9,
+ 0x0fba, 0x0fbb, 0x0fbc, 0x0fbd, 0x0fbe, 0x0fbf, 0x0fc0, 0x0fc1, 0x0fc2,
+ 0x0fc3, 0x0fc4, 0x0fc5, 0x0fc6, 0x0fc7, 0x0fc8, 0x0fc9, 0x0fca, 0x0fcb,
+ 0x0fcc, 0x0fcd, 0x0fce, 0x0fcf, 0x0fd0, 0x0fd1, 0x0fd2, 0x0fd3, 0x0fd4,
+ 0x0fd5, 0x0fd6, 0x0fd7, 0x0fd8, 0x0fd9, 0x0fda, 0x0fdb, 0x0fdc, 0x0fdd,
+ 0x0fde, 0x0fdf, 0x0fe0, 0x0fe1, 0x0fe2, 0x0fe3, 0x0fe4, 0x0fe5, 0x0fe6,
+ 0x0fe7, 0x0fe8, 0x0fe9, 0x0fea, 0x0feb, 0x0fec, 0x0fed, 0x0fee, 0x0fef,
+ 0x0ff0, 0x0ff1, 0x0ff2, 0x0ff3, 0x0ff4, 0x0ff5, 0x0ff6, 0x0ff7, 0x0ff8,
+ 0x0ff9, 0x0ffa, 0x0ffb, 0x0ffc, 0x0ffd, 0x0ffe, 0x0fff};
+
+#define SELECT_GUS_REG(port,x) outb(port+GUS_REG_SELECT,x)
+#define WHICH_GUS_REG(port) inb(port+GUS_REG_SELECT)
+#define ADDR_HIGH(x) (unsigned int) ((x >> 7L) & 0x1fffL)
+#define ADDR_LOW(x) (unsigned int) ((x & 0x7fL) << 9L)
+
+#define GUS_MIN_VOICES 14 /* Minimum possible number of voices */
+#define GUS_MAX_VOICES 32 /* Maximum possible number of voices */
+#define GUS_VOICE_LEFT 0 /* Voice used for left (and mono) playback */
+#define GUS_VOICE_RIGHT 1 /* Voice used for right playback */
+#define GUS_MEM_OFFSET 32 /* Offset into GUS memory to begin of buffer */
+#define GUS_BUFFER_MULTIPLE 1024 /* Audio buffers are multiples of this */
+#define GUS_MEM_FOR_BUFFERS 131072 /* use this many bytes on-GUS */
+#define GUS_LEFT_RIGHT_OFFSET (sc->sc_nbufs * sc->sc_chanblocksize + GUS_MEM_OFFSET)
+
+#define GUS_PREC_BYTES (sc->sc_precision >> 3) /* precision to bytes */
+
+/* splgus() must be splaudio() */
+
+#define splgus splaudio
+
+/*
+ * Interface to higher level audio driver
+ */
+
+struct audio_hw_if gus_hw_if = {
+ gusopen,
+ gusclose,
+ NULL, /* drain */
+ gus_set_in_sr,
+ gus_get_in_sr,
+ gus_set_out_sr,
+ gus_get_out_sr,
+
+ gus_query_encoding,
+ gus_set_encoding,
+ gus_get_encoding,
+
+ gus_set_precision,
+ gus_get_precision,
+
+ gus_set_channels,
+ gus_get_channels,
+
+ gus_round_blocksize,
+
+ gus_set_out_port,
+ gus_get_out_port,
+ gus_set_in_port,
+ gus_get_in_port,
+
+ gus_commit_settings,
+
+ ad1848_get_silence,
+
+ gus_expand,
+ mulaw_compress,
+
+ gus_dma_output,
+ gus_dma_input,
+ gus_halt_out_dma,
+ gus_halt_in_dma,
+ gus_cont_out_dma,
+ gus_cont_in_dma,
+
+ gus_speaker_ctl,
+
+ gus_getdev,
+ gus_setfd,
+ gus_mixer_set_port,
+ gus_mixer_get_port,
+ gus_mixer_query_devinfo,
+ 1, /* full-duplex */
+ 0,
+};
+
+
+/*
+ * Some info about the current audio device
+ */
+
+struct audio_device gus_device = {
+ "UltraSound",
+ "",
+ "gus",
+};
+
+#define FLIP_REV 5 /* This rev has flipped mixer chans */
+
+
+int
+gusprobe(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ register struct gus_softc *sc = (void *) self;
+ register struct isa_attach_args *ia = aux;
+ struct cfdata *cf = sc->sc_dev.dv_cfdata;
+ register int iobase = ia->ia_iobase;
+ u_short recdrq = cf->cf_flags;
+
+ int i;
+ unsigned char s1, s2;
+
+ /*
+ * Before we do anything else, make sure requested IRQ and DRQ are
+ * valid for this card.
+ */
+
+ if (! gus_irq_map[ia->ia_irq]) {
+ printf("gus: invalid irq %d, card not probed\n", ia->ia_irq);
+ return(0);
+ }
+
+ if (! gus_drq_map[ia->ia_drq]) {
+ printf("gus: invalid drq %d, card not probed\n", ia->ia_drq);
+ return(0);
+ }
+
+ if (recdrq != 0x00) {
+ if (recdrq > 7 || ! gus_drq_map[recdrq]) {
+ printf("gus: invalid flag given for second DMA channel (0x%x), card not probed\n", recdrq);
+ return(0);
+ }
+ } else
+ recdrq = ia->ia_drq;
+
+ if (iobase == IOBASEUNK) {
+ int i;
+ for(i = 0; i < gus_addrs; i++)
+ if (gus_test_iobase(gus_base_addrs[i])) {
+ iobase = gus_base_addrs[i];
+ goto done;
+ }
+ return 0;
+ } else if (! gus_test_iobase(iobase))
+ return 0;
+
+done:
+ sc->sc_iobase = iobase;
+ sc->sc_irq = ia->ia_irq;
+ sc->sc_drq = ia->ia_drq;
+ sc->sc_recdrq = recdrq;
+
+ ia->ia_iobase = sc->sc_iobase;
+ ia->ia_iosize = 16; /* XXX */
+ return(1);
+}
+
+/*
+ * Test to see if a particular I/O base is valid for the GUS. Return true
+ * if it is.
+ */
+
+static int
+gus_test_iobase (int iobase)
+{
+ int i = splgus();
+ u_char s1, s2;
+
+ /*
+ * Reset GUS to an initial state before we do anything.
+ */
+
+ delay(500);
+
+ SELECT_GUS_REG(iobase, GUSREG_RESET);
+ outb(iobase+GUS_DATA_HIGH, 0x00);
+
+ delay(500);
+
+ SELECT_GUS_REG(iobase, GUSREG_RESET);
+ outb(iobase+GUS_DATA_HIGH, GUSMASK_MASTER_RESET);
+
+ delay(500);
+
+ splx(i);
+
+ /*
+ * See if we can write to the board's memory
+ */
+
+ s1 = guspeek(iobase, 0L);
+ s2 = guspeek(iobase, 1L);
+
+ guspoke(iobase, 0L, 0xaa);
+ guspoke(iobase, 1L, 0x55);
+
+ if ((i=(int)guspeek(iobase, 0L)) != 0xaa) {
+ return(0);
+ }
+
+ guspoke(iobase, 0L, s1);
+ guspoke(iobase, 1L, s2);
+
+ return 1;
+}
+
+/*
+ * Setup the GUS for use; called shortly after probe
+ */
+
+void
+gusattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ register struct gus_softc *sc = (void *) self;
+ register struct isa_attach_args *ia = aux;
+ register u_short port = ia->ia_iobase;
+ int s,i;
+ register unsigned char c,d,m;
+
+ /*
+ * Figure out our board rev, and see if we need to initialize the
+ * mixer
+ */
+
+ delay(500);
+
+ c = inb(port+GUS_BOARD_REV);
+ if (c != 0xff)
+ sc->sc_revision = c;
+ else
+ sc->sc_revision = 0;
+
+
+ SELECT_GUS_REG(port, GUSREG_RESET);
+ outb(port+GUS_DATA_HIGH, 0x00);
+
+ gusreset(sc, GUS_MAX_VOICES); /* initialize all voices */
+ gusreset(sc, GUS_MIN_VOICES); /* then set to just the ones we use */
+
+ /*
+ * Setup the IRQ and DRQ lines in software, using values from
+ * config file
+ */
+
+ m = GUSMASK_LINE_IN|GUSMASK_LINE_OUT; /* disable all */
+
+ c = ((unsigned char) gus_irq_map[ia->ia_irq]) | GUSMASK_BOTH_RQ;
+
+ if (sc->sc_recdrq == sc->sc_drq)
+ d = (unsigned char) (gus_drq_map[sc->sc_drq] |
+ GUSMASK_BOTH_RQ);
+ else
+ d = (unsigned char) (gus_drq_map[sc->sc_drq] |
+ gus_drq_map[sc->sc_recdrq] << 3);
+
+ /*
+ * Program the IRQ and DMA channels on the GUS. Note that we hardwire
+ * the GUS to only use one IRQ channel, but we give the user the
+ * option of using two DMA channels (the other one given by the flags
+ * option in the config file). Two DMA channels are needed for full-
+ * duplex operation.
+ *
+ * The order of these operations is very magical.
+ */
+
+ disable_intr();
+
+ outb(port+GUS_REG_CONTROL, GUS_REG_IRQCTL);
+ outb(port+GUS_MIX_CONTROL, m);
+ outb(port+GUS_IRQCTL_CONTROL, 0x00);
+ outb(port+0x0f, 0x00);
+
+ outb(port+GUS_MIX_CONTROL, m);
+ outb(port+GUS_DMA_CONTROL, d | 0x80); /* magic reset? */
+
+ outb(port+GUS_MIX_CONTROL, m | GUSMASK_CONTROL_SEL);
+ outb(port+GUS_IRQ_CONTROL, c);
+
+ outb(port+GUS_MIX_CONTROL, m);
+ outb(port+GUS_DMA_CONTROL, d);
+
+ outb(port+GUS_MIX_CONTROL, m | GUSMASK_CONTROL_SEL);
+ outb(port+GUS_IRQ_CONTROL, c);
+
+ outb(port+GUS_VOICE_SELECT, 0x00);
+
+ /* enable line in, line out. leave mic disabled. */
+ outb(port+GUS_MIX_CONTROL,
+ (m | GUSMASK_LATCHES) & ~(GUSMASK_LINE_OUT|GUSMASK_LINE_IN));
+ outb(port+GUS_VOICE_SELECT, 0x00);
+
+ enable_intr();
+
+ sc->sc_mixcontrol =
+ (m | GUSMASK_LATCHES) & ~(GUSMASK_LINE_OUT|GUSMASK_LINE_IN);
+
+
+ if (sc->sc_revision >= 5 && sc->sc_revision <= 9) {
+ sc->sc_flags |= GUS_MIXER_INSTALLED;
+ gus_init_ics2101(sc);
+ }
+ if (sc->sc_revision >= 0xa) {
+ gus_init_cs4231(sc);
+ }
+
+ SELECT_GUS_REG(port, GUSREG_RESET);
+ /*
+ * Check to see how much memory we have on this card; see if any
+ * "mirroring" occurs. We're assuming at least 256K already exists
+ * on the card; otherwise the initial probe would have failed
+ */
+
+ guspoke(port, 0L, 0x00);
+ for(i = 1; i < 1024; i++) {
+ unsigned long loc;
+ unsigned char val;
+
+ /*
+ * See if we've run into mirroring yet
+ */
+
+ if (guspeek(port, 0L) != 0)
+ break;
+
+ loc = i << 10;
+
+ guspoke(port, loc, 0xaa);
+ if (guspeek(port, loc) != 0xaa)
+ break;
+ }
+
+ sc->sc_dsize = i;
+ sprintf(gus_device.version, "3.%d", sc->sc_revision);
+
+ printf("\n <Gravis UltraSound version 3.%d, %dKB DRAM, ",
+ sc->sc_revision, sc->sc_dsize);
+ if (HAS_MIXER(sc))
+ printf("ICS2101 mixer, ");
+ if (HAS_CODEC(sc))
+ printf("%s codec/mixer, ", sc->sc_codec.chip_name);
+ if (sc->sc_recdrq == sc->sc_drq) {
+ printf("half-duplex");
+ gus_hw_if.full_duplex = 0;
+ } else {
+ printf("full-duplex, record drq %d", sc->sc_recdrq);
+ gus_hw_if.full_duplex = 1;
+ }
+
+ printf(">\n");
+
+ /*
+ * Setup a default interrupt handler
+ */
+
+ /* XXX we shouldn't have to use splgus == splclock, nor should
+ * we use ISA_IPL_CLOCK.
+ */
+ sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_EDGE, ISA_IPL_AUDIO,
+ gusintr, sc /* sc->sc_gusdsp */);
+
+ /*
+ * Set some default values
+ */
+
+ sc->sc_irate = sc->sc_orate = 44100;
+ sc->sc_encoding = AUDIO_ENCODING_LINEAR;
+ sc->sc_precision = 16;
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_DATA_SIZE16;
+ sc->sc_voc[GUS_VOICE_RIGHT].voccntl |= GUSMASK_DATA_SIZE16;
+ sc->sc_channels = 1;
+ sc->sc_ogain = 340;
+ gus_commit_settings(sc);
+
+ /*
+ * We always put the left channel full left & right channel
+ * full right.
+ * For mono playback, we set up both voices playing the same buffer.
+ */
+ outb(sc->sc_iobase+GUS_VOICE_SELECT, (unsigned char) GUS_VOICE_LEFT);
+ SELECT_GUS_REG(sc->sc_iobase, GUSREG_PAN_POS);
+ outb(sc->sc_iobase+GUS_DATA_HIGH, GUS_PAN_FULL_LEFT);
+
+ outb(sc->sc_iobase+GUS_VOICE_SELECT, (unsigned char) GUS_VOICE_RIGHT);
+ SELECT_GUS_REG(sc->sc_iobase, GUSREG_PAN_POS);
+ outb(sc->sc_iobase+GUS_DATA_HIGH, GUS_PAN_FULL_RIGHT);
+
+ /*
+ * Attach to the generic audio layer
+ */
+
+ if (audio_hardware_attach(&gus_hw_if, HAS_CODEC(sc) ? (void *)&sc->sc_codec : (void *)sc) != 0)
+ printf("gus: could not attach to audio pseudo-device driver\n");
+}
+
+int
+gusopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ int unit = AUDIOUNIT(dev);
+ struct gus_softc *sc;
+
+ DPRINTF(("gusopen() called\n"));
+
+ if (unit >= guscd.cd_ndevs)
+ return ENXIO;
+ sc = guscd.cd_devs[unit];
+ if (!sc)
+ return ENXIO;
+
+ if (sc->sc_flags & GUS_OPEN)
+ return EBUSY;
+
+ /*
+ * Some initialization
+ */
+
+ sc->sc_flags |= GUS_OPEN;
+ sc->sc_dmabuf = 0;
+ sc->sc_playbuf = -1;
+ sc->sc_bufcnt = 0;
+ sc->sc_voc[GUS_VOICE_LEFT].start_addr = GUS_MEM_OFFSET - 1;
+ sc->sc_voc[GUS_VOICE_LEFT].current_addr = GUS_MEM_OFFSET;
+
+ if (HAS_CODEC(sc)) {
+ ad1848_open(&sc->sc_codec, dev, flags);
+ sc->sc_codec.aux1_mute = 0;
+ ad1848_mute_aux1(&sc->sc_codec, 0); /* turn on DAC output */
+ if (flags & FREAD) {
+ sc->sc_codec.mono_mute = 0;
+ cs4231_mute_mono(&sc->sc_codec, 0);
+ }
+ } else if (flags & FREAD) {
+ /* enable/unmute the microphone */
+ if (HAS_MIXER(sc)) {
+ gusics_mic_mute(&sc->sc_mixer, 0);
+ } else
+ gus_mic_ctl(sc, SPKR_ON);
+ }
+ if (sc->sc_nbufs == 0)
+ gus_round_blocksize(sc, GUS_BUFFER_MULTIPLE); /* default blksiz */
+ return 0;
+}
+
+static void
+gusmax_expand(hdl, encoding, buf, count)
+ void *hdl;
+ int encoding;
+ u_char *buf;
+ int count;
+{
+ register struct ad1848_softc *ac = hdl;
+
+ gus_expand(ac->parent, encoding, buf, count);
+}
+
+static void
+gus_expand(hdl, encoding, buf, count)
+ void *hdl;
+ int encoding;
+ u_char *buf;
+ int count;
+{
+ struct gus_softc *sc = hdl;
+
+ mulaw_expand(NULL, encoding, buf, count);
+ /*
+ * If we need stereo deinterleaving, do it now.
+ */
+ if (sc->sc_channels == 2)
+ gus_deinterleave(sc, (void *)buf, count);
+}
+
+static void
+gus_deinterleave(sc, buf, size)
+ register struct gus_softc *sc;
+ void *buf;
+ int size;
+{
+ /* deinterleave the stereo data. We can use sc->sc_deintr_buf
+ for scratch space. */
+ register int i;
+
+ /*
+ * size is in bytes.
+ */
+ if (sc->sc_precision == 16) {
+ register u_short *dei = sc->sc_deintr_buf;
+ register u_short *sbuf = buf;
+ size >>= 1; /* bytecnt to shortcnt */
+ /* copy 2nd of each pair of samples to the staging area, while
+ compacting the 1st of each pair into the original area. */
+ for (i = 0; i < size/2-1; i++) {
+ dei[i] = sbuf[i*2+1];
+ sbuf[i+1] = sbuf[i*2+2];
+ }
+ /*
+ * this has copied one less sample than half of the
+ * buffer. The first sample of the 1st stream was
+ * already in place and didn't need copying.
+ * Therefore, we've moved all of the 1st stream's
+ * samples into place. We have one sample from 2nd
+ * stream in the last slot of original area, not
+ * copied to the staging area (But we don't need to!).
+ * Copy the remainder of the original stream into place.
+ */
+ bcopy(dei, &sbuf[size/2], i * sizeof(short));
+ } else {
+ register u_char *dei = sc->sc_deintr_buf;
+ register u_char *sbuf = buf;
+ for (i = 0; i < size/2-1; i++) {
+ dei[i] = sbuf[i*2+1];
+ sbuf[i+1] = sbuf[i*2+2];
+ }
+ bcopy(dei, &sbuf[size/2], i);
+ }
+}
+
+/*
+ * Actually output a buffer to the DSP chip
+ */
+
+int
+gusmax_dma_output(addr, buf, size, intr, arg)
+ void * addr;
+ void *buf;
+ int size;
+ void (*intr)();
+ void *arg;
+{
+ register struct ad1848_softc *ac = addr;
+ return gus_dma_output(ac->parent, buf, size, intr, arg);
+}
+
+/*
+ * called at splgus() from interrupt handler.
+ */
+void
+stereo_dmaintr(void *arg)
+{
+ struct gus_softc *sc = arg;
+ struct stereo_dma_intr *sa = &sc->sc_stereo;
+
+ DMAPRINTF(("stereo_dmaintr"));
+
+ /*
+ * Put other half in its place, then call the real interrupt routine :)
+ */
+
+ sc->sc_dmaoutintr = sa->intr;
+ sc->sc_outarg = sa->arg;
+
+#ifdef GUSPLAYDEBUG
+ if (gusstats) {
+ microtime(&dmarecords[dmarecord_index].tv);
+ dmarecords[dmarecord_index].gusaddr = sa->dmabuf;
+ dmarecords[dmarecord_index].bsdaddr = sa->buffer;
+ dmarecords[dmarecord_index].count = sa->size;
+ dmarecords[dmarecord_index].channel = 1;
+ dmarecords[dmarecord_index].direction = 1;
+ dmarecord_index = ++dmarecord_index % NDMARECS;
+ }
+#endif
+
+ gusdmaout(sc, sa->flags, sa->dmabuf, (caddr_t) sa->buffer, sa->size);
+
+ sa->flags = 0;
+ sa->dmabuf = 0;
+ sa->buffer = 0;
+ sa->size = 0;
+ sa->intr = 0;
+ sa->arg = 0;
+}
+
+/*
+ * Start up DMA output to the card.
+ * Called at splgus/splaudio already, either from intr handler or from
+ * generic audio code.
+ */
+int
+gus_dma_output(addr, buf, size, intr, arg)
+ void * addr;
+ void *buf;
+ int size;
+ void (*intr)();
+ void *arg;
+{
+ struct gus_softc *sc = addr;
+ u_char *buffer = buf;
+ u_long boarddma;
+ int i, flags;
+
+ DMAPRINTF(("gus_dma_output %d @ %x\n", size, buf));
+
+ if (size != sc->sc_blocksize) {
+ DPRINTF(("gus_dma_output reqsize %d not sc_blocksize %d\n",
+ size, sc->sc_blocksize));
+ return EINVAL;
+ }
+
+ flags = GUSMASK_DMA_WRITE;
+ if (sc->sc_precision == 16)
+ flags |= GUSMASK_DMA_DATA_SIZE;
+ /* pcm16 is signed, mulaw & pcm8 are unsigned */
+ if (sc->sc_encoding == AUDIO_ENCODING_ULAW ||
+ sc->sc_encoding == AUDIO_ENCODING_PCM8)
+ flags |= GUSMASK_DMA_INVBIT;
+
+ if (sc->sc_channels == 2) {
+ if (sc->sc_precision == 16) {
+ if (size & 3) {
+ DPRINTF(("gus_dma_output: unpaired 16bit samples"));
+ size &= 3;
+ }
+ } else if (size & 1) {
+ DPRINTF(("gus_dma_output: unpaired samples"));
+ size &= 1;
+ }
+ if (size == 0)
+ return 0;
+ size >>= 1;
+
+ boarddma = size * sc->sc_dmabuf + GUS_MEM_OFFSET;
+
+ sc->sc_stereo.intr = intr;
+ sc->sc_stereo.arg = arg;
+ sc->sc_stereo.size = size;
+ sc->sc_stereo.dmabuf = boarddma + GUS_LEFT_RIGHT_OFFSET;
+ sc->sc_stereo.buffer = buffer + size;
+ sc->sc_stereo.flags = flags;
+ if (gus_dostereo) {
+ intr = stereo_dmaintr;
+ arg = sc;
+ }
+ } else
+ boarddma = size * sc->sc_dmabuf + GUS_MEM_OFFSET;
+
+
+ sc->sc_flags |= GUS_LOCKED;
+ sc->sc_dmaoutintr = intr;
+ sc->sc_outarg = arg;
+
+#ifdef GUSPLAYDEBUG
+ if (gusstats) {
+ microtime(&dmarecords[dmarecord_index].tv);
+ dmarecords[dmarecord_index].gusaddr = boarddma;
+ dmarecords[dmarecord_index].bsdaddr = buffer;
+ dmarecords[dmarecord_index].count = size;
+ dmarecords[dmarecord_index].channel = 0;
+ dmarecords[dmarecord_index].direction = 1;
+ dmarecord_index = ++dmarecord_index % NDMARECS;
+ }
+#endif
+
+ gusdmaout(sc, flags, boarddma, (caddr_t) buffer, size);
+
+ return 0;
+}
+
+void
+gusmax_close(addr)
+ void *addr;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+/* ac->aux1_mute = 1;
+ ad1848_mute_aux1(ac, 1); /* turn off DAC output */
+ ad1848_close(ac);
+ gusclose(sc);
+}
+
+/*
+ * Close out device stuff. Called at splgus() from generic audio layer.
+ */
+void
+gusclose(addr)
+ void *addr;
+{
+ struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_close: sc=0x%x\n", sc));
+
+
+/* if (sc->sc_flags & GUS_DMAOUT_ACTIVE) */ {
+ gus_halt_out_dma(sc);
+ }
+/* if (sc->sc_flags & GUS_DMAIN_ACTIVE) */ {
+ gus_halt_in_dma(sc);
+ }
+ sc->sc_flags &= ~(GUS_OPEN|GUS_LOCKED|GUS_DMAOUT_ACTIVE|GUS_DMAIN_ACTIVE);
+
+ if (sc->sc_deintr_buf) {
+ FREE(sc->sc_deintr_buf, M_DEVBUF);
+ sc->sc_deintr_buf = NULL;
+ }
+ /* turn off speaker, etc. */
+
+ /* make sure the voices shut up: */
+ gus_stop_voice(sc, GUS_VOICE_LEFT, 1);
+ gus_stop_voice(sc, GUS_VOICE_RIGHT, 0);
+}
+
+/*
+ * Service interrupts. Farm them off to helper routines if we are using the
+ * GUS for simple playback/record
+ */
+
+#ifdef DIAGNOSTIC
+int gusintrcnt;
+int gusdmaintrcnt;
+int gusvocintrcnt;
+#endif
+
+int
+gusintr(arg)
+ void *arg;
+{
+ register struct gus_softc *sc = arg;
+ unsigned char intr;
+ register u_short port = sc->sc_iobase;
+ int retval = 0;
+
+ DPRINTF(("gusintr\n"));
+#ifdef DIAGNOSTIC
+ gusintrcnt++;
+#endif
+ if (HAS_CODEC(sc))
+ retval = ad1848_intr(&sc->sc_codec);
+ if ((intr = inb(port+GUS_IRQ_STATUS)) & GUSMASK_IRQ_DMATC) {
+ DMAPRINTF(("gusintr dma flags=%x\n", sc->sc_flags));
+#ifdef DIAGNOSTIC
+ gusdmaintrcnt++;
+#endif
+ retval += gus_dmaout_intr(sc);
+ if (sc->sc_flags & GUS_DMAIN_ACTIVE) {
+ SELECT_GUS_REG(port, GUSREG_SAMPLE_CONTROL);
+ intr = inb(port+GUS_DATA_HIGH);
+ if (intr & GUSMASK_SAMPLE_DMATC) {
+ retval += gus_dmain_intr(sc);
+ }
+ }
+ }
+ if (intr & (GUSMASK_IRQ_VOICE | GUSMASK_IRQ_VOLUME)) {
+ DMAPRINTF(("gusintr voice flags=%x\n", sc->sc_flags));
+#ifdef DIAGNOSTIC
+ gusvocintrcnt++;
+#endif
+ retval += gus_voice_intr(sc);
+ }
+ if (retval)
+ return 1;
+ return retval;
+}
+
+int gus_bufcnt[GUS_MEM_FOR_BUFFERS / GUS_BUFFER_MULTIPLE];
+int gus_restart; /* how many restarts? */
+int gus_stops; /* how many times did voice stop? */
+int gus_falsestops; /* stopped but not done? */
+int gus_continues;
+
+struct playcont {
+ struct timeval tv;
+ u_int playbuf;
+ u_int dmabuf;
+ u_char bufcnt;
+ u_char vaction;
+ u_char voccntl;
+ u_char volcntl;
+ u_long curaddr;
+ u_long endaddr;
+} playstats[NDMARECS];
+
+int playcntr;
+
+static void
+gus_dmaout_timeout(arg)
+ void *arg;
+{
+ register struct gus_softc *sc = arg;
+ register u_short port = sc->sc_iobase;
+ int s;
+
+ printf("%s: dmaout timeout\n", sc->sc_dev.dv_xname);
+ /*
+ * Stop any DMA.
+ */
+
+ s = splgus();
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ outb(sc->sc_iobase+GUS_DATA_HIGH, 0);
+
+/* isa_dmaabort(sc->sc_drq); /* XXX we will dmadone below? */
+
+ gus_dmaout_dointr(sc);
+ splx(s);
+}
+
+
+/*
+ * Service DMA interrupts. This routine will only get called if we're doing
+ * a DMA transfer for playback/record requests from the audio layer.
+ */
+
+static int
+gus_dmaout_intr(sc)
+ struct gus_softc *sc;
+{
+ register u_short port = sc->sc_iobase;
+
+ /*
+ * If we got a DMA transfer complete from the GUS DRAM, then deal
+ * with it.
+ */
+
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ if (inb(port+GUS_DATA_HIGH) & GUSMASK_DMA_IRQPEND) {
+ untimeout(gus_dmaout_timeout, sc);
+ gus_dmaout_dointr(sc);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+gus_dmaout_dointr(sc)
+ struct gus_softc *sc;
+{
+ register u_short port = sc->sc_iobase;
+
+ /* sc->sc_dmaoutcnt - 1 because DMA controller counts from zero?. */
+ isa_dmadone(B_WRITE,
+ sc->sc_dmaoutaddr,
+ sc->sc_dmaoutcnt - 1,
+ sc->sc_drq);
+ sc->sc_flags &= ~GUS_DMAOUT_ACTIVE; /* pending DMA is done */
+ DMAPRINTF(("gus_dmaout_dointr %d @ %x\n", sc->sc_dmaoutcnt,
+ sc->sc_dmaoutaddr));
+
+ /*
+ * to prevent clicking, we need to copy last sample
+ * from last buffer to scratch area just before beginning of
+ * buffer. However, if we're doing formats that are converted by
+ * the card during the DMA process, we need to pick up the converted
+ * byte rather than the one we have in memory.
+ */
+ if (sc->sc_dmabuf == sc->sc_nbufs - 1) {
+ register int i;
+ switch (sc->sc_encoding) {
+ case AUDIO_ENCODING_PCM16:
+ /* we have the native format */
+ for (i = 1; i <= 2; i++)
+ guspoke(port, sc->sc_gusaddr -
+ (sc->sc_nbufs - 1) * sc->sc_chanblocksize - i,
+ sc->sc_dmaoutaddr[sc->sc_dmaoutcnt-i]);
+ break;
+ case AUDIO_ENCODING_PCM8:
+ case AUDIO_ENCODING_ULAW:
+ /* we need to fetch the translated byte, then stuff it. */
+ guspoke(port, sc->sc_gusaddr -
+ (sc->sc_nbufs - 1) * sc->sc_chanblocksize - 1,
+ guspeek(port,
+ sc->sc_gusaddr + sc->sc_chanblocksize - 1));
+ break;
+ }
+ }
+ /*
+ * If this is the first half of stereo, "ignore" this one
+ * and copy out the second half.
+ */
+ if (sc->sc_dmaoutintr == stereo_dmaintr) {
+ (*sc->sc_dmaoutintr)(sc->sc_outarg);
+ return;
+ }
+ /*
+ * If the voice is stopped, then start it. Reset the loop
+ * and roll bits. Call the audio layer routine, since if
+ * we're starting a stopped voice, that means that the next
+ * buffer can be filled
+ */
+
+ sc->sc_flags &= ~GUS_LOCKED;
+ if (sc->sc_voc[GUS_VOICE_LEFT].voccntl &
+ GUSMASK_VOICE_STOPPED) {
+ if (sc->sc_flags & GUS_PLAYING) {
+ printf("%s: playing yet stopped?\n", sc->sc_dev.dv_xname);
+ }
+ sc->sc_bufcnt++; /* another yet to be played */
+ gus_start_playing(sc, sc->sc_dmabuf);
+ gus_restart++;
+ } else {
+ /*
+ * set the sound action based on which buffer we
+ * just transferred. If we just transferred buffer 0
+ * we want the sound to loop when it gets to the nth
+ * buffer; if we just transferred
+ * any other buffer, we want the sound to roll over
+ * at least one more time. The voice interrupt
+ * handlers will take care of accounting &
+ * setting control bits if it's not caught up to us
+ * yet.
+ */
+ if (++sc->sc_bufcnt == 2) {
+ /*
+ * XXX
+ * If we're too slow in reaction here,
+ * the voice could be just approaching the
+ * end of its run. It should be set to stop,
+ * so these adjustments might not DTRT.
+ */
+ if (sc->sc_dmabuf == 0 &&
+ sc->sc_playbuf == sc->sc_nbufs - 1) {
+ /* player is just at the last buf, we're at the
+ first. Turn on looping, turn off rolling. */
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[GUS_VOICE_LEFT].volcntl &= ~GUSMASK_VOICE_ROLL;
+ playstats[playcntr].vaction = 3;
+ } else {
+ /* player is at previous buf:
+ turn on rolling, turn off looping */
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[GUS_VOICE_LEFT].volcntl |= GUSMASK_VOICE_ROLL;
+ playstats[playcntr].vaction = 4;
+ }
+#ifdef GUSPLAYDEBUG
+ if (gusstats) {
+ microtime(&playstats[playcntr].tv);
+ playstats[playcntr].endaddr = sc->sc_voc[GUS_VOICE_LEFT].end_addr;
+ playstats[playcntr].voccntl = sc->sc_voc[GUS_VOICE_LEFT].voccntl;
+ playstats[playcntr].volcntl = sc->sc_voc[GUS_VOICE_LEFT].volcntl;
+ playstats[playcntr].playbuf = sc->sc_playbuf;
+ playstats[playcntr].dmabuf = sc->sc_dmabuf;
+ playstats[playcntr].bufcnt = sc->sc_bufcnt;
+ playstats[playcntr].curaddr = gus_get_curaddr(sc, GUS_VOICE_LEFT);
+ playcntr = ++playcntr % NDMARECS;
+ }
+#endif
+ outb(port+GUS_VOICE_SELECT, GUS_VOICE_LEFT);
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].voccntl);
+ SELECT_GUS_REG(port, GUSREG_VOLUME_CONTROL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].volcntl);
+ }
+ }
+ gus_bufcnt[sc->sc_bufcnt-1]++;
+ /*
+ * flip to the next DMA buffer
+ */
+
+ sc->sc_dmabuf = ++sc->sc_dmabuf % sc->sc_nbufs;
+ /*
+ * See comments below about DMA admission control strategy.
+ * We can call the upper level here if we have an
+ * idle buffer (not currently playing) to DMA into.
+ */
+ if (sc->sc_dmaoutintr && sc->sc_bufcnt < sc->sc_nbufs) {
+ /* clean out to prevent double calls */
+ void (*pfunc) __P((void *)) = sc->sc_dmaoutintr;
+ void *arg = sc->sc_outarg;
+
+ sc->sc_outarg = 0;
+ sc->sc_dmaoutintr = 0;
+ (*pfunc)(arg);
+ }
+}
+
+/*
+ * Service voice interrupts
+ */
+
+static int
+gus_voice_intr(sc)
+ struct gus_softc *sc;
+{
+ register u_short port = sc->sc_iobase;
+ int ignore = 0, voice, rval = 0;
+ unsigned long addr;
+ unsigned char intr, status;
+
+ /*
+ * The point of this may not be obvious at first. A voice can
+ * interrupt more than once; according to the GUS SDK we are supposed
+ * to ignore multiple interrupts for the same voice.
+ */
+
+ while(1) {
+ SELECT_GUS_REG(port, GUSREG_IRQ_STATUS);
+ intr = inb(port+GUS_DATA_HIGH);
+
+ if ((intr & (GUSMASK_WIRQ_VOLUME | GUSMASK_WIRQ_VOICE))
+ == (GUSMASK_WIRQ_VOLUME | GUSMASK_WIRQ_VOICE))
+ /*
+ * No more interrupts, time to return
+ */
+ return rval;
+
+ if ((intr & GUSMASK_WIRQ_VOICE) == 0) {
+
+ /*
+ * We've got a voice interrupt. Ignore previous
+ * interrupts by the same voice.
+ */
+
+ rval = 1;
+ voice = intr & GUSMASK_WIRQ_VOICEMASK;
+
+ if ((1 << voice) & ignore)
+ break;
+
+ ignore |= 1 << voice;
+
+ /*
+ * If the voice is stopped, then force it to stop
+ * (this stops it from continuously generating IRQs)
+ */
+
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL+0x80);
+ status = inb(port+GUS_DATA_HIGH);
+ if (status & GUSMASK_VOICE_STOPPED) {
+ if (voice != GUS_VOICE_LEFT) {
+ DMAPRINTF(("%s: spurious voice %d stop?\n",
+ sc->sc_dev.dv_xname, voice));
+ gus_stop_voice(sc, voice, 0);
+ continue;
+ }
+ gus_stop_voice(sc, voice, 1);
+ /* also kill right voice */
+ gus_stop_voice(sc, GUS_VOICE_RIGHT, 0);
+ sc->sc_bufcnt--; /* it finished a buffer */
+ if (sc->sc_bufcnt > 0) {
+ /*
+ * probably a race to get here: the voice
+ * stopped while the DMA code was just trying to
+ * get the next buffer in place.
+ * Start the voice again.
+ */
+ printf("%s: stopped voice not drained? (%x)\n",
+ sc->sc_dev.dv_xname, sc->sc_bufcnt);
+ gus_falsestops++;
+
+ sc->sc_playbuf = ++sc->sc_playbuf % sc->sc_nbufs;
+ gus_start_playing(sc, sc->sc_playbuf);
+ } else if (sc->sc_bufcnt < 0) {
+#ifdef DDB
+ printf("negative bufcnt in stopped voice\n");
+ Debugger();
+#else
+ panic("negative bufcnt in stopped voice");
+#endif
+ } else {
+ sc->sc_playbuf = -1; /* none are active */
+ gus_stops++;
+ }
+ /* fall through to callback and admit another
+ buffer.... */
+ } else if (sc->sc_bufcnt != 0) {
+ /*
+ * This should always be taken if the voice
+ * is not stopped.
+ */
+ gus_continues++;
+ gus_continue_playing(sc, voice);
+ }
+ /*
+ * call the upper level to send on down another
+ * block. We do admission rate control as follows:
+ *
+ * When starting up output (in the first N
+ * blocks), call the upper layer after the DMA is
+ * complete (see above in gus_dmaout_intr()).
+ *
+ * When output is already in progress and we have
+ * no more GUS buffers to use for DMA, the DMA
+ * output routines do not call the upper layer.
+ * Instead, we call the DMA completion routine
+ * here, after the voice interrupts indicating
+ * that it's finished with a buffer.
+ *
+ * However, don't call anything here if the DMA
+ * output flag is set, (which shouldn't happen)
+ * because we'll squish somebody else's DMA if
+ * that's the case. When DMA is done, it will
+ * call back if there is a spare buffer.
+ */
+ if (sc->sc_dmaoutintr && !(sc->sc_flags & GUS_LOCKED)) {
+ if (sc->sc_dmaoutintr == stereo_dmaintr)
+ printf("gusdmaout botch?\n");
+ else {
+ /* clean out to avoid double calls */
+ void (*pfunc)() = sc->sc_dmaoutintr;
+ void *arg = sc->sc_outarg;
+
+ sc->sc_outarg = 0;
+ sc->sc_dmaoutintr = 0;
+ (*pfunc)(arg);
+ }
+ }
+ }
+
+ /*
+ * Ignore other interrupts for now
+ */
+ }
+}
+
+static void
+gus_start_playing(sc, bufno)
+struct gus_softc *sc;
+int bufno;
+{
+ register u_short port = sc->sc_iobase;
+ /*
+ * Start the voices playing, with buffer BUFNO.
+ */
+
+ /*
+ * Loop or roll if we have buffers ready.
+ */
+
+ if (sc->sc_bufcnt == 1) {
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~(GUSMASK_LOOP_ENABLE);
+ sc->sc_voc[GUS_VOICE_LEFT].volcntl &= ~(GUSMASK_VOICE_ROLL);
+ } else {
+ if (bufno == sc->sc_nbufs - 1) {
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[GUS_VOICE_LEFT].volcntl &= ~(GUSMASK_VOICE_ROLL);
+ } else {
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[GUS_VOICE_LEFT].volcntl |= GUSMASK_VOICE_ROLL;
+ }
+ }
+
+ outb(port+GUS_VOICE_SELECT, GUS_VOICE_LEFT);
+
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].voccntl);
+
+ SELECT_GUS_REG(port, GUSREG_VOLUME_CONTROL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_LEFT].volcntl);
+
+ sc->sc_voc[GUS_VOICE_LEFT].current_addr =
+ GUS_MEM_OFFSET + sc->sc_chanblocksize * bufno;
+ sc->sc_voc[GUS_VOICE_LEFT].end_addr =
+ sc->sc_voc[GUS_VOICE_LEFT].current_addr + sc->sc_chanblocksize - 1;
+ sc->sc_voc[GUS_VOICE_RIGHT].current_addr =
+ sc->sc_voc[GUS_VOICE_LEFT].current_addr +
+ (gus_dostereo && sc->sc_channels == 2 ? GUS_LEFT_RIGHT_OFFSET : 0);
+ /*
+ * set up right channel to just loop forever, no interrupts,
+ * starting at the buffer we just filled. We'll feed it data
+ * at the same time as left channel.
+ */
+ sc->sc_voc[GUS_VOICE_RIGHT].voccntl |= GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[GUS_VOICE_RIGHT].volcntl &= ~(GUSMASK_VOICE_ROLL);
+
+#ifdef GUSPLAYDEBUG
+ if (gusstats) {
+ microtime(&playstats[playcntr].tv);
+ playstats[playcntr].curaddr = sc->sc_voc[GUS_VOICE_LEFT].current_addr;
+
+ playstats[playcntr].voccntl = sc->sc_voc[GUS_VOICE_LEFT].voccntl;
+ playstats[playcntr].volcntl = sc->sc_voc[GUS_VOICE_LEFT].volcntl;
+ playstats[playcntr].endaddr = sc->sc_voc[GUS_VOICE_LEFT].end_addr;
+ playstats[playcntr].playbuf = bufno;
+ playstats[playcntr].dmabuf = sc->sc_dmabuf;
+ playstats[playcntr].bufcnt = sc->sc_bufcnt;
+ playstats[playcntr].vaction = 5;
+ playcntr = ++playcntr % NDMARECS;
+ }
+#endif
+
+ outb(port+GUS_VOICE_SELECT, GUS_VOICE_RIGHT);
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_RIGHT].voccntl);
+ SELECT_GUS_REG(port, GUSREG_VOLUME_CONTROL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[GUS_VOICE_RIGHT].volcntl);
+
+ gus_start_voice(sc, GUS_VOICE_RIGHT, 0);
+ gus_start_voice(sc, GUS_VOICE_LEFT, 1);
+ if (sc->sc_playbuf == -1)
+ /* mark start of playing */
+ sc->sc_playbuf = bufno;
+}
+
+static void
+gus_continue_playing(sc, voice)
+register struct gus_softc *sc;
+int voice;
+{
+ register u_short port = sc->sc_iobase;
+
+ /*
+ * stop this voice from interrupting while we work.
+ */
+
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].voccntl & ~(GUSMASK_VOICE_IRQ));
+
+ /*
+ * update playbuf to point to the buffer the hardware just started
+ * playing
+ */
+ sc->sc_playbuf = ++sc->sc_playbuf % sc->sc_nbufs;
+
+ /*
+ * account for buffer just finished
+ */
+ if (--sc->sc_bufcnt == 0) {
+ DPRINTF(("gus: bufcnt 0 on continuing voice?\n"));
+ }
+ if (sc->sc_playbuf == sc->sc_dmabuf && (sc->sc_flags & GUS_LOCKED))
+ printf("continue into active dmabuf?\n");
+
+ /*
+ * Select the end of the buffer based on the currently active
+ * buffer, [plus extra contiguous buffers (if ready)].
+ */
+
+ /*
+ * set endpoint at end of buffer we just started playing.
+ *
+ * The total gets -1 because end addrs are one less than you might
+ * think (the end_addr is the address of the last sample to play)
+ */
+ gus_set_endaddr(sc, voice, GUS_MEM_OFFSET +
+ sc->sc_chanblocksize * (sc->sc_playbuf + 1) - 1);
+
+ if (sc->sc_bufcnt < 2) {
+ /*
+ * Clear out the loop and roll flags, and rotate the currently
+ * playing buffer. That way, if we don't manage to get more
+ * data before this buffer finishes, we'll just stop.
+ */
+ sc->sc_voc[voice].voccntl &= ~GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[voice].volcntl &= ~GUSMASK_VOICE_ROLL;
+ playstats[playcntr].vaction = 0;
+ } else {
+ /*
+ * We have some buffers to play. set LOOP if we're on the
+ * last buffer in the ring, otherwise set ROLL.
+ */
+ if (sc->sc_playbuf == sc->sc_nbufs - 1) {
+ sc->sc_voc[voice].voccntl |= GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[voice].volcntl &= ~GUSMASK_VOICE_ROLL;
+ playstats[playcntr].vaction = 1;
+ } else {
+ sc->sc_voc[voice].voccntl &= ~GUSMASK_LOOP_ENABLE;
+ sc->sc_voc[voice].volcntl |= GUSMASK_VOICE_ROLL;
+ playstats[playcntr].vaction = 2;
+ }
+ }
+#ifdef GUSPLAYDEBUG
+ if (gusstats) {
+ microtime(&playstats[playcntr].tv);
+ playstats[playcntr].curaddr = gus_get_curaddr(sc, voice);
+
+ playstats[playcntr].voccntl = sc->sc_voc[voice].voccntl;
+ playstats[playcntr].volcntl = sc->sc_voc[voice].volcntl;
+ playstats[playcntr].endaddr = sc->sc_voc[voice].end_addr;
+ playstats[playcntr].playbuf = sc->sc_playbuf;
+ playstats[playcntr].dmabuf = sc->sc_dmabuf;
+ playstats[playcntr].bufcnt = sc->sc_bufcnt;
+ playcntr = ++playcntr % NDMARECS;
+ }
+#endif
+
+ /*
+ * (re-)set voice parameters. This will reenable interrupts from this
+ * voice.
+ */
+
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
+ SELECT_GUS_REG(port, GUSREG_VOLUME_CONTROL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].volcntl);
+}
+
+/*
+ * Send/receive data into GUS's DRAM using DMA. Called at splgus()
+ */
+
+static void
+gusdmaout(sc, flags, gusaddr, buffaddr, length)
+ struct gus_softc *sc;
+ int flags, length;
+ unsigned long gusaddr;
+ caddr_t buffaddr;
+{
+ register unsigned char c = (unsigned char) flags;
+ register u_short port = sc->sc_iobase;
+ int s;
+
+ DMAPRINTF(("gusdmaout flags=%x scflags=%x\n", flags, sc->sc_flags));
+
+ sc->sc_gusaddr = gusaddr;
+
+ /*
+ * If we're using a 16 bit DMA channel, we have to jump through some
+ * extra hoops; this includes translating the DRAM address a bit
+ */
+
+ if (sc->sc_drq >= 4) {
+ c |= GUSMASK_DMA_WIDTH;
+ gusaddr = convert_to_16bit(gusaddr);
+ }
+
+ /*
+ * Add flag bits that we always set - fast DMA, enable IRQ
+ */
+
+ c |= GUSMASK_DMA_ENABLE | GUSMASK_DMA_R0 | GUSMASK_DMA_IRQ;
+
+ /*
+ * Make sure the GUS _isn't_ setup for DMA
+ */
+
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ outb(port+GUS_DATA_HIGH, 0);
+
+ /*
+ * Tell the PC DMA controller to start doing DMA
+ */
+
+ sc->sc_dmaoutaddr = (u_char *) buffaddr;
+ sc->sc_dmaoutcnt = length;
+ isa_dmastart(B_WRITE, buffaddr, length, sc->sc_drq);
+
+ /*
+ * Set up DMA address - use the upper 16 bits ONLY
+ */
+
+ sc->sc_flags |= GUS_DMAOUT_ACTIVE;
+
+ SELECT_GUS_REG(port, GUSREG_DMA_START);
+ outw(port+GUS_DATA_LOW, (int) (gusaddr >> 4));
+
+ /*
+ * Tell the GUS to start doing DMA
+ */
+
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ outb(port+GUS_DATA_HIGH, c);
+
+ /*
+ * XXX If we don't finish in one second, give up...
+ */
+ untimeout(gus_dmaout_timeout, sc); /* flush old one, if there is one */
+ timeout(gus_dmaout_timeout, sc, hz);
+}
+
+/*
+ * Start a voice playing on the GUS. Called from interrupt handler at
+ * splgus().
+ */
+
+static void
+gus_start_voice(sc, voice, intrs)
+ struct gus_softc *sc;
+ int voice;
+ int intrs;
+{
+ register u_short port = sc->sc_iobase;
+ unsigned long start;
+ unsigned long current;
+ unsigned long end;
+
+ /*
+ * Pick all the values for the voice out of the gus_voice struct
+ * and use those to program the voice
+ */
+
+ start = sc->sc_voc[voice].start_addr;
+ current = sc->sc_voc[voice].current_addr;
+ end = sc->sc_voc[voice].end_addr;
+
+ /*
+ * If we're using 16 bit data, mangle the addresses a bit
+ */
+
+ if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16) {
+ /* -1 on start so that we get onto sample boundary--other
+ code always sets it for 1-byte rollover protection */
+ start = convert_to_16bit(start-1);
+ current = convert_to_16bit(current);
+ end = convert_to_16bit(end);
+ }
+
+ /*
+ * Select the voice we want to use, and program the data addresses
+ */
+
+ outb(port+GUS_VOICE_SELECT, (unsigned char) voice);
+
+ SELECT_GUS_REG(port, GUSREG_START_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, ADDR_HIGH(start));
+ SELECT_GUS_REG(port, GUSREG_START_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, ADDR_LOW(start));
+
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, ADDR_HIGH(current));
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, ADDR_LOW(current));
+
+ SELECT_GUS_REG(port, GUSREG_END_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, ADDR_HIGH(end));
+ SELECT_GUS_REG(port, GUSREG_END_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, ADDR_LOW(end));
+
+ /*
+ * (maybe) enable interrupts, disable voice stopping
+ */
+
+ if (intrs) {
+ sc->sc_flags |= GUS_PLAYING; /* playing is about to start */
+ sc->sc_voc[voice].voccntl |= GUSMASK_VOICE_IRQ;
+ DMAPRINTF(("gus voice playing=%x\n", sc->sc_flags));
+ } else
+ sc->sc_voc[voice].voccntl &= ~GUSMASK_VOICE_IRQ;
+ sc->sc_voc[voice].voccntl &= ~(GUSMASK_VOICE_STOPPED |
+ GUSMASK_STOP_VOICE);
+
+ /*
+ * Tell the GUS about it. Note that we're doing volume ramping here
+ * from 0 up to the set volume to help reduce clicks.
+ */
+
+ SELECT_GUS_REG(port, GUSREG_START_VOLUME);
+ outb(port+GUS_DATA_HIGH, 0x00);
+ SELECT_GUS_REG(port, GUSREG_END_VOLUME);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].current_volume >> 4);
+ SELECT_GUS_REG(port, GUSREG_CUR_VOLUME);
+ outw(port+GUS_DATA_LOW, 0x00);
+ SELECT_GUS_REG(port, GUSREG_VOLUME_RATE);
+ outb(port+GUS_DATA_HIGH, 63);
+
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
+ SELECT_GUS_REG(port, GUSREG_VOLUME_CONTROL);
+ outb(port+GUS_DATA_HIGH, 0x00);
+ delay(50);
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
+ SELECT_GUS_REG(port, GUSREG_VOLUME_CONTROL);
+ outb(port+GUS_DATA_HIGH, 0x00);
+
+}
+
+/*
+ * Stop a given voice. called at splgus()
+ */
+
+static void
+gus_stop_voice(sc, voice, intrs_too)
+ struct gus_softc *sc;
+ int voice;
+ int intrs_too;
+{
+ register u_short port = sc->sc_iobase;
+
+ sc->sc_voc[voice].voccntl |= GUSMASK_VOICE_STOPPED |
+ GUSMASK_STOP_VOICE;
+ if (intrs_too) {
+ sc->sc_voc[voice].voccntl &= ~(GUSMASK_VOICE_IRQ);
+ /* no more DMA to do */
+ sc->sc_flags &= ~GUS_PLAYING;
+ }
+ DMAPRINTF(("gusintr voice notplaying=%x\n", sc->sc_flags));
+
+ guspoke(port, 0L, 0);
+
+ outb(port+GUS_VOICE_SELECT, (unsigned char) voice);
+
+ SELECT_GUS_REG(port, GUSREG_CUR_VOLUME);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
+ delay(100);
+ SELECT_GUS_REG(port, GUSREG_CUR_VOLUME);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[voice].voccntl);
+
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, 0x0000);
+
+}
+
+
+/*
+ * Set the volume of a given voice. Called at splgus().
+ */
+static void
+gus_set_volume(sc, voice, volume)
+ struct gus_softc *sc;
+ int voice, volume;
+{
+ register u_short port = sc->sc_iobase;
+ unsigned int gusvol;
+
+ gusvol = gus_log_volumes[volume < 512 ? volume : 511];
+
+ sc->sc_voc[voice].current_volume = gusvol;
+
+ outb(port+GUS_VOICE_SELECT, (unsigned char) voice);
+
+ SELECT_GUS_REG(port, GUSREG_START_VOLUME);
+ outb(port+GUS_DATA_HIGH, (unsigned char) (gusvol >> 4));
+
+ SELECT_GUS_REG(port, GUSREG_END_VOLUME);
+ outb(port+GUS_DATA_HIGH, (unsigned char) (gusvol >> 4));
+
+ SELECT_GUS_REG(port, GUSREG_CUR_VOLUME);
+ outw(port+GUS_DATA_LOW, gusvol << 4);
+ delay(500);
+ outw(port+GUS_DATA_LOW, gusvol << 4);
+
+}
+
+/*
+ * Interface to the audio layer - set the data encoding type
+ */
+
+int
+gusmax_set_encoding(addr, encoding)
+ void * addr;
+ u_int encoding;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ (void) ad1848_set_encoding(ac, encoding);
+ return gus_set_encoding(sc, encoding);
+}
+
+int
+gus_set_encoding(addr, encoding)
+ void * addr;
+ u_int encoding;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_set_encoding called\n"));
+
+ /* XXX todo: add alaw for codec */
+ if (encoding != AUDIO_ENCODING_ULAW &&
+ encoding != AUDIO_ENCODING_PCM16 &&
+ encoding != AUDIO_ENCODING_PCM8)
+ return EINVAL;
+
+ if (encoding != AUDIO_ENCODING_PCM16)
+ sc->sc_precision = 8; /* XXX force it. */
+
+ sc->sc_encoding = encoding;
+
+ if (sc->sc_precision == 8) {
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~GUSMASK_DATA_SIZE16;
+ sc->sc_voc[GUS_VOICE_RIGHT].voccntl &= ~GUSMASK_DATA_SIZE16;
+ } else {
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_DATA_SIZE16;
+ sc->sc_voc[GUS_VOICE_RIGHT].voccntl |= GUSMASK_DATA_SIZE16;
+ }
+ return 0;
+}
+
+int
+gusmax_set_channels(addr, channels)
+ void * addr;
+ int channels;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ (void) ad1848_set_channels(ac, channels);
+ return gus_set_channels(sc, channels);
+}
+
+int
+gus_set_channels(addr, channels)
+ void * addr;
+ int channels;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_set_channels called\n"));
+
+ if (channels != 1 && channels != 2)
+ return EINVAL;
+
+ sc->sc_channels = channels;
+
+ return 0;
+}
+
+/*
+ * Interface to the audio layer - set the data precision
+ */
+
+int
+gusmax_set_precision(addr, bits)
+ void * addr;
+ u_int bits;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+
+ (void) ad1848_set_precision(ac, bits);
+ return gus_set_precision(sc, bits);
+}
+
+
+int
+gus_set_precision(addr, bits)
+ void * addr;
+ u_int bits;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_set_precision called\n"));
+
+ if (bits != 8 && bits != 16)
+ return EINVAL;
+
+ if (sc->sc_encoding != AUDIO_ENCODING_PCM16 && bits != 8)
+ /* If we're doing PCM8 or MULAW, it must be 8 bits. */
+ return EINVAL;
+
+ sc->sc_precision = bits;
+
+ if (bits == 16) {
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl |= GUSMASK_DATA_SIZE16;
+ sc->sc_voc[GUS_VOICE_RIGHT].voccntl |= GUSMASK_DATA_SIZE16;
+ } else {
+ sc->sc_voc[GUS_VOICE_LEFT].voccntl &= ~GUSMASK_DATA_SIZE16;
+ sc->sc_voc[GUS_VOICE_RIGHT].voccntl &= ~GUSMASK_DATA_SIZE16;
+ }
+ return 0;
+}
+
+/*
+ * Interface to the audio layer - set the blocksize to the correct number
+ * of units
+ */
+
+int
+gusmax_round_blocksize(addr, blocksize)
+ void * addr;
+ int blocksize;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+
+/* blocksize = ad1848_round_blocksize(ac, blocksize);*/
+ return gus_round_blocksize(sc, blocksize);
+}
+
+int
+gus_round_blocksize(addr, blocksize)
+ void * addr;
+ int blocksize;
+{
+ register struct gus_softc *sc = addr;
+ register unsigned long i;
+
+ DPRINTF(("gus_round_blocksize called\n"));
+
+ if (sc->sc_encoding == AUDIO_ENCODING_ULAW && blocksize > 32768)
+ blocksize = 32768;
+ else if (blocksize > 65536)
+ blocksize = 65536;
+
+ if ((blocksize % GUS_BUFFER_MULTIPLE) != 0)
+ blocksize = (blocksize / GUS_BUFFER_MULTIPLE + 1) *
+ GUS_BUFFER_MULTIPLE;
+
+ /* set up temporary buffer to hold the deinterleave, if necessary
+ for stereo output */
+ if (sc->sc_deintr_buf) {
+ FREE(sc->sc_deintr_buf, M_DEVBUF);
+ sc->sc_deintr_buf = NULL;
+ }
+ MALLOC(sc->sc_deintr_buf, void *, blocksize>>1, M_DEVBUF, M_WAITOK);
+
+ sc->sc_blocksize = blocksize;
+ /* multi-buffering not quite working yet. */
+ sc->sc_nbufs = /*GUS_MEM_FOR_BUFFERS / blocksize*/ 2;
+
+ gus_set_chan_addrs(sc);
+
+ return blocksize;
+}
+
+/*
+ * Interfaces to the audio layer - return values from the software config
+ * struct
+ */
+
+int
+gusmax_get_encoding(addr)
+ void * addr;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ return gus_get_encoding(sc);
+}
+
+int
+gus_get_encoding(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_get_encoding called\n"));
+
+ /* XXX TODO: codec stuff */
+ return sc->sc_encoding;
+}
+
+int
+gusmax_get_channels(addr)
+ void * addr;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ return gus_get_channels(sc);
+}
+
+int
+gus_get_channels(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_get_channels called\n"));
+
+ return sc->sc_channels;
+}
+
+u_long
+gus_get_in_sr(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_get_in_sr called\n"));
+ return sc->sc_irate;
+}
+
+u_long
+gusmax_get_in_sr(addr)
+ void * addr;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ return gus_get_in_sr(sc);
+}
+
+u_long
+gusmax_get_out_sr(addr)
+ void * addr;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ return gus_get_out_sr(sc);
+}
+
+u_long
+gus_get_out_sr(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_get_out_sr called\n"));
+ return sc->sc_orate;
+}
+
+int
+gusmax_get_precision(addr)
+ void * addr;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_get_precision(sc->parent);
+}
+
+int
+gus_get_precision(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_get_precision called\n"));
+
+ return sc->sc_precision;
+}
+
+int
+gus_get_out_gain(addr)
+ caddr_t addr;
+{
+ register struct gus_softc *sc = (struct gus_softc *) addr;
+
+ DPRINTF(("gus_get_out_gain called\n"));
+ return sc->sc_ogain / 2;
+}
+
+/*
+ * Interface to the audio layer - set the sample rate of the output voices
+ */
+
+int
+gusmax_set_out_sr(addr, rate)
+ void * addr;
+ u_long rate;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ (void) ad1848_set_out_sr(ac, rate);
+ return gus_set_out_sr(sc, rate);
+}
+
+int
+gus_set_out_sr(addr, rate)
+ void * addr;
+ u_long rate;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_set_out_sr called\n"));
+
+ if (rate > gus_max_frequency[sc->sc_voices - GUS_MIN_VOICES])
+ rate = gus_max_frequency[sc->sc_voices - GUS_MIN_VOICES];
+
+ sc->sc_orate = rate;
+
+ return 0;
+}
+
+static inline void gus_set_voices(sc, voices)
+struct gus_softc *sc;
+int voices;
+{
+ register u_short port = sc->sc_iobase;
+ /*
+ * Select the active number of voices
+ */
+
+ SELECT_GUS_REG(port, GUSREG_ACTIVE_VOICES);
+ outb(port+GUS_DATA_HIGH, (voices-1) | 0xc0);
+
+ sc->sc_voices = voices;
+}
+
+/*
+ * Actually set the settings of various values on the card
+ */
+
+int
+gusmax_commit_settings(addr)
+ void * addr;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+
+ (void) ad1848_commit_settings(ac);
+ return gus_commit_settings(sc);
+}
+
+/*
+ * Commit the settings. Called at normal IPL.
+ */
+int
+gus_commit_settings(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+ int s;
+
+ DPRINTF(("gus_commit_settings called (gain = %d)\n",sc->sc_ogain));
+
+
+ s = splgus();
+
+ gus_set_recrate(sc, sc->sc_irate);
+ gus_set_volume(sc, GUS_VOICE_LEFT, sc->sc_ogain);
+ gus_set_volume(sc, GUS_VOICE_RIGHT, sc->sc_ogain);
+ gus_set_samprate(sc, GUS_VOICE_LEFT, sc->sc_orate);
+ gus_set_samprate(sc, GUS_VOICE_RIGHT, sc->sc_orate);
+ splx(s);
+ gus_set_chan_addrs(sc);
+
+ return 0;
+}
+
+static void
+gus_set_chan_addrs(sc)
+struct gus_softc *sc;
+{
+ /*
+ * We use sc_nbufs * blocksize bytes of storage in the on-board GUS
+ * ram.
+ * For mono, each of the sc_nbufs buffers is DMA'd to in one chunk,
+ * and both left & right channels play the same buffer.
+ *
+ * For stereo, each channel gets a contiguous half of the memory,
+ * and each has sc_nbufs buffers of size blocksize/2.
+ * Stereo data are deinterleaved in main memory before the DMA out
+ * routines are called to queue the output.
+ *
+ * The blocksize per channel is kept in sc_chanblocksize.
+ */
+ if (sc->sc_channels == 2)
+ sc->sc_chanblocksize = sc->sc_blocksize/2;
+ else
+ sc->sc_chanblocksize = sc->sc_blocksize;
+
+ sc->sc_voc[GUS_VOICE_LEFT].start_addr = GUS_MEM_OFFSET - 1;
+ sc->sc_voc[GUS_VOICE_RIGHT].start_addr =
+ (gus_dostereo && sc->sc_channels == 2 ? GUS_LEFT_RIGHT_OFFSET : 0)
+ + GUS_MEM_OFFSET - 1;
+ sc->sc_voc[GUS_VOICE_RIGHT].current_addr =
+ sc->sc_voc[GUS_VOICE_RIGHT].start_addr + 1;
+ sc->sc_voc[GUS_VOICE_RIGHT].end_addr =
+ sc->sc_voc[GUS_VOICE_RIGHT].start_addr +
+ sc->sc_nbufs * sc->sc_chanblocksize;
+
+}
+
+/*
+ * Set the sample rate of the given voice. Called at splgus().
+ */
+
+static void
+gus_set_samprate(sc, voice, freq)
+ struct gus_softc *sc;
+ int voice, freq;
+{
+ register u_short port = sc->sc_iobase;
+ unsigned int fc;
+ unsigned long temp, f = (unsigned long) freq;
+
+ /*
+ * calculate fc based on the number of active voices;
+ * we need to use longs to preserve enough bits
+ */
+
+ temp = (unsigned long) gus_max_frequency[sc->sc_voices-GUS_MIN_VOICES];
+
+ fc = (unsigned int)(((f << 9L) + (temp >> 1L)) / temp);
+
+ fc <<= 1;
+
+
+ /*
+ * Program the voice frequency, and set it in the voice data record
+ */
+
+ outb(port+GUS_VOICE_SELECT, (unsigned char) voice);
+ SELECT_GUS_REG(port, GUSREG_FREQ_CONTROL);
+ outw(port+GUS_DATA_LOW, fc);
+
+ sc->sc_voc[voice].rate = freq;
+
+}
+
+/*
+ * Interface to the audio layer - set the recording sampling rate
+ */
+
+int
+gusmax_set_in_sr(addr, rate)
+ void * addr;
+ u_long rate;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ (void) ad1848_set_in_sr(ac, rate);
+ return gus_set_in_sr(sc, rate);
+}
+
+
+int
+gus_set_in_sr(addr, rate)
+ void *addr;
+ u_long rate;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gus_set_in_sr called\n"));
+
+ sc->sc_irate = rate;
+
+ return 0;
+}
+/*
+ * Set the sample rate of the recording frequency. Formula is from the GUS
+ * SDK. Called at splgus().
+ */
+
+static void
+gus_set_recrate(sc, rate)
+ struct gus_softc *sc;
+ u_long rate;
+{
+ register u_short port = sc->sc_iobase;
+ u_char realrate;
+ int s;
+ DPRINTF(("gus_set_recrate %lu\n", rate));
+
+/* realrate = 9878400/(16*(rate+2)); /* formula from GUS docs */
+ realrate = (9878400 >> 4)/rate - 2; /* formula from code, sigh. */
+
+ SELECT_GUS_REG(port, GUSREG_SAMPLE_FREQ);
+ outb(port+GUS_DATA_HIGH, realrate);
+}
+
+/*
+ * Interface to the audio layer - turn the output on or off. Note that some
+ * of these bits are flipped in the register
+ */
+
+int
+gusmax_speaker_ctl(addr, newstate)
+ void * addr;
+ int newstate;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_speaker_ctl(sc->parent, newstate);
+}
+
+int
+gus_speaker_ctl(addr, newstate)
+ void * addr;
+ int newstate;
+{
+ register struct gus_softc *sc = (struct gus_softc *) addr;
+
+ /* Line out bit is flipped: 0 enables, 1 disables */
+ if ((newstate == SPKR_ON) &&
+ (sc->sc_mixcontrol & GUSMASK_LINE_OUT)) {
+ sc->sc_mixcontrol &= ~GUSMASK_LINE_OUT;
+ outb(sc->sc_iobase+GUS_MIX_CONTROL, sc->sc_mixcontrol);
+ }
+ if ((newstate == SPKR_OFF) &&
+ (sc->sc_mixcontrol & GUSMASK_LINE_OUT) == 0) {
+ sc->sc_mixcontrol |= GUSMASK_LINE_OUT;
+ outb(sc->sc_iobase+GUS_MIX_CONTROL, sc->sc_mixcontrol);
+ }
+
+ return 0;
+}
+
+static int
+gus_linein_ctl(addr, newstate)
+ void * addr;
+ int newstate;
+{
+ register struct gus_softc *sc = (struct gus_softc *) addr;
+
+ /* Line in bit is flipped: 0 enables, 1 disables */
+ if ((newstate == SPKR_ON) &&
+ (sc->sc_mixcontrol & GUSMASK_LINE_IN)) {
+ sc->sc_mixcontrol &= ~GUSMASK_LINE_IN;
+ outb(sc->sc_iobase+GUS_MIX_CONTROL, sc->sc_mixcontrol);
+ }
+ if ((newstate == SPKR_OFF) &&
+ (sc->sc_mixcontrol & GUSMASK_LINE_IN) == 0) {
+ sc->sc_mixcontrol |= GUSMASK_LINE_IN;
+ outb(sc->sc_iobase+GUS_MIX_CONTROL, sc->sc_mixcontrol);
+ }
+
+ return 0;
+}
+
+static int
+gus_mic_ctl(addr, newstate)
+ void * addr;
+ int newstate;
+{
+ register struct gus_softc *sc = (struct gus_softc *) addr;
+
+ /* Mic bit is normal: 1 enables, 0 disables */
+ if ((newstate == SPKR_ON) &&
+ (sc->sc_mixcontrol & GUSMASK_MIC_IN) == 0) {
+ sc->sc_mixcontrol |= GUSMASK_MIC_IN;
+ outb(sc->sc_iobase+GUS_MIX_CONTROL, sc->sc_mixcontrol);
+ }
+ if ((newstate == SPKR_OFF) &&
+ (sc->sc_mixcontrol & GUSMASK_MIC_IN)) {
+ sc->sc_mixcontrol &= ~GUSMASK_MIC_IN;
+ outb(sc->sc_iobase+GUS_MIX_CONTROL, sc->sc_mixcontrol);
+ }
+
+ return 0;
+}
+
+/*
+ * Set the end address of a give voice. Called at splgus()
+ */
+
+static void
+gus_set_endaddr(sc, voice, addr)
+ struct gus_softc *sc;
+ int voice;
+ unsigned long addr;
+{
+ register u_short port = sc->sc_iobase;
+
+ sc->sc_voc[voice].end_addr = addr;
+
+ if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16)
+ addr = convert_to_16bit(addr);
+
+ SELECT_GUS_REG(port, GUSREG_END_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, ADDR_HIGH(addr));
+ SELECT_GUS_REG(port, GUSREG_END_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, ADDR_LOW(addr));
+
+}
+
+#if 0
+/*
+ * Set current address. called at splgus()
+ */
+static void
+gus_set_curaddr(sc, voice, addr)
+ struct gus_softc *sc;
+ int voice;
+ unsigned long addr;
+{
+ register u_short port = sc->sc_iobase;
+
+ sc->sc_voc[voice].current_addr = addr;
+
+ if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16)
+ addr = convert_to_16bit(addr);
+
+ outb(port+GUS_VOICE_SELECT, (unsigned char) voice);
+
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, ADDR_HIGH(addr));
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, ADDR_LOW(addr));
+
+}
+#endif
+
+/*
+ * Get current GUS playback address. Called at splgus().
+ */
+static unsigned long
+gus_get_curaddr(sc, voice)
+ struct gus_softc *sc;
+ int voice;
+{
+ register u_short port = sc->sc_iobase;
+ unsigned long addr;
+
+ outb(port+GUS_VOICE_SELECT, (unsigned char) voice);
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_HIGH|GUSREG_READ);
+ addr = (inw(port+GUS_DATA_LOW) & 0x1fff) << 7;
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_LOW|GUSREG_READ);
+ addr |= (inw(port+GUS_DATA_LOW) >> 9L) & 0x7f;
+
+ if (sc->sc_voc[voice].voccntl & GUSMASK_DATA_SIZE16)
+ addr = (addr & 0xc0000) | ((addr & 0x1ffff) << 1); /* undo 16-bit change */
+ DPRINTF(("gus voice %d curaddr %d end_addr %d\n",
+ voice, addr, sc->sc_voc[voice].end_addr));
+ /* XXX sanity check the address? */
+
+ return(addr);
+}
+
+/*
+ * Convert an address value to a "16 bit" value - why this is necessary I
+ * have NO idea
+ */
+
+static unsigned long
+convert_to_16bit(address)
+ unsigned long address;
+{
+ unsigned long old_address;
+
+ old_address = address;
+ address >>= 1;
+ address &= 0x0001ffffL;
+ address |= (old_address & 0x000c0000L);
+
+ return (address);
+}
+
+/*
+ * Write a value into the GUS's DRAM
+ */
+
+static void
+guspoke(port, address, value)
+ int port;
+ long address;
+ unsigned char value;
+{
+
+ /*
+ * Select the DRAM address
+ */
+
+ SELECT_GUS_REG(port, GUSREG_DRAM_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, (unsigned int) (address & 0xffff));
+ SELECT_GUS_REG(port, GUSREG_DRAM_ADDR_HIGH);
+ outb(port+GUS_DATA_HIGH, (unsigned char) ((address >> 16) & 0xff));
+
+ /*
+ * Actually write the data
+ */
+
+ outb(port+GUS_DRAM_DATA, value);
+}
+
+/*
+ * Read a value from the GUS's DRAM
+ */
+
+static unsigned char
+guspeek(port, address)
+ int port;
+ u_long address;
+{
+
+ /*
+ * Select the DRAM address
+ */
+
+ SELECT_GUS_REG(port, GUSREG_DRAM_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, (unsigned int) (address & 0xffff));
+ SELECT_GUS_REG(port, GUSREG_DRAM_ADDR_HIGH);
+ outb(port+GUS_DATA_HIGH, (unsigned char) ((address >> 16) & 0xff));
+
+ /*
+ * Read in the data from the board
+ */
+
+ return (unsigned char) inb(port+GUS_DRAM_DATA);
+}
+
+/*
+ * Reset the Gravis UltraSound card, completely
+ */
+
+static void
+gusreset(sc, voices)
+ struct gus_softc *sc;
+ int voices;
+{
+ register u_short port = sc->sc_iobase;
+ int i,s;
+
+ s = splgus();
+
+ /*
+ * Reset the GF1 chip
+ */
+
+ SELECT_GUS_REG(port, GUSREG_RESET);
+ outb(port+GUS_DATA_HIGH, 0x00);
+
+ delay(500);
+
+ /*
+ * Release reset
+ */
+
+ SELECT_GUS_REG(port, GUSREG_RESET);
+ outb(port+GUS_DATA_HIGH, GUSMASK_MASTER_RESET);
+
+ delay(500);
+
+ /*
+ * Reset MIDI port as well
+ */
+
+ outb(GUS_MIDI_CONTROL,MIDI_RESET);
+
+ delay(500);
+
+ outb(GUS_MIDI_CONTROL,0x00);
+
+ /*
+ * Clear interrupts
+ */
+
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ outb(port+GUS_DATA_HIGH, 0x00);
+ SELECT_GUS_REG(port, GUSREG_TIMER_CONTROL);
+ outb(port+GUS_DATA_HIGH, 0x00);
+ SELECT_GUS_REG(port, GUSREG_SAMPLE_CONTROL);
+ outb(port+GUS_DATA_HIGH, 0x00);
+
+ gus_set_voices(sc, voices);
+
+ inb(port+GUS_IRQ_STATUS);
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ inb(port+GUS_DATA_HIGH);
+ SELECT_GUS_REG(port, GUSREG_SAMPLE_CONTROL);
+ inb(port+GUS_DATA_HIGH);
+ SELECT_GUS_REG(port, GUSREG_IRQ_STATUS);
+ inb(port+GUS_DATA_HIGH);
+
+ /*
+ * Reset voice specific information
+ */
+
+ for(i = 0; i < voices; i++) {
+ outb(port+GUS_VOICE_SELECT, (unsigned char) i);
+
+ SELECT_GUS_REG(port, GUSREG_VOICE_CNTL);
+
+ sc->sc_voc[i].voccntl = GUSMASK_VOICE_STOPPED |
+ GUSMASK_STOP_VOICE;
+
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[i].voccntl);
+
+ sc->sc_voc[i].volcntl = GUSMASK_VOLUME_STOPPED |
+ GUSMASK_STOP_VOLUME;
+
+ SELECT_GUS_REG(port, GUSREG_VOLUME_CONTROL);
+ outb(port+GUS_DATA_HIGH, sc->sc_voc[i].volcntl);
+
+ delay(100);
+
+ gus_set_samprate(sc, i, 8000);
+ SELECT_GUS_REG(port, GUSREG_START_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_START_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_END_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_END_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_VOLUME_RATE);
+ outb(port+GUS_DATA_HIGH, 0x01);
+ SELECT_GUS_REG(port, GUSREG_START_VOLUME);
+ outb(port+GUS_DATA_HIGH, 0x10);
+ SELECT_GUS_REG(port, GUSREG_END_VOLUME);
+ outb(port+GUS_DATA_HIGH, 0xe0);
+ SELECT_GUS_REG(port, GUSREG_CUR_VOLUME);
+ outw(port+GUS_DATA_LOW, 0x0000);
+
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_HIGH);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_CUR_ADDR_LOW);
+ outw(port+GUS_DATA_LOW, 0x0000);
+ SELECT_GUS_REG(port, GUSREG_PAN_POS);
+ outb(port+GUS_DATA_HIGH, 0x07);
+ }
+
+ /*
+ * Clear out any pending IRQs
+ */
+
+ inb(port+GUS_IRQ_STATUS);
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ inb(port+GUS_DATA_HIGH);
+ SELECT_GUS_REG(port, GUSREG_SAMPLE_CONTROL);
+ inb(port+GUS_DATA_HIGH);
+ SELECT_GUS_REG(port, GUSREG_IRQ_STATUS);
+ inb(port+GUS_DATA_HIGH);
+
+ SELECT_GUS_REG(port, GUSREG_RESET);
+ outb(port+GUS_DATA_HIGH, GUSMASK_MASTER_RESET | GUSMASK_DAC_ENABLE |
+ GUSMASK_IRQ_ENABLE);
+
+ splx(s);
+}
+
+
+static void
+gus_init_cs4231(sc)
+ struct gus_softc *sc;
+{
+ register u_short port = sc->sc_iobase;
+ u_char ctrl;
+
+ ctrl = (port & 0xf0) >> 4; /* set port address middle nibble */
+ /*
+ * The codec is a bit weird--swapped dma channels.
+ */
+ ctrl |= GUS_MAX_CODEC_ENABLE;
+ if (sc->sc_drq >= 4)
+ ctrl |= GUS_MAX_RECCHAN16;
+ if (sc->sc_recdrq >= 4)
+ ctrl |= GUS_MAX_PLAYCHAN16;
+
+ outb(port+GUS_MAX_CTRL, ctrl);
+
+ sc->sc_codec.sc_iobase = port+GUS_MAX_CODEC_BASE;
+
+ if (ad1848_probe(&sc->sc_codec) == 0) {
+ sc->sc_flags &= ~GUS_CODEC_INSTALLED;
+ } else {
+ struct ad1848_volume vol = {AUDIO_MAX_GAIN, AUDIO_MAX_GAIN};
+ struct audio_hw_if gusmax_hw_if = {
+ gusopen,
+ gusmax_close,
+ NULL, /* drain */
+ gusmax_set_in_sr,
+ gusmax_get_in_sr,
+ gusmax_set_out_sr,
+ gusmax_get_out_sr,
+
+ ad1848_query_encoding, /* query encoding */
+ gusmax_set_encoding,
+ gusmax_get_encoding,
+
+ gusmax_set_precision,
+ gusmax_get_precision,
+
+ gusmax_set_channels,
+ gusmax_get_channels,
+
+ gusmax_round_blocksize,
+
+ gusmax_set_out_port,
+ gusmax_get_out_port,
+ gusmax_set_in_port,
+ gusmax_get_in_port,
+
+ gusmax_commit_settings,
+
+ ad1848_get_silence,
+
+ gusmax_expand, /* XXX use codec */
+ mulaw_compress,
+
+ gusmax_dma_output,
+ gusmax_dma_input,
+ gusmax_halt_out_dma,
+ gusmax_halt_in_dma,
+ gusmax_cont_out_dma,
+ gusmax_cont_in_dma,
+
+ gusmax_speaker_ctl,
+
+ gus_getdev,
+ gus_setfd,
+ gusmax_mixer_set_port,
+ gusmax_mixer_get_port,
+ gusmax_mixer_query_devinfo,
+ 1, /* full-duplex */
+ 0,
+ };
+ sc->sc_flags |= GUS_CODEC_INSTALLED;
+ sc->sc_codec.parent = sc;
+ sc->sc_codec.sc_drq = sc->sc_recdrq;
+ sc->sc_codec.sc_recdrq = sc->sc_drq;
+ gus_hw_if = gusmax_hw_if;
+ /* enable line in and mic in the GUS mixer; the codec chip
+ will do the real mixing for them. */
+ sc->sc_mixcontrol &= ~GUSMASK_LINE_IN; /* 0 enables. */
+ sc->sc_mixcontrol |= GUSMASK_MIC_IN; /* 1 enables. */
+ outb(sc->sc_iobase+GUS_MIX_CONTROL, sc->sc_mixcontrol);
+
+ ad1848_attach(&sc->sc_codec);
+ /* turn on pre-MUX microphone gain. */
+ ad1848_set_mic_gain(&sc->sc_codec, &vol);
+ }
+}
+
+
+/*
+ * Return info about the audio device, for the AUDIO_GETINFO ioctl
+ */
+
+int
+gus_getdev(addr, dev)
+ void * addr;
+ struct audio_device *dev;
+{
+ *dev = gus_device;
+ return 0;
+}
+
+/*
+ * stubs (XXX)
+ */
+
+int
+gus_set_in_gain(addr, gain, balance)
+ caddr_t addr;
+ u_int gain;
+ u_char balance;
+{
+ DPRINTF(("gus_set_in_gain called\n"));
+ return 0;
+}
+
+int
+gus_get_in_gain(addr)
+ caddr_t addr;
+{
+ DPRINTF(("gus_get_in_gain called\n"));
+ return 0;
+}
+
+int
+gusmax_set_out_port(addr, port)
+ void * addr;
+ int port;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_set_out_port(sc->parent, port);
+}
+
+int
+gus_set_out_port(addr, port)
+ void * addr;
+ int port;
+{
+ register struct gus_softc *sc = addr;
+ DPRINTF(("gus_set_out_port called\n"));
+ sc->sc_out_port = port;
+
+ return 0;
+}
+
+int
+gusmax_get_out_port(addr)
+ void * addr;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_get_out_port(sc->parent);
+}
+
+int
+gus_get_out_port(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+ DPRINTF(("gus_get_out_port() called\n"));
+ return sc->sc_out_port;
+}
+
+int
+gusmax_set_in_port(addr, port)
+ void * addr;
+ int port;
+{
+ register struct ad1848_softc *sc = addr;
+ DPRINTF(("gusmax_set_in_port: %d\n", port));
+
+ switch(port) {
+ case GUSMAX_MONO_LVL:
+ port = MIC_IN_PORT;
+ break;
+ case GUSMAX_LINE_IN_LVL:
+ port = LINE_IN_PORT;
+ break;
+ case GUSMAX_DAC_LVL:
+ port = AUX1_IN_PORT;
+ break;
+ case GUSMAX_MIX_IN:
+ port = DAC_IN_PORT;
+ break;
+ default:
+ return(EINVAL);
+ /*NOTREACHED*/
+ }
+ return(ad1848_set_rec_port(sc, port));
+}
+
+int
+gusmax_get_in_port(addr)
+ void * addr;
+{
+ register struct ad1848_softc *sc = addr;
+ int port = GUSMAX_MONO_LVL;
+
+ switch(ad1848_get_rec_port(sc)) {
+ case MIC_IN_PORT:
+ port = GUSMAX_MONO_LVL;
+ break;
+ case LINE_IN_PORT:
+ port = GUSMAX_LINE_IN_LVL;
+ break;
+ case DAC_IN_PORT:
+ port = GUSMAX_MIX_IN;
+ break;
+ case AUX1_IN_PORT:
+ port = GUSMAX_DAC_LVL;
+ break;
+ }
+
+ DPRINTF(("gusmax_get_in_port: %d\n", port));
+
+ return(port);
+}
+
+int
+gus_set_in_port(addr, port)
+ void * addr;
+ int port;
+{
+ register struct gus_softc *sc = addr;
+ DPRINTF(("gus_set_in_port called\n"));
+ /*
+ * On the GUS with ICS mixer, the ADC input is after the mixer stage,
+ * so we can't set the input port.
+ *
+ * On the GUS with CS4231 codec/mixer, see gusmax_set_in_port().
+ */
+ sc->sc_in_port = port;
+
+ return 0;
+}
+
+
+int
+gus_get_in_port(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+ DPRINTF(("gus_get_in_port called\n"));
+ return sc->sc_in_port;
+}
+
+
+int
+gusmax_dma_input(addr, buf, size, callback, arg)
+ void * addr;
+ void *buf;
+ int size;
+ void (*callback)();
+ void *arg;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_dma_input(sc->parent, buf, size, callback, arg);
+}
+
+/*
+ * Start sampling the input source into the requested DMA buffer.
+ * Called at splgus(), either from top-half or from interrupt handler.
+ */
+int
+gus_dma_input(addr, buf, size, callback, arg)
+ void * addr;
+ void *buf;
+ int size;
+ void (*callback)();
+ void *arg;
+{
+ register struct gus_softc *sc = addr;
+ register u_short port = sc->sc_iobase;
+ register u_char dmac;
+ DMAPRINTF(("gus_dma_input called\n"));
+
+ /*
+ * Sample SIZE bytes of data from the card, into buffer at BUF.
+ */
+
+ if (sc->sc_precision == 16)
+ return EINVAL; /* XXX */
+
+ /* set DMA modes */
+ dmac = GUSMASK_SAMPLE_IRQ|GUSMASK_SAMPLE_START;
+ if (sc->sc_recdrq >= 4)
+ dmac |= GUSMASK_SAMPLE_DATA16;
+ if (sc->sc_encoding == AUDIO_ENCODING_ULAW ||
+ sc->sc_encoding == AUDIO_ENCODING_PCM8)
+ dmac |= GUSMASK_SAMPLE_INVBIT;
+ if (sc->sc_channels == 2)
+ dmac |= GUSMASK_SAMPLE_STEREO;
+ isa_dmastart(B_READ, (caddr_t) buf, size, sc->sc_recdrq);
+
+ DMAPRINTF(("gus_dma_input isa_dmastarted\n"));
+ sc->sc_flags |= GUS_DMAIN_ACTIVE;
+ sc->sc_dmainintr = callback;
+ sc->sc_inarg = arg;
+ sc->sc_dmaincnt = size;
+ sc->sc_dmainaddr = buf;
+
+ SELECT_GUS_REG(port, GUSREG_SAMPLE_CONTROL);
+ outb(port+GUS_DATA_HIGH, dmac); /* Go! */
+
+
+ DMAPRINTF(("gus_dma_input returning\n"));
+
+ return 0;
+}
+
+static int
+gus_dmain_intr(sc)
+ struct gus_softc *sc;
+{
+ void (*callback) __P((void *));
+ void *arg;
+
+ DMAPRINTF(("gus_dmain_intr called\n"));
+ if (sc->sc_dmainintr) {
+ isa_dmadone(B_READ, sc->sc_dmainaddr, sc->sc_dmaincnt - 1,
+ sc->sc_recdrq);
+ callback = sc->sc_dmainintr;
+ arg = sc->sc_inarg;
+
+ sc->sc_dmainaddr = 0;
+ sc->sc_dmaincnt = 0;
+ sc->sc_dmainintr = 0;
+ sc->sc_inarg = 0;
+
+ sc->sc_flags &= ~GUS_DMAIN_ACTIVE;
+ DMAPRINTF(("calling dmain_intr callback %x(%x)\n", callback, arg));
+ (*callback)(arg);
+ return 1;
+ } else {
+ DMAPRINTF(("gus_dmain_intr false?\n"));
+ return 0; /* XXX ??? */
+ }
+}
+
+int
+gusmax_halt_out_dma(addr)
+ void * addr;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_halt_out_dma(sc->parent);
+}
+
+
+int
+gusmax_halt_in_dma(addr)
+ void * addr;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_halt_in_dma(sc->parent);
+}
+
+int
+gusmax_cont_out_dma(addr)
+ void * addr;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_cont_out_dma(sc->parent);
+}
+
+int
+gusmax_cont_in_dma(addr)
+ void * addr;
+{
+ register struct ad1848_softc *sc = addr;
+ return gus_cont_in_dma(sc->parent);
+}
+
+/*
+ * Stop any DMA output. Called at splgus().
+ */
+int
+gus_halt_out_dma(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+ register u_short port = sc->sc_iobase;
+
+ DMAPRINTF(("gus_halt_out_dma called\n"));
+ /*
+ * Make sure the GUS _isn't_ setup for DMA
+ */
+
+ SELECT_GUS_REG(port, GUSREG_DMA_CONTROL);
+ outb(sc->sc_iobase+GUS_DATA_HIGH, 0);
+
+ untimeout(gus_dmaout_timeout, sc);
+ isa_dmaabort(sc->sc_drq);
+ sc->sc_flags &= ~(GUS_DMAOUT_ACTIVE|GUS_LOCKED);
+ sc->sc_dmaoutintr = 0;
+ sc->sc_outarg = 0;
+ sc->sc_dmaoutaddr = 0;
+ sc->sc_dmaoutcnt = 0;
+ sc->sc_dmabuf = 0;
+ sc->sc_bufcnt = 0;
+ sc->sc_playbuf = -1;
+ /* also stop playing */
+ gus_stop_voice(sc, GUS_VOICE_LEFT, 1);
+ gus_stop_voice(sc, GUS_VOICE_RIGHT, 0);
+
+ return 0;
+}
+
+/*
+ * Stop any DMA output. Called at splgus().
+ */
+int
+gus_halt_in_dma(addr)
+ void * addr;
+{
+ register struct gus_softc *sc = addr;
+ register u_short port = sc->sc_iobase;
+ DMAPRINTF(("gus_halt_in_dma called\n"));
+
+ /*
+ * Make sure the GUS _isn't_ setup for DMA
+ */
+
+ SELECT_GUS_REG(port, GUSREG_SAMPLE_CONTROL);
+ outb(port+GUS_DATA_HIGH,
+ inb(port+GUS_DATA_HIGH) & ~(GUSMASK_SAMPLE_START|GUSMASK_SAMPLE_IRQ));
+
+ isa_dmaabort(sc->sc_recdrq);
+ sc->sc_flags &= ~GUS_DMAIN_ACTIVE;
+ sc->sc_dmainintr = 0;
+ sc->sc_inarg = 0;
+ sc->sc_dmainaddr = 0;
+ sc->sc_dmaincnt = 0;
+
+ return 0;
+}
+
+int
+gus_cont_out_dma(addr)
+ void * addr;
+{
+ DPRINTF(("gus_cont_out_dma called\n"));
+ return EOPNOTSUPP;
+}
+
+int
+gus_cont_in_dma(addr)
+ void * addr;
+{
+ DPRINTF(("gus_cont_in_dma called\n"));
+ return EOPNOTSUPP;
+}
+
+
+static int
+gus_setfd(addr, flag)
+ void *addr;
+ int flag;
+{
+ if (gus_hw_if.full_duplex == 0)
+ return ENOTTY;
+
+ return(0); /* nothing fancy to do. */
+}
+
+static inline int
+gus_to_vol(cp, vol)
+ mixer_ctrl_t *cp;
+ struct ad1848_volume *vol;
+{
+ if (cp->un.value.num_channels == 1) {
+ vol->left = vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ return(1);
+ }
+ else if (cp->un.value.num_channels == 2) {
+ vol->left = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
+ vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
+ return(1);
+ }
+ return(0);
+}
+
+static inline int
+gus_from_vol(cp, vol)
+ mixer_ctrl_t *cp;
+ struct ad1848_volume *vol;
+{
+ if (cp->un.value.num_channels == 1) {
+ cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol->left;
+ return(1);
+ }
+ else if (cp->un.value.num_channels == 2) {
+ cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol->left;
+ cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol->right;
+ return(1);
+ }
+ return(0);
+}
+
+static int
+gusmax_mixer_get_port(addr, cp)
+ void *addr;
+ mixer_ctrl_t *cp;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ struct ad1848_volume vol;
+ u_char eq;
+ int error = EINVAL;
+
+ DPRINTF(("gusmax_mixer_get_port: port=%d\n", cp->dev));
+
+ switch (cp->dev) {
+#if 0 /* use mono level instead */
+ case GUSMAX_MIC_IN_LVL: /* Microphone */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ error = ad1848_get_mic_gain(ac, &vol);
+ if (!error)
+ gus_from_vol(cp, &vol);
+ }
+ break;
+#endif
+
+ case GUSMAX_DAC_LVL: /* dac out */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ error = ad1848_get_aux1_gain(ac, &vol);
+ if (!error)
+ gus_from_vol(cp, &vol);
+ }
+ break;
+
+ case GUSMAX_LINE_IN_LVL: /* line in */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ error = cs4231_get_linein_gain(ac, &vol);
+ if (!error)
+ gus_from_vol(cp, &vol);
+ }
+ break;
+
+ case GUSMAX_MONO_LVL: /* mono */
+ if (cp->type == AUDIO_MIXER_VALUE &&
+ cp->un.value.num_channels == 1) {
+ error = cs4231_get_mono_gain(ac, &vol);
+ if (!error)
+ gus_from_vol(cp, &vol);
+ }
+ break;
+
+ case GUSMAX_CD_LVL: /* CD */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ error = ad1848_get_aux2_gain(ac, &vol);
+ if (!error)
+ gus_from_vol(cp, &vol);
+ }
+ break;
+
+ case GUSMAX_MONITOR_LVL: /* monitor level */
+ if (cp->type == AUDIO_MIXER_VALUE &&
+ cp->un.value.num_channels == 1) {
+ error = ad1848_get_mon_gain(ac, &vol);
+ if (!error)
+ cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
+ vol.left;
+ }
+ break;
+
+ case GUSMAX_OUT_LVL: /* output level */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ error = ad1848_get_out_gain(ac, &vol);
+ if (!error)
+ gus_from_vol(cp, &vol);
+ }
+ break;
+
+ case GUSMAX_SPEAKER_LVL: /* fake speaker for mute naming */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (sc->sc_mixcontrol & GUSMASK_LINE_OUT)
+ vol.left = vol.right = AUDIO_MAX_GAIN;
+ else
+ vol.left = vol.right = AUDIO_MIN_GAIN;
+ error = 0;
+ gus_from_vol(cp, &vol);
+ }
+ break;
+
+ case GUSMAX_LINE_IN_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ac->line_mute;
+ error = 0;
+ }
+ break;
+
+
+ case GUSMAX_DAC_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ac->aux1_mute;
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_CD_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ac->aux2_mute;
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_MONO_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ac->mono_mute;
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_MONITOR_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ac->mon_mute;
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_SPEAKER_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = sc->sc_mixcontrol & GUSMASK_LINE_OUT ? 1 : 0;
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_REC_LVL: /* record level */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ error = ad1848_get_rec_gain(ac, &vol);
+ if (!error)
+ gus_from_vol(cp, &vol);
+ }
+ break;
+
+ case GUSMAX_RECORD_SOURCE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ad1848_get_rec_port(ac);
+ error = 0;
+ }
+ break;
+
+ default:
+ error = ENXIO;
+ break;
+ }
+
+ return(error);
+}
+
+static int
+gus_mixer_get_port(addr, cp)
+ void *addr;
+ mixer_ctrl_t *cp;
+{
+ register struct gus_softc *sc = addr;
+ register struct ics2101_softc *ic = &sc->sc_mixer;
+ struct ad1848_volume vol;
+ int error = EINVAL;
+ u_int mute;
+
+ DPRINTF(("gus_mixer_get_port: dev=%d type=%d\n", cp->dev, cp->type));
+
+ if (!HAS_MIXER(sc) && cp->dev > GUSICS_MASTER_MUTE)
+ return ENXIO;
+
+ switch (cp->dev) {
+
+ case GUSICS_MIC_IN_MUTE: /* Microphone */
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ if (HAS_MIXER(sc))
+ cp->un.ord = ic->sc_mute[GUSMIX_CHAN_MIC][ICSMIX_LEFT];
+ else
+ cp->un.ord =
+ sc->sc_mixcontrol & GUSMASK_MIC_IN ? 0 : 1;
+ error = 0;
+ }
+ break;
+
+ case GUSICS_LINE_IN_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ if (HAS_MIXER(sc))
+ cp->un.ord = ic->sc_mute[GUSMIX_CHAN_LINE][ICSMIX_LEFT];
+ else
+ cp->un.ord =
+ sc->sc_mixcontrol & GUSMASK_LINE_IN ? 1 : 0;
+ error = 0;
+ }
+ break;
+
+ case GUSICS_MASTER_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ if (HAS_MIXER(sc))
+ cp->un.ord = ic->sc_mute[GUSMIX_CHAN_MASTER][ICSMIX_LEFT];
+ else
+ cp->un.ord =
+ sc->sc_mixcontrol & GUSMASK_LINE_OUT ? 1 : 0;
+ error = 0;
+ }
+ break;
+
+ case GUSICS_DAC_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ic->sc_mute[GUSMIX_CHAN_DAC][ICSMIX_LEFT];
+ error = 0;
+ }
+ break;
+
+ case GUSICS_CD_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ cp->un.ord = ic->sc_mute[GUSMIX_CHAN_CD][ICSMIX_LEFT];
+ error = 0;
+ }
+ break;
+
+ case GUSICS_MASTER_LVL:
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ vol.left = ic->sc_setting[GUSMIX_CHAN_MASTER][ICSMIX_LEFT];
+ vol.right = ic->sc_setting[GUSMIX_CHAN_MASTER][ICSMIX_RIGHT];
+ if (gus_from_vol(cp, &vol))
+ error = 0;
+ }
+ break;
+
+ case GUSICS_MIC_IN_LVL: /* Microphone */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ vol.left = ic->sc_setting[GUSMIX_CHAN_MIC][ICSMIX_LEFT];
+ vol.right = ic->sc_setting[GUSMIX_CHAN_MIC][ICSMIX_RIGHT];
+ if (gus_from_vol(cp, &vol))
+ error = 0;
+ }
+ break;
+
+ case GUSICS_LINE_IN_LVL: /* line in */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ vol.left = ic->sc_setting[GUSMIX_CHAN_LINE][ICSMIX_LEFT];
+ vol.right = ic->sc_setting[GUSMIX_CHAN_LINE][ICSMIX_RIGHT];
+ if (gus_from_vol(cp, &vol))
+ error = 0;
+ }
+ break;
+
+
+ case GUSICS_CD_LVL:
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ vol.left = ic->sc_setting[GUSMIX_CHAN_CD][ICSMIX_LEFT];
+ vol.right = ic->sc_setting[GUSMIX_CHAN_CD][ICSMIX_RIGHT];
+ if (gus_from_vol(cp, &vol))
+ error = 0;
+ }
+ break;
+
+ case GUSICS_DAC_LVL: /* dac out */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ vol.left = ic->sc_setting[GUSMIX_CHAN_DAC][ICSMIX_LEFT];
+ vol.right = ic->sc_setting[GUSMIX_CHAN_DAC][ICSMIX_RIGHT];
+ if (gus_from_vol(cp, &vol))
+ error = 0;
+ }
+ break;
+
+
+ case GUSICS_RECORD_SOURCE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ /* Can't set anything else useful, sigh. */
+ cp->un.ord = 0;
+ }
+ break;
+
+ default:
+ return ENXIO;
+ /*NOTREACHED*/
+ }
+ return error;
+}
+
+static void
+gusics_master_mute(ic, mute)
+ struct ics2101_softc *ic;
+ int mute;
+{
+ ics2101_mix_mute(ic, GUSMIX_CHAN_MASTER, ICSMIX_LEFT, mute);
+ ics2101_mix_mute(ic, GUSMIX_CHAN_MASTER, ICSMIX_RIGHT, mute);
+}
+
+static void
+gusics_mic_mute(ic, mute)
+ struct ics2101_softc *ic;
+ int mute;
+{
+ ics2101_mix_mute(ic, GUSMIX_CHAN_MIC, ICSMIX_LEFT, mute);
+ ics2101_mix_mute(ic, GUSMIX_CHAN_MIC, ICSMIX_RIGHT, mute);
+}
+
+static void
+gusics_linein_mute(ic, mute)
+ struct ics2101_softc *ic;
+ int mute;
+{
+ ics2101_mix_mute(ic, GUSMIX_CHAN_LINE, ICSMIX_LEFT, mute);
+ ics2101_mix_mute(ic, GUSMIX_CHAN_LINE, ICSMIX_RIGHT, mute);
+}
+
+static void
+gusics_cd_mute(ic, mute)
+ struct ics2101_softc *ic;
+ int mute;
+{
+ ics2101_mix_mute(ic, GUSMIX_CHAN_CD, ICSMIX_LEFT, mute);
+ ics2101_mix_mute(ic, GUSMIX_CHAN_CD, ICSMIX_RIGHT, mute);
+}
+
+static void
+gusics_dac_mute(ic, mute)
+ struct ics2101_softc *ic;
+ int mute;
+{
+ ics2101_mix_mute(ic, GUSMIX_CHAN_DAC, ICSMIX_LEFT, mute);
+ ics2101_mix_mute(ic, GUSMIX_CHAN_DAC, ICSMIX_RIGHT, mute);
+}
+
+static int
+gusmax_mixer_set_port(addr, cp)
+ void *addr;
+ mixer_ctrl_t *cp;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+ struct ad1848_volume vol;
+ int error = EINVAL;
+
+ DPRINTF(("gusmax_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
+
+ switch (cp->dev) {
+#if 0
+ case GUSMAX_MIC_IN_LVL: /* Microphone */
+ if (cp->type == AUDIO_MIXER_VALUE &&
+ cp->un.value.num_channels == 1) {
+ /* XXX enable/disable pre-MUX fixed gain */
+ if (gus_to_vol(cp, &vol))
+ error = ad1848_set_mic_gain(ac, &vol);
+ }
+ break;
+#endif
+
+ case GUSMAX_DAC_LVL: /* dac out */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol))
+ error = ad1848_set_aux1_gain(ac, &vol);
+ }
+ break;
+
+ case GUSMAX_LINE_IN_LVL: /* line in */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol))
+ error = cs4231_set_linein_gain(ac, &vol);
+ }
+ break;
+
+ case GUSMAX_MONO_LVL: /* mic/mono in */
+ if (cp->type == AUDIO_MIXER_VALUE &&
+ cp->un.value.num_channels == 1) {
+ if (gus_to_vol(cp, &vol))
+ error = cs4231_set_mono_gain(ac, &vol);
+ }
+ break;
+
+ case GUSMAX_CD_LVL: /* CD: AUX2 */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol))
+ error = ad1848_set_aux2_gain(ac, &vol);
+ }
+ break;
+
+ case GUSMAX_MONITOR_LVL:
+ if (cp->type == AUDIO_MIXER_VALUE &&
+ cp->un.value.num_channels == 1) {
+ vol.left = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ error = ad1848_set_mon_gain(ac, &vol);
+ }
+ break;
+
+ case GUSMAX_OUT_LVL: /* output volume */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol))
+ error = ad1848_set_out_gain(ac, &vol);
+ }
+ break;
+
+ case GUSMAX_SPEAKER_LVL:
+ if (cp->type == AUDIO_MIXER_VALUE &&
+ cp->un.value.num_channels == 1) {
+ if (gus_to_vol(cp, &vol)) {
+ gus_speaker_ctl(sc, vol.left > AUDIO_MIN_GAIN ?
+ SPKR_ON : SPKR_OFF);
+ error = 0;
+ }
+ }
+ break;
+
+ case GUSMAX_LINE_IN_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ ac->line_mute = cp->un.ord ? 1 : 0;
+ DPRINTF(("line mute %d\n", cp->un.ord));
+ cs4231_mute_line(ac, ac->line_mute);
+ gus_linein_ctl(sc, ac->line_mute ? SPKR_OFF : SPKR_ON);
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_DAC_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ ac->aux1_mute = cp->un.ord ? 1 : 0;
+ DPRINTF(("dac mute %d\n", cp->un.ord));
+ ad1848_mute_aux1(ac, ac->aux1_mute);
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_CD_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ ac->aux2_mute = cp->un.ord ? 1 : 0;
+ DPRINTF(("cd mute %d\n", cp->un.ord));
+ ad1848_mute_aux2(ac, ac->aux2_mute);
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_MONO_MUTE: /* Microphone */
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ ac->mono_mute = cp->un.ord ? 1 : 0;
+ DPRINTF(("mono mute %d\n", cp->un.ord));
+ cs4231_mute_mono(ac, ac->mono_mute);
+ gus_mic_ctl(sc, ac->mono_mute ? SPKR_OFF : SPKR_ON);
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_MONITOR_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ ac->mon_mute = cp->un.ord ? 1 : 0;
+ DPRINTF(("mono mute %d\n", cp->un.ord));
+ cs4231_mute_monitor(ac, ac->mon_mute);
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_SPEAKER_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ gus_speaker_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
+ error = 0;
+ }
+ break;
+
+ case GUSMAX_REC_LVL: /* record level */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol))
+ error = ad1848_set_rec_gain(ac, &vol);
+ }
+ break;
+
+ case GUSMAX_RECORD_SOURCE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ error = ad1848_set_rec_port(ac, cp->un.ord);
+ }
+ break;
+
+ default:
+ return ENXIO;
+ /*NOTREACHED*/
+ }
+ return error;
+}
+
+static int
+gus_mixer_set_port(addr, cp)
+ void *addr;
+ mixer_ctrl_t *cp;
+{
+ register struct gus_softc *sc = addr;
+ register struct ics2101_softc *ic = &sc->sc_mixer;
+ struct ad1848_volume vol;
+ int error = EINVAL;
+ u_int mute;
+
+ DPRINTF(("gus_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
+
+ if (!HAS_MIXER(sc) && cp->dev > GUSICS_MASTER_MUTE)
+ return ENXIO;
+
+ switch (cp->dev) {
+
+ case GUSICS_MIC_IN_MUTE: /* Microphone */
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ DPRINTF(("mic mute %d\n", cp->un.ord));
+ if (HAS_MIXER(sc)) {
+ gusics_mic_mute(ic, cp->un.ord);
+ }
+ gus_mic_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
+ error = 0;
+ }
+ break;
+
+ case GUSICS_LINE_IN_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ DPRINTF(("linein mute %d\n", cp->un.ord));
+ if (HAS_MIXER(sc)) {
+ gusics_linein_mute(ic, cp->un.ord);
+ }
+ gus_linein_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
+ error = 0;
+ }
+ break;
+
+ case GUSICS_MASTER_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ DPRINTF(("master mute %d\n", cp->un.ord));
+ if (HAS_MIXER(sc)) {
+ gusics_master_mute(ic, cp->un.ord);
+ }
+ gus_speaker_ctl(sc, cp->un.ord ? SPKR_OFF : SPKR_ON);
+ error = 0;
+ }
+ break;
+
+ case GUSICS_DAC_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ gusics_dac_mute(ic, cp->un.ord);
+ error = 0;
+ }
+ break;
+
+ case GUSICS_CD_MUTE:
+ if (cp->type == AUDIO_MIXER_ENUM) {
+ gusics_cd_mute(ic, cp->un.ord);
+ error = 0;
+ }
+ break;
+
+ case GUSICS_MASTER_LVL:
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol)) {
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MASTER,
+ ICSMIX_LEFT,
+ vol.left);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MASTER,
+ ICSMIX_RIGHT,
+ vol.right);
+ error = 0;
+ }
+ }
+ break;
+
+ case GUSICS_MIC_IN_LVL: /* Microphone */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol)) {
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MIC,
+ ICSMIX_LEFT,
+ vol.left);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MIC,
+ ICSMIX_RIGHT,
+ vol.right);
+ error = 0;
+ }
+ }
+ break;
+
+ case GUSICS_LINE_IN_LVL: /* line in */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol)) {
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_LINE,
+ ICSMIX_LEFT,
+ vol.left);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_LINE,
+ ICSMIX_RIGHT,
+ vol.right);
+ error = 0;
+ }
+ }
+ break;
+
+
+ case GUSICS_CD_LVL:
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol)) {
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_CD,
+ ICSMIX_LEFT,
+ vol.left);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_CD,
+ ICSMIX_RIGHT,
+ vol.right);
+ error = 0;
+ }
+ }
+ break;
+
+ case GUSICS_DAC_LVL: /* dac out */
+ if (cp->type == AUDIO_MIXER_VALUE) {
+ if (gus_to_vol(cp, &vol)) {
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_DAC,
+ ICSMIX_LEFT,
+ vol.left);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_DAC,
+ ICSMIX_RIGHT,
+ vol.right);
+ error = 0;
+ }
+ }
+ break;
+
+
+ case GUSICS_RECORD_SOURCE:
+ if (cp->type == AUDIO_MIXER_ENUM && cp->un.ord == 0) {
+ /* Can't set anything else useful, sigh. */
+ error = 0;
+ }
+ break;
+
+ default:
+ return ENXIO;
+ /*NOTREACHED*/
+ }
+ return error;
+}
+
+static int
+gusmax_mixer_query_devinfo(addr, dip)
+ void *addr;
+ register mixer_devinfo_t *dip;
+{
+ register struct ad1848_softc *ac = addr;
+ register struct gus_softc *sc = ac->parent;
+
+ DPRINTF(("gusmax_query_devinfo: index=%d\n", dip->index));
+
+ switch(dip->index) {
+ case GUSMAX_MIX_IN: /* mixed MUX input */
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->prev = dip->next = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioNmixerout);
+ dip->un.e.num_mem = 0; /* XXX */
+ break;
+
+#if 0
+ case GUSMAX_MIC_IN_LVL: /* Microphone */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSMAX_MIC_IN_MUTE;
+ strcpy(dip->label.name, AudioNmicrophone);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+#endif
+
+ case GUSMAX_MONO_LVL: /* mono/microphone mixer */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSMAX_MONO_MUTE;
+ strcpy(dip->label.name, AudioNmicrophone);
+ dip->un.v.num_channels = 1;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSMAX_DAC_LVL: /* dacout */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSMAX_DAC_MUTE;
+ strcpy(dip->label.name, AudioNdac);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSMAX_LINE_IN_LVL: /* line */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSMAX_LINE_IN_MUTE;
+ strcpy(dip->label.name, AudioNline);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSMAX_CD_LVL: /* cd */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSMAX_CD_MUTE;
+ strcpy(dip->label.name, AudioNcd);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+
+ case GUSMAX_MONITOR_LVL: /* monitor level */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_MONITOR_CLASS;
+ dip->next = GUSMAX_MONITOR_MUTE;
+ dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioNmonitor);
+ dip->un.v.num_channels = 1;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSMAX_OUT_LVL: /* cs4231 output volume: not useful? */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_MONITOR_CLASS;
+ dip->prev = dip->next = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioNoutput);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSMAX_SPEAKER_LVL: /* fake speaker volume */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_MONITOR_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSMAX_SPEAKER_MUTE;
+ strcpy(dip->label.name, AudioNspeaker);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSMAX_LINE_IN_MUTE:
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSMAX_LINE_IN_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSMAX_DAC_MUTE:
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSMAX_DAC_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSMAX_CD_MUTE:
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSMAX_CD_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSMAX_MONO_MUTE:
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSMAX_MONO_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSMAX_MONITOR_MUTE:
+ dip->mixer_class = GUSMAX_OUTPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSMAX_MONITOR_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSMAX_SPEAKER_MUTE:
+ dip->mixer_class = GUSMAX_OUTPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSMAX_SPEAKER_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ mute:
+ strcpy(dip->label.name, AudioNmute);
+ dip->un.e.num_mem = 2;
+ strcpy(dip->un.e.member[0].label.name, AudioNoff);
+ dip->un.e.member[0].ord = 0;
+ strcpy(dip->un.e.member[1].label.name, AudioNon);
+ dip->un.e.member[1].ord = 1;
+ break;
+
+ case GUSMAX_REC_LVL: /* record level */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSMAX_RECORD_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSMAX_RECORD_SOURCE;
+ strcpy(dip->label.name, AudioNrecord);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSMAX_RECORD_SOURCE:
+ dip->mixer_class = GUSMAX_RECORD_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSMAX_REC_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioNsource);
+ dip->un.e.num_mem = 4;
+ strcpy(dip->un.e.member[0].label.name, AudioNoutput);
+ dip->un.e.member[0].ord = GUSMAX_MIX_IN;
+ strcpy(dip->un.e.member[1].label.name, AudioNmicrophone);
+ dip->un.e.member[1].ord = GUSMAX_MONO_LVL;
+ strcpy(dip->un.e.member[2].label.name, AudioNdac);
+ dip->un.e.member[2].ord = GUSMAX_DAC_LVL;
+ strcpy(dip->un.e.member[3].label.name, AudioNline);
+ dip->un.e.member[3].ord = GUSMAX_LINE_IN_LVL;
+ break;
+
+ case GUSMAX_INPUT_CLASS: /* input class descriptor */
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = GUSMAX_INPUT_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCInputs);
+ break;
+
+ case GUSMAX_OUTPUT_CLASS: /* output class descriptor */
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = GUSMAX_OUTPUT_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCOutputs);
+ break;
+
+ case GUSMAX_MONITOR_CLASS: /* monitor class descriptor */
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = GUSMAX_MONITOR_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCMonitor);
+ break;
+
+ case GUSMAX_RECORD_CLASS: /* record source class */
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = GUSMAX_RECORD_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCRecord);
+ break;
+
+ default:
+ return ENXIO;
+ /*NOTREACHED*/
+ }
+ DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
+ return 0;
+}
+
+static int
+gus_mixer_query_devinfo(addr, dip)
+ void *addr;
+ register mixer_devinfo_t *dip;
+{
+ register struct gus_softc *sc = addr;
+
+ DPRINTF(("gusmax_query_devinfo: index=%d\n", dip->index));
+
+ if (!HAS_MIXER(sc) && dip->index > GUSICS_MASTER_MUTE)
+ return ENXIO;
+
+ switch(dip->index) {
+
+ case GUSICS_MIC_IN_LVL: /* Microphone */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSICS_MIC_IN_MUTE;
+ strcpy(dip->label.name, AudioNmicrophone);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSICS_LINE_IN_LVL: /* line */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSICS_LINE_IN_MUTE;
+ strcpy(dip->label.name, AudioNline);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSICS_CD_LVL: /* cd */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSICS_CD_MUTE;
+ strcpy(dip->label.name, AudioNcd);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSICS_DAC_LVL: /* dacout */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSICS_DAC_MUTE;
+ strcpy(dip->label.name, AudioNdac);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+ case GUSICS_MASTER_LVL: /* master output */
+ dip->type = AUDIO_MIXER_VALUE;
+ dip->mixer_class = GUSICS_OUTPUT_CLASS;
+ dip->prev = AUDIO_MIXER_LAST;
+ dip->next = GUSICS_MASTER_MUTE;
+ strcpy(dip->label.name, AudioNvolume);
+ dip->un.v.num_channels = 2;
+ strcpy(dip->un.v.units.name, AudioNvolume);
+ break;
+
+
+ case GUSICS_LINE_IN_MUTE:
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSICS_LINE_IN_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSICS_DAC_MUTE:
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSICS_DAC_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSICS_CD_MUTE:
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSICS_CD_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSICS_MIC_IN_MUTE:
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSICS_MIC_IN_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+ goto mute;
+
+ case GUSICS_MASTER_MUTE:
+ dip->mixer_class = GUSICS_OUTPUT_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = GUSICS_MASTER_LVL;
+ dip->next = AUDIO_MIXER_LAST;
+mute:
+ strcpy(dip->label.name, AudioNmute);
+ dip->un.e.num_mem = 2;
+ strcpy(dip->un.e.member[0].label.name, AudioNoff);
+ dip->un.e.member[0].ord = 0;
+ strcpy(dip->un.e.member[1].label.name, AudioNon);
+ dip->un.e.member[1].ord = 1;
+ break;
+
+ case GUSICS_RECORD_SOURCE:
+ dip->mixer_class = GUSICS_RECORD_CLASS;
+ dip->type = AUDIO_MIXER_ENUM;
+ dip->prev = dip->next = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioNsource);
+ dip->un.e.num_mem = 1;
+ strcpy(dip->un.e.member[0].label.name, AudioNoutput);
+ dip->un.e.member[0].ord = GUSICS_MASTER_LVL;
+ break;
+
+ case GUSICS_INPUT_CLASS:
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = GUSICS_INPUT_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCInputs);
+ break;
+
+ case GUSICS_OUTPUT_CLASS:
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = GUSICS_OUTPUT_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCOutputs);
+ break;
+
+ case GUSICS_RECORD_CLASS:
+ dip->type = AUDIO_MIXER_CLASS;
+ dip->mixer_class = GUSICS_RECORD_CLASS;
+ dip->next = dip->prev = AUDIO_MIXER_LAST;
+ strcpy(dip->label.name, AudioCRecord);
+ break;
+
+ default:
+ return ENXIO;
+ /*NOTREACHED*/
+ }
+ DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
+ return 0;
+}
+
+static int
+gus_query_encoding(addr, fp)
+ void *addr;
+ struct audio_encoding *fp;
+{
+ register struct gus_softc *sc = addr;
+
+ switch (fp->index) {
+ case 0:
+ strcpy(fp->name, AudioEmulaw);
+ fp->format_id = AUDIO_ENCODING_ULAW;
+ break;
+ case 1:
+ strcpy(fp->name, AudioEpcm16);
+ fp->format_id = AUDIO_ENCODING_PCM16;
+ break;
+ case 2:
+ strcpy(fp->name, AudioEpcm8);
+ fp->format_id = AUDIO_ENCODING_PCM8;
+ break;
+ default:
+ return(EINVAL);
+ /*NOTREACHED*/
+ }
+ return (0);
+}
+
+/*
+ * Setup the ICS mixer in "transparent" mode: reset everything to a sensible
+ * level. Levels as suggested by GUS SDK code.
+ */
+
+static void
+gus_init_ics2101(sc)
+ struct gus_softc *sc;
+{
+ register u_short port = sc->sc_iobase;
+ register struct ics2101_softc *ic = &sc->sc_mixer;
+ sc->sc_mixer.sc_selio = port+GUS_MIXER_SELECT;
+ sc->sc_mixer.sc_dataio = port+GUS_MIXER_DATA;
+ sc->sc_mixer.sc_flags = (sc->sc_revision == 5) ? ICS_FLIP : 0;
+
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MIC,
+ ICSMIX_LEFT,
+ ICSMIX_MIN_ATTN);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MIC,
+ ICSMIX_RIGHT,
+ ICSMIX_MIN_ATTN);
+ /*
+ * Start with microphone muted by the mixer...
+ */
+ gusics_mic_mute(ic, 1);
+
+ /* ... and enabled by the GUS master mix control */
+ gus_mic_ctl(sc, SPKR_ON);
+
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_LINE,
+ ICSMIX_LEFT,
+ ICSMIX_MIN_ATTN);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_LINE,
+ ICSMIX_RIGHT,
+ ICSMIX_MIN_ATTN);
+
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_CD,
+ ICSMIX_LEFT,
+ ICSMIX_MIN_ATTN);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_CD,
+ ICSMIX_RIGHT,
+ ICSMIX_MIN_ATTN);
+
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_DAC,
+ ICSMIX_LEFT,
+ ICSMIX_MIN_ATTN);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_DAC,
+ ICSMIX_RIGHT,
+ ICSMIX_MIN_ATTN);
+
+ ics2101_mix_attenuate(ic,
+ ICSMIX_CHAN_4,
+ ICSMIX_LEFT,
+ ICSMIX_MAX_ATTN);
+ ics2101_mix_attenuate(ic,
+ ICSMIX_CHAN_4,
+ ICSMIX_RIGHT,
+ ICSMIX_MAX_ATTN);
+
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MASTER,
+ ICSMIX_LEFT,
+ ICSMIX_MIN_ATTN);
+ ics2101_mix_attenuate(ic,
+ GUSMIX_CHAN_MASTER,
+ ICSMIX_RIGHT,
+ ICSMIX_MIN_ATTN);
+ /* unmute other stuff: */
+ gusics_cd_mute(ic, 0);
+ gusics_dac_mute(ic, 0);
+ gusics_linein_mute(ic, 0);
+ return;
+}
+
+
+#endif /* NGUS */