/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software developed for The NetBSD Foundation
* by Andrew Doran.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
/*
* Copyright (c) 1999, 2000, 2004 Klaus J. Klein
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
/*
* XXX Work around the 24-bit implementation limit of the Audio 1 DMA
* XXX engine by allocating through the ISA DMA tag.
*/
#if defined(amd64) || defined(i386)
#include <dev/isa/isavar.h>
#endif
/* Set mixer regs to something reasonable, needs work. */
sc->sc_recmon = sc->sc_spatializer = sc->sc_mvmute = 0;
eso_set_monooutsrc(sc, ESO_MIXREG_MPM_MOMUTE);
eso_set_monoinbypass(sc, 0);
eso_set_preamp(sc, 1);
for (idx = 0; idx < ESO_NGAINDEVS; idx++) {
int v;
switch (idx) {
case ESO_MIC_PLAY_VOL:
case ESO_LINE_PLAY_VOL:
case ESO_CD_PLAY_VOL:
case ESO_MONO_PLAY_VOL:
case ESO_AUXB_PLAY_VOL:
case ESO_DAC_REC_VOL:
case ESO_LINE_REC_VOL:
case ESO_SYNTH_REC_VOL:
case ESO_CD_REC_VOL:
case ESO_MONO_REC_VOL:
case ESO_AUXB_REC_VOL:
case ESO_SPATIALIZER:
v = 0;
break;
case ESO_MASTER_VOL:
v = ESO_GAIN_TO_6BIT(AUDIO_MAX_GAIN / 2);
break;
default:
v = ESO_GAIN_TO_4BIT(AUDIO_MAX_GAIN / 2);
break;
}
sc->sc_gain[idx][ESO_LEFT] = sc->sc_gain[idx][ESO_RIGHT] = v;
eso_set_gain(sc, idx);
}
eso_set_recsrc(sc, ESO_MIXREG_ERS_MIC);
mutex_spin_exit(&sc->sc_intr_lock);
/* Map and establish the interrupt. */
if (pci_intr_map(pa, &ih)) {
aprint_error_dev(sc->sc_dev, "couldn't map interrupt\n");
return;
}
/*
* Set up the DDMA Control register; a suitable I/O region has been
* supposedly mapped in the VC base address register.
*
* The Solo-1 has an ... interesting silicon bug that causes it to
* not respond to I/O space accesses to the Audio 1 DMA controller
* if the latter's mapping base address is aligned on a 1K boundary.
* As a consequence, it is quite possible for the mapping provided
* in the VC BAR to be useless. To work around this, we defer this
* part until all autoconfiguration on our parent bus is completed
* and then try to map it ourselves in fulfillment of the constraint.
*
* According to the register map we may write to the low 16 bits
* only, but experimenting has shown we're safe.
* -kjk
*/
if (ESO_VALID_DDMAC_BASE(vcbase)) {
pci_conf_write(pa->pa_pc, pa->pa_tag, ESO_PCI_DDMAC,
vcbase | ESO_PCI_DDMAC_DE);
sc->sc_dmac_configured = 1;
aprint_normal_dev(sc->sc_dev,
"mapping Audio 1 DMA using VC I/O space at 0x%lx\n",
(unsigned long)vcbase);
} else {
DPRINTF(("%s: VC I/O space at 0x%lx not suitable, deferring\n",
device_xname(sc->sc_dev), (unsigned long)vcbase));
sc->sc_pa = *pa;
config_defer(self, eso_defer);
}
sc = device_private(self);
pa = &sc->sc_pa;
aprint_normal_dev(sc->sc_dev, "");
/*
* This is outright ugly, but since we must not make assumptions
* on the underlying allocator's behaviour it's the most straight-
* forward way to implement it. Note that we skip over the first
* 1K region, which is typically occupied by an attached ISA bus.
*/
mutex_enter(&sc->sc_lock);
for (start = 0x0400; start < 0xffff; start += 0x0400) {
if (bus_space_alloc(sc->sc_iot,
start + sc->sc_vcsize, start + 0x0400 - 1,
sc->sc_vcsize, sc->sc_vcsize, 0, 0, &addr,
&sc->sc_dmac_ioh) != 0)
continue;
mutex_spin_enter(&sc->sc_intr_lock);
pci_conf_write(pa->pa_pc, pa->pa_tag, ESO_PCI_DDMAC,
addr | ESO_PCI_DDMAC_DE);
mutex_spin_exit(&sc->sc_intr_lock);
sc->sc_dmac_iot = sc->sc_iot;
sc->sc_dmac_configured = 1;
aprint_normal("mapping Audio 1 DMA using I/O space at 0x%lx\n",
(unsigned long)addr);
if (sc->sc_rintr)
sc->sc_rintr(sc->sc_rarg);
else
cv_broadcast(&sc->sc_rcv);
}
if (irqctl & ESO_IO_IRQCTL_A2IRQ) {
/*
* Clear the A2 IRQ latch: the cached value reflects the
* current DAC settings with the IRQ latch bit not set.
*/
eso_write_mixreg(sc, ESO_MIXREG_A2C2, sc->sc_a2c2);
if (sc->sc_pintr)
sc->sc_pintr(sc->sc_parg);
else
cv_broadcast(&sc->sc_pcv);
}
/*
* Raise a flag to cause a lazy update of the in-softc gain
* values the next time the software mixer is read to keep
* interrupt service cost low. ~0 cannot occur otherwise
* as the master volume has a precision of 6 bits only.
*/
sc->sc_gain[ESO_MASTER_VOL][ESO_LEFT] = (uint8_t)~0;
}
/* Perform a software reset, including DMA FIFOs. */
static int
eso_reset(struct eso_softc *sc)
{
int i;
bus_space_write_1(sc->sc_sb_iot, sc->sc_sb_ioh, ESO_SB_RESET,
ESO_SB_RESET_SW | ESO_SB_RESET_FIFO);
/* `Delay' suggested in the data sheet. */
(void)bus_space_read_1(sc->sc_sb_iot, sc->sc_sb_ioh, ESO_SB_STATUS);
bus_space_write_1(sc->sc_sb_iot, sc->sc_sb_ioh, ESO_SB_RESET, 0);
/* Wait for reset to take effect. */
for (i = 0; i < ESO_RESET_TIMEOUT; i++) {
/* Poll for data to become available. */
if ((bus_space_read_1(sc->sc_sb_iot, sc->sc_sb_ioh,
ESO_SB_RBSR) & ESO_SB_RBSR_RDAV) != 0 &&
bus_space_read_1(sc->sc_sb_iot, sc->sc_sb_ioh,
ESO_SB_RDR) == ESO_SB_RDR_RESETMAGIC) {
/* We use a few fixed rate which doesn't have rounding error. */
switch (p->sample_rate) {
case 8000:
case 48000:
srg = (128 - ESO_CLK1 / p->sample_rate);
srg |= ESO_CLK1_SELECT;
break;
case 22050:
case 44100:
srg = (128 - ESO_CLK0 / p->sample_rate);
break;
default:
/* NOTREACHED */
return EINVAL;
}
/* Roll-off frequency of 87%, as in the ES1888 driver. */
fltdiv = 256 - 200279L / p->sample_rate;
/*
* Disable auto-initialize DMA, allowing the FIFO to drain and then
* stop. The interrupt callback pointer is cleared at this
* point so that an outstanding FIFO interrupt for the remaining data
* will be acknowledged without further processing.
*
* This does not immediately `abort' an operation in progress (c.f.
* audio(9)) but is the method to leave the FIFO behind in a clean
* state with the least hair. (Besides, that item needs to be
* rephrased for trigger_*()-based DMA environments.)
*/
eso_write_mixreg(sc, ESO_MIXREG_A2C1,
ESO_MIXREG_A2C1_FIFOENB | ESO_MIXREG_A2C1_DMAENB);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ESO_IO_A2DMAM,
ESO_IO_A2DMAM_DMAENB);
static int
eso_set_port(void *hdl, mixer_ctrl_t *cp)
{
struct eso_softc *sc;
unsigned int lgain, rgain;
uint8_t tmp;
int error;
sc = hdl;
error = 0;
mutex_spin_enter(&sc->sc_intr_lock);
switch (cp->dev) {
case ESO_DAC_PLAY_VOL:
case ESO_MIC_PLAY_VOL:
case ESO_LINE_PLAY_VOL:
case ESO_SYNTH_PLAY_VOL:
case ESO_CD_PLAY_VOL:
case ESO_AUXB_PLAY_VOL:
case ESO_RECORD_VOL:
case ESO_DAC_REC_VOL:
case ESO_MIC_REC_VOL:
case ESO_LINE_REC_VOL:
case ESO_SYNTH_REC_VOL:
case ESO_CD_REC_VOL:
case ESO_AUXB_REC_VOL:
if (cp->type != AUDIO_MIXER_VALUE) {
error = EINVAL;
break;
}
/*
* Stereo-capable mixer ports: if we get a single-channel
* gain value passed in, then we duplicate it to both left
* and right channels.
*/
switch (cp->un.value.num_channels) {
case 1:
lgain = rgain = ESO_GAIN_TO_4BIT(
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
break;
case 2:
lgain = ESO_GAIN_TO_4BIT(
cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]);
rgain = ESO_GAIN_TO_4BIT(
cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]);
break;
default:
error = EINVAL;
break;
}
static int
eso_get_port(void *hdl, mixer_ctrl_t *cp)
{
struct eso_softc *sc;
sc = hdl;
mutex_spin_enter(&sc->sc_intr_lock);
switch (cp->dev) {
case ESO_MASTER_VOL:
/* Reload from mixer after hardware volume control use. */
if (sc->sc_gain[cp->dev][ESO_LEFT] == (uint8_t)~0)
eso_reload_master_vol(sc);
/* FALLTHROUGH */
case ESO_DAC_PLAY_VOL:
case ESO_MIC_PLAY_VOL:
case ESO_LINE_PLAY_VOL:
case ESO_SYNTH_PLAY_VOL:
case ESO_CD_PLAY_VOL:
case ESO_AUXB_PLAY_VOL:
case ESO_RECORD_VOL:
case ESO_DAC_REC_VOL:
case ESO_MIC_REC_VOL:
case ESO_LINE_REC_VOL:
case ESO_SYNTH_REC_VOL:
case ESO_CD_REC_VOL:
case ESO_AUXB_REC_VOL:
/*
* Stereo-capable ports: if a single-channel query is made,
* just return the left channel's value (since single-channel
* settings themselves are applied to both channels).
*/
switch (cp->un.value.num_channels) {
case 1:
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
sc->sc_gain[cp->dev][ESO_LEFT];
break;
case 2:
cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
sc->sc_gain[cp->dev][ESO_LEFT];
cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
sc->sc_gain[cp->dev][ESO_RIGHT];
break;
default:
break;
}
break;
case ESO_MONO_PLAY_VOL:
case ESO_PCSPEAKER_VOL:
case ESO_MONO_REC_VOL:
case ESO_SPATIALIZER:
if (cp->un.value.num_channels != 1) {
break;
}
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
sc->sc_gain[cp->dev][ESO_LEFT];
break;
case ESO_RECORD_MONITOR:
cp->un.ord = sc->sc_recmon;
break;
case ESO_RECORD_SOURCE:
cp->un.ord = sc->sc_recsrc;
break;
case ESO_MONOOUT_SOURCE:
cp->un.ord = sc->sc_monooutsrc;
break;
case ESO_MONOIN_BYPASS:
cp->un.ord = sc->sc_monoinbypass;
break;
case ESO_SPATIALIZER_ENABLE:
cp->un.ord = sc->sc_spatializer;
break;
case ESO_MIC_PREAMP:
cp->un.ord = sc->sc_preamp;
break;
case ESO_MASTER_MUTE:
/* Reload from mixer after hardware volume control use. */
if (sc->sc_gain[ESO_MASTER_VOL][ESO_LEFT] == (uint8_t)~0)
eso_reload_master_vol(sc);
cp->un.ord = sc->sc_mvmute;
break;
default:
break;
}
mutex_spin_exit(&sc->sc_intr_lock);
return 0;
}
static int
eso_query_devinfo(void *hdl, mixer_devinfo_t *dip)
{
static void *
eso_allocm(void *hdl, int direction, size_t size)
{
struct eso_softc *sc;
struct eso_dma *ed;
size_t boundary;
int error;
sc = hdl;
ed = kmem_alloc(sizeof (*ed), KM_SLEEP);
/*
* Apparently the Audio 1 DMA controller's current address
* register can't roll over a 64K address boundary, so we have to
* take care of that ourselves. Similarly, the Audio 2 DMA
* controller needs a 1M address boundary.
*/
if (direction == AUMODE_RECORD)
boundary = 0x10000;
else
boundary = 0x100000;
/*
* XXX Work around allocation problems for Audio 1, which
* XXX implements the 24 low address bits only, with
* XXX machine-specific DMA tag use.
*/
#ifdef alpha
/*
* XXX Force allocation through the (ISA) SGMAP.
*/
if (direction == AUMODE_RECORD)
ed->ed_dmat = alphabus_dma_get_tag(sc->sc_dmat, ALPHA_BUS_ISA);
else
#elif defined(amd64) || defined(i386)
/*
* XXX Force allocation through the ISA DMA tag.
*/
if (direction == AUMODE_RECORD)
ed->ed_dmat = &isa_bus_dma_tag;
else
#endif
ed->ed_dmat = sc->sc_dmat;
/*
* The playback DMA buffer size on the Solo-1 is limited to 0xfff0
* bytes. This is because IO_A2DMAC is a two byte value
* indicating the literal byte count, and the 4 least significant
* bits are read-only. Zero is not used as a special case for
* 0x10000.
*
* For recording, DMAC_DMAC is the byte count - 1, so 0x10000 can
* be represented.
*/
maxsize = (direction == AUMODE_PLAY) ? 0xfff0 : 0x10000;
if (bufsize > maxsize)
bufsize = maxsize;
return bufsize;
}
/* ARGSUSED */
static int
eso_get_props(void *hdl)
{
/*
* If we failed to configure the Audio 1 DMA controller, bail here
* while retaining availability of the DAC direction (in Audio 2).
*/
if (!sc->sc_dmac_configured)
return EIO;
for (i = 0; i < di.un.e.num_mem; i++) {
if (preamp == di.un.e.member[i].ord) {
mpm = eso_read_mixreg(sc, ESO_MIXREG_MPM);
mpm &= ~(ESO_MIXREG_MPM_PREAMP | ESO_MIXREG_MPM_RESV0);
mpm |= (preamp ? ESO_MIXREG_MPM_PREAMP : 0);
eso_write_mixreg(sc, ESO_MIXREG_MPM, mpm);
sc->sc_preamp = preamp;
return 0;
}
}
return EINVAL;
}
/*
* Reload Master Volume and Mute values in softc from mixer; used when
* those have previously been invalidated by use of hardware volume controls.
*/
static void
eso_reload_master_vol(struct eso_softc *sc)
{
uint8_t mv;
KASSERT(mutex_owned(&sc->sc_intr_lock));
mv = eso_read_mixreg(sc, ESO_MIXREG_LMVM);
sc->sc_gain[ESO_MASTER_VOL][ESO_LEFT] =
(mv & ~ESO_MIXREG_LMVM_MUTE) << 2;
mv = eso_read_mixreg(sc, ESO_MIXREG_LMVM);
sc->sc_gain[ESO_MASTER_VOL][ESO_RIGHT] =
(mv & ~ESO_MIXREG_RMVM_MUTE) << 2;
/* Currently both channels are muted simultaneously; either is OK. */
sc->sc_mvmute = (mv & ESO_MIXREG_RMVM_MUTE) != 0;
}
switch (port) {
case ESO_DAC_PLAY_VOL:
mixreg = ESO_MIXREG_PVR_A2;
break;
case ESO_MIC_PLAY_VOL:
mixreg = ESO_MIXREG_PVR_MIC;
break;
case ESO_LINE_PLAY_VOL:
mixreg = ESO_MIXREG_PVR_LINE;
break;
case ESO_SYNTH_PLAY_VOL:
mixreg = ESO_MIXREG_PVR_SYNTH;
break;
case ESO_CD_PLAY_VOL:
mixreg = ESO_MIXREG_PVR_CD;
break;
case ESO_AUXB_PLAY_VOL:
mixreg = ESO_MIXREG_PVR_AUXB;
break;
case ESO_DAC_REC_VOL:
mixreg = ESO_MIXREG_RVR_A2;
break;
case ESO_MIC_REC_VOL:
mixreg = ESO_MIXREG_RVR_MIC;
break;
case ESO_LINE_REC_VOL:
mixreg = ESO_MIXREG_RVR_LINE;
break;
case ESO_SYNTH_REC_VOL:
mixreg = ESO_MIXREG_RVR_SYNTH;
break;
case ESO_CD_REC_VOL:
mixreg = ESO_MIXREG_RVR_CD;
break;
case ESO_AUXB_REC_VOL:
mixreg = ESO_MIXREG_RVR_AUXB;
break;
case ESO_MONO_PLAY_VOL:
mixreg = ESO_MIXREG_PVR_MONO;
break;
case ESO_MONO_REC_VOL:
mixreg = ESO_MIXREG_RVR_MONO;
break;
case ESO_PCSPEAKER_VOL:
/* Special case - only 3-bit, mono, and reserved bits. */
tmp = eso_read_mixreg(sc, ESO_MIXREG_PCSVR);
tmp &= ESO_MIXREG_PCSVR_RESV;
/* Map bits 7:5 -> 2:0. */
tmp |= (sc->sc_gain[port][ESO_LEFT] >> 5);
eso_write_mixreg(sc, ESO_MIXREG_PCSVR, tmp);
return;
case ESO_MASTER_VOL:
/* Special case - separate regs, and 6-bit precision. */
/* Map bits 7:2 -> 5:0, reflect mute settings. */
eso_write_mixreg(sc, ESO_MIXREG_LMVM,
(sc->sc_gain[port][ESO_LEFT] >> 2) |
(sc->sc_mvmute ? ESO_MIXREG_LMVM_MUTE : 0x00));
eso_write_mixreg(sc, ESO_MIXREG_RMVM,
(sc->sc_gain[port][ESO_RIGHT] >> 2) |
(sc->sc_mvmute ? ESO_MIXREG_RMVM_MUTE : 0x00));
return;
case ESO_SPATIALIZER:
/* Special case - only `mono', and higher precision. */
eso_write_mixreg(sc, ESO_MIXREG_SPATLVL,
sc->sc_gain[port][ESO_LEFT]);
return;
case ESO_RECORD_VOL:
/* Very Special case, controller register. */
eso_write_ctlreg(sc, ESO_CTLREG_RECLVL,ESO_4BIT_GAIN_TO_STEREO(
sc->sc_gain[port][ESO_LEFT], sc->sc_gain[port][ESO_RIGHT]));
return;
default:
#ifdef DIAGNOSTIC
panic("eso_set_gain: bad port %u", port);
/* NOTREACHED */
#else
return;
#endif
}