851de295eb
pci_attach_args *" instead of from four separate parameters which in all cases were extracted from the same "struct pci_attach_args". This both simplifies the driver api, and allows for alternate PCI interrupt mapping schemes, such as one using the tables described in the Intel Multiprocessor Spec which describe interrupt wirings for devices behind pci-pci bridges based on the device's location rather the bridge's location. Tested on alpha and i386; welcome to 1.5Q
1808 lines
44 KiB
C
1808 lines
44 KiB
C
/* $NetBSD: eap.c,v 1.42 2000/12/28 22:59:12 sommerfeld Exp $ */
|
|
/* $OpenBSD: eap.c,v 1.6 1999/10/05 19:24:42 csapuntz Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Lennart Augustsson <augustss@netbsd.org> and Charles M. Hannum.
|
|
*
|
|
* 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 the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Debugging: Andreas Gustafsson <gson@araneus.fi>
|
|
* Testing: Chuck Cranor <chuck@maria.wustl.edu>
|
|
* Phil Nelson <phil@cs.wwu.edu>
|
|
*
|
|
* ES1371/AC97: Ezra Story <ezy@panix.com>
|
|
*/
|
|
|
|
/*
|
|
* Ensoniq ES1370 + AK4531 and ES1371/ES1373 + AC97
|
|
*
|
|
* Documentation links:
|
|
*
|
|
* ftp://ftp.alsa-project.org/pub/manuals/ensoniq/
|
|
* ftp://ftp.alsa-project.org/pub/manuals/asahi_kasei/4531.pdf
|
|
* ftp://download.intel.com/ial/scalableplatforms/audio/ac97r21.pdf
|
|
*/
|
|
|
|
#include "midi.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/device.h>
|
|
#include <sys/proc.h>
|
|
|
|
#include <dev/pci/pcidevs.h>
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
#include <sys/audioio.h>
|
|
#include <dev/audio_if.h>
|
|
#include <dev/midi_if.h>
|
|
#include <dev/mulaw.h>
|
|
#include <dev/auconv.h>
|
|
#include <dev/ic/ac97var.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/pci/eapreg.h>
|
|
|
|
#define PCI_CBIO 0x10
|
|
|
|
/* Debug */
|
|
#ifdef AUDIO_DEBUG
|
|
#define DPRINTF(x) if (eapdebug) printf x
|
|
#define DPRINTFN(n,x) if (eapdebug>(n)) printf x
|
|
int eapdebug = 0;
|
|
#else
|
|
#define DPRINTF(x)
|
|
#define DPRINTFN(n,x)
|
|
#endif
|
|
|
|
int eap_match __P((struct device *, struct cfdata *, void *));
|
|
void eap_attach __P((struct device *, struct device *, void *));
|
|
int eap_intr __P((void *));
|
|
|
|
struct eap_dma {
|
|
bus_dmamap_t map;
|
|
caddr_t addr;
|
|
bus_dma_segment_t segs[1];
|
|
int nsegs;
|
|
size_t size;
|
|
struct eap_dma *next;
|
|
};
|
|
|
|
#define DMAADDR(p) ((p)->map->dm_segs[0].ds_addr)
|
|
#define KERNADDR(p) ((void *)((p)->addr))
|
|
|
|
struct eap_softc {
|
|
struct device sc_dev; /* base device */
|
|
void *sc_ih; /* interrupt vectoring */
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
bus_dma_tag_t sc_dmatag; /* DMA tag */
|
|
|
|
struct eap_dma *sc_dmas;
|
|
|
|
void (*sc_pintr)(void *); /* dma completion intr handler */
|
|
void *sc_parg; /* arg for sc_intr() */
|
|
#ifdef DIAGNOSTIC
|
|
char sc_prun;
|
|
#endif
|
|
|
|
void (*sc_rintr)(void *); /* dma completion intr handler */
|
|
void *sc_rarg; /* arg for sc_intr() */
|
|
#ifdef DIAGNOSTIC
|
|
char sc_rrun;
|
|
#endif
|
|
|
|
#if NMIDI > 0
|
|
void (*sc_iintr)(void *, int); /* midi input ready handler */
|
|
void (*sc_ointr)(void *); /* midi output ready handler */
|
|
void *sc_arg;
|
|
#endif
|
|
|
|
u_short sc_port[AK_NPORTS]; /* mirror of the hardware setting */
|
|
u_int sc_record_source; /* recording source mask */
|
|
u_int sc_output_source; /* output source mask */
|
|
u_int sc_mic_preamp;
|
|
char sc_1371; /* Using ES1371/AC97 codec */
|
|
|
|
struct ac97_codec_if *codec_if;
|
|
struct ac97_host_if host_if;
|
|
};
|
|
|
|
int eap_allocmem __P((struct eap_softc *, size_t, size_t, struct eap_dma *));
|
|
int eap_freemem __P((struct eap_softc *, struct eap_dma *));
|
|
|
|
#define EWRITE1(sc, r, x) bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x))
|
|
#define EWRITE2(sc, r, x) bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x))
|
|
#define EWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x))
|
|
#define EREAD1(sc, r) bus_space_read_1((sc)->iot, (sc)->ioh, (r))
|
|
#define EREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r))
|
|
#define EREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r))
|
|
|
|
struct cfattach eap_ca = {
|
|
sizeof(struct eap_softc), eap_match, eap_attach
|
|
};
|
|
|
|
int eap_open __P((void *, int));
|
|
void eap_close __P((void *));
|
|
int eap_query_encoding __P((void *, struct audio_encoding *));
|
|
int eap_set_params __P((void *, int, int, struct audio_params *, struct audio_params *));
|
|
int eap_round_blocksize __P((void *, int));
|
|
int eap_trigger_output __P((void *, void *, void *, int, void (*)(void *),
|
|
void *, struct audio_params *));
|
|
int eap_trigger_input __P((void *, void *, void *, int, void (*)(void *),
|
|
void *, struct audio_params *));
|
|
int eap_halt_output __P((void *));
|
|
int eap_halt_input __P((void *));
|
|
void eap1370_write_codec __P((struct eap_softc *, int, int));
|
|
int eap_getdev __P((void *, struct audio_device *));
|
|
int eap1370_mixer_set_port __P((void *, mixer_ctrl_t *));
|
|
int eap1370_mixer_get_port __P((void *, mixer_ctrl_t *));
|
|
int eap1371_mixer_set_port __P((void *, mixer_ctrl_t *));
|
|
int eap1371_mixer_get_port __P((void *, mixer_ctrl_t *));
|
|
int eap1370_query_devinfo __P((void *, mixer_devinfo_t *));
|
|
void *eap_malloc __P((void *, int, size_t, int, int));
|
|
void eap_free __P((void *, void *, int));
|
|
size_t eap_round_buffersize __P((void *, int, size_t));
|
|
paddr_t eap_mappage __P((void *, void *, off_t, int));
|
|
int eap_get_props __P((void *));
|
|
void eap1370_set_mixer __P((struct eap_softc *sc, int a, int d));
|
|
u_int32_t eap1371_src_wait __P((struct eap_softc *sc));
|
|
void eap1371_set_adc_rate __P((struct eap_softc *sc, int rate));
|
|
void eap1371_set_dac_rate __P((struct eap_softc *sc, int rate, int which));
|
|
int eap1371_src_read __P((struct eap_softc *sc, int a));
|
|
void eap1371_src_write __P((struct eap_softc *sc, int a, int d));
|
|
int eap1371_query_devinfo __P((void *addr, mixer_devinfo_t *dip));
|
|
|
|
int eap1371_attach_codec __P((void *sc, struct ac97_codec_if *));
|
|
int eap1371_read_codec __P((void *sc, u_int8_t a, u_int16_t *d));
|
|
int eap1371_write_codec __P((void *sc, u_int8_t a, u_int16_t d));
|
|
void eap1371_reset_codec __P((void *sc));
|
|
int eap1371_get_portnum_by_name __P((struct eap_softc *, char *, char *,
|
|
char *));
|
|
#if NMIDI > 0
|
|
void eap_midi_close __P((void *));
|
|
void eap_midi_getinfo __P((void *, struct midi_info *));
|
|
int eap_midi_open __P((void *, int, void (*)(void *, int),
|
|
void (*)(void *), void *));
|
|
int eap_midi_output __P((void *, int));
|
|
#endif
|
|
|
|
struct audio_hw_if eap1370_hw_if = {
|
|
eap_open,
|
|
eap_close,
|
|
NULL,
|
|
eap_query_encoding,
|
|
eap_set_params,
|
|
eap_round_blocksize,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
eap_halt_output,
|
|
eap_halt_input,
|
|
NULL,
|
|
eap_getdev,
|
|
NULL,
|
|
eap1370_mixer_set_port,
|
|
eap1370_mixer_get_port,
|
|
eap1370_query_devinfo,
|
|
eap_malloc,
|
|
eap_free,
|
|
eap_round_buffersize,
|
|
eap_mappage,
|
|
eap_get_props,
|
|
eap_trigger_output,
|
|
eap_trigger_input,
|
|
};
|
|
|
|
struct audio_hw_if eap1371_hw_if = {
|
|
eap_open,
|
|
eap_close,
|
|
NULL,
|
|
eap_query_encoding,
|
|
eap_set_params,
|
|
eap_round_blocksize,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
eap_halt_output,
|
|
eap_halt_input,
|
|
NULL,
|
|
eap_getdev,
|
|
NULL,
|
|
eap1371_mixer_set_port,
|
|
eap1371_mixer_get_port,
|
|
eap1371_query_devinfo,
|
|
eap_malloc,
|
|
eap_free,
|
|
eap_round_buffersize,
|
|
eap_mappage,
|
|
eap_get_props,
|
|
eap_trigger_output,
|
|
eap_trigger_input,
|
|
};
|
|
|
|
#if NMIDI > 0
|
|
struct midi_hw_if eap_midi_hw_if = {
|
|
eap_midi_open,
|
|
eap_midi_close,
|
|
eap_midi_output,
|
|
eap_midi_getinfo,
|
|
0, /* ioctl */
|
|
};
|
|
#endif
|
|
|
|
struct audio_device eap_device = {
|
|
"Ensoniq AudioPCI",
|
|
"",
|
|
"eap"
|
|
};
|
|
|
|
int
|
|
eap_match(parent, match, aux)
|
|
struct device *parent;
|
|
struct cfdata *match;
|
|
void *aux;
|
|
{
|
|
struct pci_attach_args *pa = (struct pci_attach_args *) aux;
|
|
|
|
switch (PCI_VENDOR(pa->pa_id)) {
|
|
case PCI_VENDOR_CREATIVELABS:
|
|
switch (PCI_PRODUCT(pa->pa_id)) {
|
|
case PCI_PRODUCT_CREATIVELABS_EV1938:
|
|
return (1);
|
|
}
|
|
break;
|
|
case PCI_VENDOR_ENSONIQ:
|
|
switch (PCI_PRODUCT(pa->pa_id)) {
|
|
case PCI_PRODUCT_ENSONIQ_AUDIOPCI:
|
|
case PCI_PRODUCT_ENSONIQ_AUDIOPCI97:
|
|
case PCI_PRODUCT_ENSONIQ_CT5880:
|
|
return (1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
eap1370_write_codec(sc, a, d)
|
|
struct eap_softc *sc;
|
|
int a, d;
|
|
{
|
|
int icss, to;
|
|
|
|
to = EAP_WRITE_TIMEOUT;
|
|
do {
|
|
icss = EREAD4(sc, EAP_ICSS);
|
|
DPRINTFN(5,("eap: codec %d prog: icss=0x%08x\n", a, icss));
|
|
if (!to--) {
|
|
printf("eap: timeout writing to codec\n");
|
|
return;
|
|
}
|
|
} while(icss & EAP_CWRIP); /* XXX could use CSTAT here */
|
|
EWRITE4(sc, EAP_CODEC, EAP_SET_CODEC(a, d));
|
|
}
|
|
|
|
/*
|
|
* Reading and writing the CODEC is very convoluted. This mimics the
|
|
* FreeBSD and Linux drivers.
|
|
*/
|
|
|
|
static __inline void eap1371_ready_codec
|
|
__P((struct eap_softc *sc, u_int8_t a, u_int32_t wd));
|
|
static __inline void
|
|
eap1371_ready_codec(sc, a, wd)
|
|
struct eap_softc *sc;
|
|
u_int8_t a;
|
|
u_int32_t wd;
|
|
{
|
|
int to, s;
|
|
u_int32_t src, t;
|
|
|
|
for (to = 0; to < EAP_WRITE_TIMEOUT; to++) {
|
|
if (!(EREAD4(sc, E1371_CODEC) & E1371_CODEC_WIP))
|
|
break;
|
|
delay(1);
|
|
}
|
|
if (to > EAP_WRITE_TIMEOUT)
|
|
printf("%s: eap1371_ready_codec timeout 1\n",
|
|
sc->sc_dev.dv_xname);
|
|
|
|
s = splaudio();
|
|
src = eap1371_src_wait(sc) & E1371_SRC_CTLMASK;
|
|
EWRITE4(sc, E1371_SRC, src | E1371_SRC_STATE_OK);
|
|
|
|
for (to = 0; to < EAP_READ_TIMEOUT; to++) {
|
|
t = EREAD4(sc, E1371_SRC);
|
|
if ((t & E1371_SRC_STATE_MASK) == 0)
|
|
break;
|
|
delay(1);
|
|
}
|
|
if (to > EAP_WRITE_TIMEOUT)
|
|
printf("%s: eap1371_ready_codec timeout 2\n",
|
|
sc->sc_dev.dv_xname);
|
|
|
|
for (to = 0; to < EAP_READ_TIMEOUT; to++) {
|
|
t = EREAD4(sc, E1371_SRC);
|
|
if ((t & E1371_SRC_STATE_MASK) == E1371_SRC_STATE_OK)
|
|
break;
|
|
delay(1);
|
|
}
|
|
if (to > EAP_WRITE_TIMEOUT)
|
|
printf("%s: eap1371_ready_codec timeout 3\n",
|
|
sc->sc_dev.dv_xname);
|
|
|
|
EWRITE4(sc, E1371_CODEC, wd);
|
|
|
|
eap1371_src_wait(sc);
|
|
EWRITE4(sc, E1371_SRC, src);
|
|
|
|
splx(s);
|
|
}
|
|
|
|
int
|
|
eap1371_read_codec(sc_, a, d)
|
|
void *sc_;
|
|
u_int8_t a;
|
|
u_int16_t *d;
|
|
{
|
|
struct eap_softc *sc = sc_;
|
|
int to;
|
|
u_int32_t t;
|
|
|
|
eap1371_ready_codec(sc, a, E1371_SET_CODEC(a, 0) | E1371_CODEC_READ);
|
|
|
|
for (to = 0; to < EAP_WRITE_TIMEOUT; to++) {
|
|
if (!(EREAD4(sc, E1371_CODEC) & E1371_CODEC_WIP))
|
|
break;
|
|
}
|
|
if (to > EAP_WRITE_TIMEOUT)
|
|
printf("%s: eap1371_read_codec timeout 1\n",
|
|
sc->sc_dev.dv_xname);
|
|
|
|
for (to = 0; to < EAP_WRITE_TIMEOUT; to++) {
|
|
t = EREAD4(sc, E1371_CODEC);
|
|
if (t & E1371_CODEC_VALID)
|
|
break;
|
|
}
|
|
if (to > EAP_WRITE_TIMEOUT)
|
|
printf("%s: eap1371_read_codec timeout 2\n",
|
|
sc->sc_dev.dv_xname);
|
|
|
|
*d = (u_int16_t)t;
|
|
|
|
DPRINTFN(10, ("eap1371: reading codec (%x) = %x\n", a, *d));
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap1371_write_codec(sc_, a, d)
|
|
void *sc_;
|
|
u_int8_t a;
|
|
u_int16_t d;
|
|
{
|
|
struct eap_softc *sc = sc_;
|
|
|
|
eap1371_ready_codec(sc, a, E1371_SET_CODEC(a, d));
|
|
|
|
DPRINTFN(10, ("eap1371: writing codec %x --> %x\n", d, a));
|
|
|
|
return (0);
|
|
}
|
|
|
|
u_int32_t
|
|
eap1371_src_wait(sc)
|
|
struct eap_softc *sc;
|
|
{
|
|
int to;
|
|
u_int32_t src;
|
|
|
|
for (to = 0; to < EAP_READ_TIMEOUT; to++) {
|
|
src = EREAD4(sc, E1371_SRC);
|
|
if (!(src & E1371_SRC_RBUSY))
|
|
return (src);
|
|
delay(1);
|
|
}
|
|
printf("%s: eap1371_src_wait timeout\n", sc->sc_dev.dv_xname);
|
|
return (src);
|
|
}
|
|
|
|
int
|
|
eap1371_src_read(sc, a)
|
|
struct eap_softc *sc;
|
|
int a;
|
|
{
|
|
int to;
|
|
u_int32_t src, t;
|
|
|
|
src = eap1371_src_wait(sc) & E1371_SRC_CTLMASK;
|
|
src |= E1371_SRC_ADDR(a);
|
|
EWRITE4(sc, E1371_SRC, src | E1371_SRC_STATE_OK);
|
|
|
|
for (to = 0; to < EAP_READ_TIMEOUT; to++) {
|
|
t = EREAD4(sc, E1371_SRC);
|
|
if ((t & E1371_SRC_STATE_MASK) == E1371_SRC_STATE_OK)
|
|
break;
|
|
delay(1);
|
|
}
|
|
EWRITE4(sc, E1371_SRC, src);
|
|
|
|
return t & E1371_SRC_DATAMASK;
|
|
}
|
|
|
|
void
|
|
eap1371_src_write(sc, a, d)
|
|
struct eap_softc *sc;
|
|
int a,d;
|
|
{
|
|
u_int32_t r;
|
|
|
|
r = eap1371_src_wait(sc) & E1371_SRC_CTLMASK;
|
|
r |= E1371_SRC_RAMWE | E1371_SRC_ADDR(a) | E1371_SRC_DATA(d);
|
|
EWRITE4(sc, E1371_SRC, r);
|
|
}
|
|
|
|
void
|
|
eap1371_set_adc_rate(sc, rate)
|
|
struct eap_softc *sc;
|
|
int rate;
|
|
{
|
|
int freq, n, truncm;
|
|
int out;
|
|
int s;
|
|
|
|
/* Whatever, it works, so I'll leave it :) */
|
|
|
|
if (rate > 48000)
|
|
rate = 48000;
|
|
if (rate < 4000)
|
|
rate = 4000;
|
|
n = rate / 3000;
|
|
if ((1 << n) & SRC_MAGIC)
|
|
n--;
|
|
truncm = ((21 * n) - 1) | 1;
|
|
freq = ((48000 << 15) / rate) * n;
|
|
if (rate >= 24000) {
|
|
if (truncm > 239)
|
|
truncm = 239;
|
|
out = ESRC_SET_TRUNC((239 - truncm) / 2);
|
|
} else {
|
|
if (truncm > 119)
|
|
truncm = 119;
|
|
out = ESRC_SMF | ESRC_SET_TRUNC((119 - truncm) / 2);
|
|
}
|
|
out |= ESRC_SET_N(n);
|
|
s = splaudio();
|
|
eap1371_src_write(sc, ESRC_ADC+ESRC_TRUNC_N, out);
|
|
|
|
|
|
out = eap1371_src_read(sc, ESRC_ADC+ESRC_IREGS) & 0xff;
|
|
eap1371_src_write(sc, ESRC_ADC+ESRC_IREGS, out |
|
|
ESRC_SET_VFI(freq >> 15));
|
|
eap1371_src_write(sc, ESRC_ADC+ESRC_VFF, freq & 0x7fff);
|
|
eap1371_src_write(sc, ESRC_ADC_VOLL, ESRC_SET_ADC_VOL(n));
|
|
eap1371_src_write(sc, ESRC_ADC_VOLR, ESRC_SET_ADC_VOL(n));
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
eap1371_set_dac_rate(sc, rate, which)
|
|
struct eap_softc *sc;
|
|
int rate;
|
|
int which;
|
|
{
|
|
int dac = which == 1 ? ESRC_DAC1 : ESRC_DAC2;
|
|
int freq, r;
|
|
int s;
|
|
|
|
/* Whatever, it works, so I'll leave it :) */
|
|
|
|
if (rate > 48000)
|
|
rate = 48000;
|
|
if (rate < 4000)
|
|
rate = 4000;
|
|
freq = (rate << 15) / 3000;
|
|
|
|
s = splaudio();
|
|
eap1371_src_wait(sc);
|
|
r = EREAD4(sc, E1371_SRC) & (E1371_SRC_DISABLE |
|
|
E1371_SRC_DISP2 | E1371_SRC_DISP1 | E1371_SRC_DISREC);
|
|
r |= (which == 1) ? E1371_SRC_DISP1 : E1371_SRC_DISP2;
|
|
EWRITE4(sc, E1371_SRC, r);
|
|
r = eap1371_src_read(sc, dac + ESRC_IREGS) & 0x00ff;
|
|
eap1371_src_write(sc, dac + ESRC_IREGS, r | ((freq >> 5) & 0xfc00));
|
|
eap1371_src_write(sc, dac + ESRC_VFF, freq & 0x7fff);
|
|
r = EREAD4(sc, E1371_SRC) & (E1371_SRC_DISABLE |
|
|
E1371_SRC_DISP2 | E1371_SRC_DISP1 | E1371_SRC_DISREC);
|
|
r &= ~(which == 1 ? E1371_SRC_DISP1 : E1371_SRC_DISP2);
|
|
EWRITE4(sc, E1371_SRC, r);
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
eap_attach(parent, self, aux)
|
|
struct device *parent;
|
|
struct device *self;
|
|
void *aux;
|
|
{
|
|
struct eap_softc *sc = (struct eap_softc *)self;
|
|
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
|
|
pci_chipset_tag_t pc = pa->pa_pc;
|
|
struct audio_hw_if *eap_hw_if;
|
|
char const *intrstr;
|
|
pci_intr_handle_t ih;
|
|
pcireg_t csr;
|
|
char devinfo[256];
|
|
mixer_ctrl_t ctl;
|
|
int i;
|
|
int revision, ct5880;
|
|
const char *revstr = "";
|
|
|
|
/* Flag if we're "creative" */
|
|
sc->sc_1371 = !(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ENSONIQ &&
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ENSONIQ_AUDIOPCI);
|
|
|
|
pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo);
|
|
revision = PCI_REVISION(pa->pa_class);
|
|
if (sc->sc_1371) {
|
|
ct5880 = 0;
|
|
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ENSONIQ &&
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ENSONIQ_CT5880)
|
|
ct5880 = 1;
|
|
switch (revision) {
|
|
case EAP_EV1938_A: revstr = "EV1938A "; break;
|
|
case EAP_CT5880_C: revstr = "CT5880C "; ct5880 = 1; break;
|
|
case EAP_ES1373_A: revstr = "ES1373A "; break;
|
|
case EAP_ES1373_B: revstr = "ES1373B "; break;
|
|
case EAP_CT5880_A: revstr = "CT5880A "; ct5880 = 1; break;
|
|
case EAP_ES1371_B: revstr = "ES1371B "; break;
|
|
}
|
|
}
|
|
printf(": %s %s(rev. 0x%02x)\n", devinfo, revstr, revision);
|
|
|
|
/* Map I/O register */
|
|
if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0,
|
|
&sc->iot, &sc->ioh, NULL, NULL)) {
|
|
printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname);
|
|
return;
|
|
}
|
|
|
|
sc->sc_dmatag = pa->pa_dmat;
|
|
|
|
/* Enable the device. */
|
|
csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
|
|
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
|
|
csr | PCI_COMMAND_MASTER_ENABLE);
|
|
|
|
/* Map and establish the interrupt. */
|
|
if (pci_intr_map(pa, &ih)) {
|
|
printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
|
|
return;
|
|
}
|
|
intrstr = pci_intr_string(pc, ih);
|
|
sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, eap_intr, sc);
|
|
if (sc->sc_ih == NULL) {
|
|
printf("%s: couldn't establish interrupt",
|
|
sc->sc_dev.dv_xname);
|
|
if (intrstr != NULL)
|
|
printf(" at %s", intrstr);
|
|
printf("\n");
|
|
return;
|
|
}
|
|
printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);
|
|
|
|
if (!sc->sc_1371) {
|
|
/* Enable interrupts and looping mode. */
|
|
/* enable the parts we need */
|
|
EWRITE4(sc, EAP_SIC, EAP_P2_INTR_EN | EAP_R1_INTR_EN);
|
|
EWRITE4(sc, EAP_ICSC, EAP_CDC_EN);
|
|
|
|
/* reset codec */
|
|
/* normal operation */
|
|
/* select codec clocks */
|
|
eap1370_write_codec(sc, AK_RESET, AK_PD);
|
|
eap1370_write_codec(sc, AK_RESET, AK_PD | AK_NRST);
|
|
eap1370_write_codec(sc, AK_CS, 0x0);
|
|
|
|
eap_hw_if = &eap1370_hw_if;
|
|
|
|
/* Enable all relevant mixer switches. */
|
|
ctl.dev = EAP_OUTPUT_SELECT;
|
|
ctl.type = AUDIO_MIXER_SET;
|
|
ctl.un.mask = 1 << EAP_VOICE_VOL | 1 << EAP_FM_VOL |
|
|
1 << EAP_CD_VOL | 1 << EAP_LINE_VOL | 1 << EAP_AUX_VOL |
|
|
1 << EAP_MIC_VOL;
|
|
eap_hw_if->set_port(sc, &ctl);
|
|
|
|
ctl.type = AUDIO_MIXER_VALUE;
|
|
ctl.un.value.num_channels = 1;
|
|
for (ctl.dev = EAP_MASTER_VOL; ctl.dev < EAP_MIC_VOL;
|
|
ctl.dev++) {
|
|
ctl.un.value.level[AUDIO_MIXER_LEVEL_MONO] = VOL_0DB;
|
|
eap_hw_if->set_port(sc, &ctl);
|
|
}
|
|
ctl.un.value.level[AUDIO_MIXER_LEVEL_MONO] = 0;
|
|
eap_hw_if->set_port(sc, &ctl);
|
|
ctl.dev = EAP_MIC_PREAMP;
|
|
ctl.type = AUDIO_MIXER_ENUM;
|
|
ctl.un.ord = 0;
|
|
eap_hw_if->set_port(sc, &ctl);
|
|
ctl.dev = EAP_RECORD_SOURCE;
|
|
ctl.type = AUDIO_MIXER_SET;
|
|
ctl.un.mask = 1 << EAP_MIC_VOL;
|
|
eap_hw_if->set_port(sc, &ctl);
|
|
} else {
|
|
/* clean slate */
|
|
|
|
EWRITE4(sc, EAP_SIC, 0);
|
|
EWRITE4(sc, EAP_ICSC, 0);
|
|
EWRITE4(sc, E1371_LEGACY, 0);
|
|
|
|
if (ct5880) {
|
|
EWRITE4(sc, EAP_ICSS, EAP_CT5880_AC97_RESET);
|
|
/* Let codec wake up */
|
|
tsleep(sc, PRIBIO, "eapcdc", hz / 20);
|
|
}
|
|
|
|
/* Reset from es1371's perspective */
|
|
EWRITE4(sc, EAP_ICSC, E1371_SYNC_RES);
|
|
delay(20);
|
|
EWRITE4(sc, EAP_ICSC, 0);
|
|
|
|
/*
|
|
* Must properly reprogram sample rate converter,
|
|
* or it locks up. Set some defaults for the life of the
|
|
* machine, and set up a sb default sample rate.
|
|
*/
|
|
EWRITE4(sc, E1371_SRC, E1371_SRC_DISABLE);
|
|
for (i = 0; i < 0x80; i++)
|
|
eap1371_src_write(sc, i, 0);
|
|
eap1371_src_write(sc, ESRC_DAC1+ESRC_TRUNC_N, ESRC_SET_N(16));
|
|
eap1371_src_write(sc, ESRC_DAC2+ESRC_TRUNC_N, ESRC_SET_N(16));
|
|
eap1371_src_write(sc, ESRC_DAC1+ESRC_IREGS, ESRC_SET_VFI(16));
|
|
eap1371_src_write(sc, ESRC_DAC2+ESRC_IREGS, ESRC_SET_VFI(16));
|
|
eap1371_src_write(sc, ESRC_ADC_VOLL, ESRC_SET_ADC_VOL(16));
|
|
eap1371_src_write(sc, ESRC_ADC_VOLR, ESRC_SET_ADC_VOL(16));
|
|
eap1371_src_write(sc, ESRC_DAC1_VOLL, ESRC_SET_DAC_VOLI(1));
|
|
eap1371_src_write(sc, ESRC_DAC1_VOLR, ESRC_SET_DAC_VOLI(1));
|
|
eap1371_src_write(sc, ESRC_DAC2_VOLL, ESRC_SET_DAC_VOLI(1));
|
|
eap1371_src_write(sc, ESRC_DAC2_VOLR, ESRC_SET_DAC_VOLI(1));
|
|
eap1371_set_adc_rate(sc, 22050);
|
|
eap1371_set_dac_rate(sc, 22050, 1);
|
|
eap1371_set_dac_rate(sc, 22050, 2);
|
|
|
|
EWRITE4(sc, E1371_SRC, 0);
|
|
|
|
/* Reset codec */
|
|
|
|
/* Interrupt enable */
|
|
sc->host_if.arg = sc;
|
|
sc->host_if.attach = eap1371_attach_codec;
|
|
sc->host_if.read = eap1371_read_codec;
|
|
sc->host_if.write = eap1371_write_codec;
|
|
sc->host_if.reset = eap1371_reset_codec;
|
|
|
|
if (ac97_attach(&sc->host_if) == 0) {
|
|
/* Interrupt enable */
|
|
EWRITE4(sc, EAP_SIC, EAP_P2_INTR_EN | EAP_R1_INTR_EN);
|
|
} else
|
|
return;
|
|
|
|
eap_hw_if = &eap1371_hw_if;
|
|
|
|
/* Just enable the DAC and master volumes by default */
|
|
ctl.type = AUDIO_MIXER_ENUM;
|
|
ctl.un.ord = 0; /* off */
|
|
ctl.dev = eap1371_get_portnum_by_name(sc, AudioCoutputs,
|
|
AudioNmaster, AudioNmute);
|
|
eap1371_mixer_set_port(sc, &ctl);
|
|
ctl.dev = eap1371_get_portnum_by_name(sc, AudioCinputs,
|
|
AudioNdac, AudioNmute);
|
|
eap1371_mixer_set_port(sc, &ctl);
|
|
ctl.dev = eap1371_get_portnum_by_name(sc, AudioCrecord,
|
|
AudioNvolume, AudioNmute);
|
|
eap1371_mixer_set_port(sc, &ctl);
|
|
|
|
ctl.dev = eap1371_get_portnum_by_name(sc, AudioCrecord,
|
|
AudioNsource, NULL);
|
|
ctl.type = AUDIO_MIXER_ENUM;
|
|
ctl.un.ord = 0;
|
|
eap1371_mixer_set_port(sc, &ctl);
|
|
|
|
}
|
|
|
|
audio_attach_mi(eap_hw_if, sc, &sc->sc_dev);
|
|
|
|
#if NMIDI > 0
|
|
midi_attach_mi(&eap_midi_hw_if, sc, &sc->sc_dev);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
eap1371_attach_codec(sc_, codec_if)
|
|
void *sc_;
|
|
struct ac97_codec_if *codec_if;
|
|
{
|
|
struct eap_softc *sc = sc_;
|
|
|
|
sc->codec_if = codec_if;
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
eap1371_reset_codec(sc_)
|
|
void *sc_;
|
|
{
|
|
struct eap_softc *sc = sc_;
|
|
u_int32_t icsc;
|
|
int s;
|
|
|
|
s = splaudio();
|
|
icsc = EREAD4(sc, EAP_ICSC);
|
|
EWRITE4(sc, EAP_ICSC, icsc | E1371_SYNC_RES);
|
|
delay(20);
|
|
EWRITE4(sc, EAP_ICSC, icsc & ~E1371_SYNC_RES);
|
|
delay(1);
|
|
splx(s);
|
|
|
|
return;
|
|
}
|
|
|
|
int
|
|
eap_intr(p)
|
|
void *p;
|
|
{
|
|
struct eap_softc *sc = p;
|
|
u_int32_t intr, sic;
|
|
|
|
intr = EREAD4(sc, EAP_ICSS);
|
|
if (!(intr & EAP_INTR))
|
|
return (0);
|
|
sic = EREAD4(sc, EAP_SIC);
|
|
DPRINTFN(5, ("eap_intr: ICSS=0x%08x, SIC=0x%08x\n", intr, sic));
|
|
if (intr & EAP_I_ADC) {
|
|
/*
|
|
* XXX This is a hack!
|
|
* The EAP chip sometimes generates the recording interrupt
|
|
* while it is still transferring the data. To make sure
|
|
* it has all arrived we busy wait until the count is right.
|
|
* The transfer we are waiting for is 8 longwords.
|
|
*/
|
|
int s, nw, n;
|
|
EWRITE4(sc, EAP_MEMPAGE, EAP_ADC_PAGE);
|
|
s = EREAD4(sc, EAP_ADC_CSR);
|
|
nw = ((s & 0xffff) + 1) >> 2; /* # of words in DMA */
|
|
n = 0;
|
|
while (((EREAD4(sc, EAP_ADC_SIZE) >> 16) + 8) % nw == 0) {
|
|
delay(10);
|
|
if (++n > 100) {
|
|
printf("eapintr: dma fix timeout");
|
|
break;
|
|
}
|
|
}
|
|
/* Continue with normal interrupt handling. */
|
|
EWRITE4(sc, EAP_SIC, sic & ~EAP_R1_INTR_EN);
|
|
EWRITE4(sc, EAP_SIC, sic);
|
|
if (sc->sc_rintr)
|
|
sc->sc_rintr(sc->sc_rarg);
|
|
}
|
|
if (intr & EAP_I_DAC2) {
|
|
EWRITE4(sc, EAP_SIC, sic & ~EAP_P2_INTR_EN);
|
|
EWRITE4(sc, EAP_SIC, sic);
|
|
if (sc->sc_pintr)
|
|
sc->sc_pintr(sc->sc_parg);
|
|
}
|
|
#if NMIDI > 0
|
|
if ((intr & EAP_I_UART) && sc->sc_iintr != NULL) {
|
|
u_int32_t data;
|
|
|
|
if (EREAD1(sc, EAP_UART_STATUS) & EAP_US_RXINT) {
|
|
while (EREAD1(sc, EAP_UART_STATUS) & EAP_US_RXRDY) {
|
|
data = EREAD1(sc, EAP_UART_DATA);
|
|
sc->sc_iintr(sc->sc_arg, data);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
eap_allocmem(sc, size, align, p)
|
|
struct eap_softc *sc;
|
|
size_t size;
|
|
size_t align;
|
|
struct eap_dma *p;
|
|
{
|
|
int error;
|
|
|
|
p->size = size;
|
|
error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
|
|
p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
|
|
&p->nsegs, BUS_DMA_NOWAIT);
|
|
if (error)
|
|
return (error);
|
|
|
|
error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
|
|
&p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
|
|
if (error)
|
|
goto free;
|
|
|
|
error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
|
|
0, BUS_DMA_NOWAIT, &p->map);
|
|
if (error)
|
|
goto unmap;
|
|
|
|
error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL,
|
|
BUS_DMA_NOWAIT);
|
|
if (error)
|
|
goto destroy;
|
|
return (0);
|
|
|
|
destroy:
|
|
bus_dmamap_destroy(sc->sc_dmatag, p->map);
|
|
unmap:
|
|
bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
|
|
free:
|
|
bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
eap_freemem(sc, p)
|
|
struct eap_softc *sc;
|
|
struct eap_dma *p;
|
|
{
|
|
bus_dmamap_unload(sc->sc_dmatag, p->map);
|
|
bus_dmamap_destroy(sc->sc_dmatag, p->map);
|
|
bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
|
|
bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap_open(addr, flags)
|
|
void *addr;
|
|
int flags;
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Close function is called at splaudio().
|
|
*/
|
|
void
|
|
eap_close(addr)
|
|
void *addr;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
|
|
eap_halt_output(sc);
|
|
eap_halt_input(sc);
|
|
|
|
sc->sc_pintr = 0;
|
|
sc->sc_rintr = 0;
|
|
}
|
|
|
|
int
|
|
eap_query_encoding(addr, fp)
|
|
void *addr;
|
|
struct audio_encoding *fp;
|
|
{
|
|
switch (fp->index) {
|
|
case 0:
|
|
strcpy(fp->name, AudioEulinear);
|
|
fp->encoding = AUDIO_ENCODING_ULINEAR;
|
|
fp->precision = 8;
|
|
fp->flags = 0;
|
|
return (0);
|
|
case 1:
|
|
strcpy(fp->name, AudioEmulaw);
|
|
fp->encoding = AUDIO_ENCODING_ULAW;
|
|
fp->precision = 8;
|
|
fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
|
|
return (0);
|
|
case 2:
|
|
strcpy(fp->name, AudioEalaw);
|
|
fp->encoding = AUDIO_ENCODING_ALAW;
|
|
fp->precision = 8;
|
|
fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
|
|
return (0);
|
|
case 3:
|
|
strcpy(fp->name, AudioEslinear);
|
|
fp->encoding = AUDIO_ENCODING_SLINEAR;
|
|
fp->precision = 8;
|
|
fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
|
|
return (0);
|
|
case 4:
|
|
strcpy(fp->name, AudioEslinear_le);
|
|
fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
|
|
fp->precision = 16;
|
|
fp->flags = 0;
|
|
return (0);
|
|
case 5:
|
|
strcpy(fp->name, AudioEulinear_le);
|
|
fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
|
|
fp->precision = 16;
|
|
fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
|
|
return (0);
|
|
case 6:
|
|
strcpy(fp->name, AudioEslinear_be);
|
|
fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
|
|
fp->precision = 16;
|
|
fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
|
|
return (0);
|
|
case 7:
|
|
strcpy(fp->name, AudioEulinear_be);
|
|
fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
|
|
fp->precision = 16;
|
|
fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
|
|
return (0);
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
|
|
int
|
|
eap_set_params(addr, setmode, usemode, play, rec)
|
|
void *addr;
|
|
int setmode, usemode;
|
|
struct audio_params *play, *rec;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
struct audio_params *p;
|
|
int mode;
|
|
u_int32_t div;
|
|
|
|
/*
|
|
* The es1370 only has one clock, so make the sample rates match.
|
|
*/
|
|
if (!sc->sc_1371) {
|
|
if (play->sample_rate != rec->sample_rate &&
|
|
usemode == (AUMODE_PLAY | AUMODE_RECORD)) {
|
|
if (setmode == AUMODE_PLAY) {
|
|
rec->sample_rate = play->sample_rate;
|
|
setmode |= AUMODE_RECORD;
|
|
} else if (setmode == AUMODE_RECORD) {
|
|
play->sample_rate = rec->sample_rate;
|
|
setmode |= AUMODE_PLAY;
|
|
} else
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
|
|
for (mode = AUMODE_RECORD; mode != -1;
|
|
mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
|
|
if ((setmode & mode) == 0)
|
|
continue;
|
|
|
|
p = mode == AUMODE_PLAY ? play : rec;
|
|
|
|
if (p->sample_rate < 4000 || p->sample_rate > 48000 ||
|
|
(p->precision != 8 && p->precision != 16) ||
|
|
(p->channels != 1 && p->channels != 2))
|
|
return (EINVAL);
|
|
|
|
p->factor = 1;
|
|
p->sw_code = 0;
|
|
switch (p->encoding) {
|
|
case AUDIO_ENCODING_SLINEAR_BE:
|
|
if (p->precision == 16)
|
|
p->sw_code = swap_bytes;
|
|
else
|
|
p->sw_code = change_sign8;
|
|
break;
|
|
case AUDIO_ENCODING_SLINEAR_LE:
|
|
if (p->precision != 16)
|
|
p->sw_code = change_sign8;
|
|
break;
|
|
case AUDIO_ENCODING_ULINEAR_BE:
|
|
if (p->precision == 16) {
|
|
if (mode == AUMODE_PLAY)
|
|
p->sw_code = swap_bytes_change_sign16_le;
|
|
else
|
|
p->sw_code = change_sign16_swap_bytes_le;
|
|
}
|
|
break;
|
|
case AUDIO_ENCODING_ULINEAR_LE:
|
|
if (p->precision == 16)
|
|
p->sw_code = change_sign16_le;
|
|
break;
|
|
case AUDIO_ENCODING_ULAW:
|
|
if (mode == AUMODE_PLAY) {
|
|
p->factor = 2;
|
|
p->sw_code = mulaw_to_slinear16_le;
|
|
} else
|
|
p->sw_code = ulinear8_to_mulaw;
|
|
break;
|
|
case AUDIO_ENCODING_ALAW:
|
|
if (mode == AUMODE_PLAY) {
|
|
p->factor = 2;
|
|
p->sw_code = alaw_to_slinear16_le;
|
|
} else
|
|
p->sw_code = ulinear8_to_alaw;
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
|
|
if (sc->sc_1371) {
|
|
eap1371_set_dac_rate(sc, play->sample_rate, 1);
|
|
eap1371_set_dac_rate(sc, play->sample_rate, 2);
|
|
eap1371_set_adc_rate(sc, rec->sample_rate);
|
|
} else {
|
|
/* Set the speed */
|
|
DPRINTFN(2, ("eap_set_params: old ICSC = 0x%08x\n",
|
|
EREAD4(sc, EAP_ICSC)));
|
|
div = EREAD4(sc, EAP_ICSC) & ~EAP_PCLKBITS;
|
|
/*
|
|
* XXX
|
|
* The -2 isn't documented, but seemed to make the wall
|
|
* time match
|
|
* what I expect. - mycroft
|
|
*/
|
|
if (usemode == AUMODE_RECORD)
|
|
div |= EAP_SET_PCLKDIV(EAP_XTAL_FREQ /
|
|
rec->sample_rate - 2);
|
|
else
|
|
div |= EAP_SET_PCLKDIV(EAP_XTAL_FREQ /
|
|
play->sample_rate - 2);
|
|
div |= EAP_CCB_INTRM;
|
|
EWRITE4(sc, EAP_ICSC, div);
|
|
DPRINTFN(2, ("eap_set_params: set ICSC = 0x%08x\n", div));
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap_round_blocksize(addr, blk)
|
|
void *addr;
|
|
int blk;
|
|
{
|
|
return (blk & -32); /* keep good alignment */
|
|
}
|
|
|
|
int
|
|
eap_trigger_output(addr, start, end, blksize, intr, arg, param)
|
|
void *addr;
|
|
void *start, *end;
|
|
int blksize;
|
|
void (*intr) __P((void *));
|
|
void *arg;
|
|
struct audio_params *param;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
struct eap_dma *p;
|
|
u_int32_t icsc, sic;
|
|
int sampshift;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (sc->sc_prun)
|
|
panic("eap_trigger_output: already running");
|
|
sc->sc_prun = 1;
|
|
#endif
|
|
|
|
DPRINTFN(1, ("eap_trigger_output: sc=%p start=%p end=%p "
|
|
"blksize=%d intr=%p(%p)\n", addr, start, end, blksize, intr, arg));
|
|
sc->sc_pintr = intr;
|
|
sc->sc_parg = arg;
|
|
|
|
icsc = EREAD4(sc, EAP_ICSC);
|
|
EWRITE4(sc, EAP_ICSC, icsc & ~EAP_DAC2_EN);
|
|
|
|
sic = EREAD4(sc, EAP_SIC);
|
|
sic &= ~(EAP_P2_S_EB | EAP_P2_S_MB | EAP_INC_BITS);
|
|
sic |= EAP_SET_P2_ST_INC(0) | EAP_SET_P2_END_INC(param->precision * param->factor / 8);
|
|
sampshift = 0;
|
|
if (param->precision * param->factor == 16) {
|
|
sic |= EAP_P2_S_EB;
|
|
sampshift++;
|
|
}
|
|
if (param->channels == 2) {
|
|
sic |= EAP_P2_S_MB;
|
|
sampshift++;
|
|
}
|
|
EWRITE4(sc, EAP_SIC, sic);
|
|
|
|
for (p = sc->sc_dmas; p && KERNADDR(p) != start; p = p->next)
|
|
;
|
|
if (!p) {
|
|
printf("eap_trigger_output: bad addr %p\n", start);
|
|
return (EINVAL);
|
|
}
|
|
|
|
DPRINTF(("eap_trigger_output: DAC2_ADDR=0x%x, DAC2_SIZE=0x%x\n",
|
|
(int)DMAADDR(p),
|
|
EAP_SET_SIZE(0, (((char *)end - (char *)start) >> 2) - 1)));
|
|
EWRITE4(sc, EAP_MEMPAGE, EAP_DAC_PAGE);
|
|
EWRITE4(sc, EAP_DAC2_ADDR, DMAADDR(p));
|
|
EWRITE4(sc, EAP_DAC2_SIZE,
|
|
EAP_SET_SIZE(0, (((char *)end - (char *)start) >> 2) - 1));
|
|
|
|
EWRITE2(sc, EAP_DAC2_CSR, (blksize >> sampshift) - 1);
|
|
|
|
EWRITE4(sc, EAP_ICSC, icsc | EAP_DAC2_EN);
|
|
|
|
DPRINTFN(1, ("eap_trigger_output: set ICSC = 0x%08x\n", icsc));
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap_trigger_input(addr, start, end, blksize, intr, arg, param)
|
|
void *addr;
|
|
void *start, *end;
|
|
int blksize;
|
|
void (*intr) __P((void *));
|
|
void *arg;
|
|
struct audio_params *param;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
struct eap_dma *p;
|
|
u_int32_t icsc, sic;
|
|
int sampshift;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (sc->sc_rrun)
|
|
panic("eap_trigger_input: already running");
|
|
sc->sc_rrun = 1;
|
|
#endif
|
|
|
|
DPRINTFN(1, ("eap_trigger_input: sc=%p start=%p end=%p blksize=%d intr=%p(%p)\n",
|
|
addr, start, end, blksize, intr, arg));
|
|
sc->sc_rintr = intr;
|
|
sc->sc_rarg = arg;
|
|
|
|
icsc = EREAD4(sc, EAP_ICSC);
|
|
EWRITE4(sc, EAP_ICSC, icsc & ~EAP_ADC_EN);
|
|
|
|
sic = EREAD4(sc, EAP_SIC);
|
|
sic &= ~(EAP_R1_S_EB | EAP_R1_S_MB);
|
|
sampshift = 0;
|
|
if (param->precision * param->factor == 16) {
|
|
sic |= EAP_R1_S_EB;
|
|
sampshift++;
|
|
}
|
|
if (param->channels == 2) {
|
|
sic |= EAP_R1_S_MB;
|
|
sampshift++;
|
|
}
|
|
EWRITE4(sc, EAP_SIC, sic);
|
|
|
|
for (p = sc->sc_dmas; p && KERNADDR(p) != start; p = p->next)
|
|
;
|
|
if (!p) {
|
|
printf("eap_trigger_input: bad addr %p\n", start);
|
|
return (EINVAL);
|
|
}
|
|
|
|
DPRINTF(("eap_trigger_input: ADC_ADDR=0x%x, ADC_SIZE=0x%x\n",
|
|
(int)DMAADDR(p),
|
|
EAP_SET_SIZE(0, (((char *)end - (char *)start) >> 2) - 1)));
|
|
EWRITE4(sc, EAP_MEMPAGE, EAP_ADC_PAGE);
|
|
EWRITE4(sc, EAP_ADC_ADDR, DMAADDR(p));
|
|
EWRITE4(sc, EAP_ADC_SIZE,
|
|
EAP_SET_SIZE(0, (((char *)end - (char *)start) >> 2) - 1));
|
|
|
|
EWRITE2(sc, EAP_ADC_CSR, (blksize >> sampshift) - 1);
|
|
|
|
EWRITE4(sc, EAP_ICSC, icsc | EAP_ADC_EN);
|
|
|
|
DPRINTFN(1, ("eap_trigger_input: set ICSC = 0x%08x\n", icsc));
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap_halt_output(addr)
|
|
void *addr;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
u_int32_t icsc;
|
|
|
|
DPRINTF(("eap: eap_halt_output\n"));
|
|
icsc = EREAD4(sc, EAP_ICSC);
|
|
EWRITE4(sc, EAP_ICSC, icsc & ~EAP_DAC2_EN);
|
|
#ifdef DIAGNOSTIC
|
|
sc->sc_prun = 0;
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap_halt_input(addr)
|
|
void *addr;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
u_int32_t icsc;
|
|
|
|
DPRINTF(("eap: eap_halt_input\n"));
|
|
icsc = EREAD4(sc, EAP_ICSC);
|
|
EWRITE4(sc, EAP_ICSC, icsc & ~EAP_ADC_EN);
|
|
#ifdef DIAGNOSTIC
|
|
sc->sc_rrun = 0;
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap_getdev(addr, retp)
|
|
void *addr;
|
|
struct audio_device *retp;
|
|
{
|
|
*retp = eap_device;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap1371_mixer_set_port(addr, cp)
|
|
void *addr;
|
|
mixer_ctrl_t *cp;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
|
|
return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp));
|
|
}
|
|
|
|
int
|
|
eap1371_mixer_get_port(addr, cp)
|
|
void *addr;
|
|
mixer_ctrl_t *cp;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
|
|
return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
|
|
}
|
|
|
|
int
|
|
eap1371_query_devinfo(addr, dip)
|
|
void *addr;
|
|
mixer_devinfo_t *dip;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
|
|
return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
|
|
}
|
|
|
|
int
|
|
eap1371_get_portnum_by_name(sc, class, device, qualifier)
|
|
struct eap_softc *sc;
|
|
char *class, *device, *qualifier;
|
|
{
|
|
return (sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if, class,
|
|
device, qualifier));
|
|
}
|
|
|
|
void
|
|
eap1370_set_mixer(sc, a, d)
|
|
struct eap_softc *sc;
|
|
int a, d;
|
|
{
|
|
eap1370_write_codec(sc, a, d);
|
|
|
|
sc->sc_port[a] = d;
|
|
DPRINTFN(1, ("eap1370_mixer_set_port port 0x%02x = 0x%02x\n", a, d));
|
|
}
|
|
|
|
int
|
|
eap1370_mixer_set_port(addr, cp)
|
|
void *addr;
|
|
mixer_ctrl_t *cp;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
int lval, rval, l, r, la, ra;
|
|
int l1, r1, l2, r2, m, o1, o2;
|
|
|
|
if (cp->dev == EAP_RECORD_SOURCE) {
|
|
if (cp->type != AUDIO_MIXER_SET)
|
|
return (EINVAL);
|
|
m = sc->sc_record_source = cp->un.mask;
|
|
l1 = l2 = r1 = r2 = 0;
|
|
if (m & (1 << EAP_VOICE_VOL))
|
|
l2 |= AK_M_VOICE, r2 |= AK_M_VOICE;
|
|
if (m & (1 << EAP_FM_VOL))
|
|
l1 |= AK_M_FM_L, r1 |= AK_M_FM_R;
|
|
if (m & (1 << EAP_CD_VOL))
|
|
l1 |= AK_M_CD_L, r1 |= AK_M_CD_R;
|
|
if (m & (1 << EAP_LINE_VOL))
|
|
l1 |= AK_M_LINE_L, r1 |= AK_M_LINE_R;
|
|
if (m & (1 << EAP_AUX_VOL))
|
|
l2 |= AK_M2_AUX_L, r2 |= AK_M2_AUX_R;
|
|
if (m & (1 << EAP_MIC_VOL))
|
|
l2 |= AK_M_TMIC, r2 |= AK_M_TMIC;
|
|
eap1370_set_mixer(sc, AK_IN_MIXER1_L, l1);
|
|
eap1370_set_mixer(sc, AK_IN_MIXER1_R, r1);
|
|
eap1370_set_mixer(sc, AK_IN_MIXER2_L, l2);
|
|
eap1370_set_mixer(sc, AK_IN_MIXER2_R, r2);
|
|
return (0);
|
|
}
|
|
if (cp->dev == EAP_OUTPUT_SELECT) {
|
|
if (cp->type != AUDIO_MIXER_SET)
|
|
return (EINVAL);
|
|
m = sc->sc_output_source = cp->un.mask;
|
|
o1 = o2 = 0;
|
|
if (m & (1 << EAP_VOICE_VOL))
|
|
o2 |= AK_M_VOICE_L | AK_M_VOICE_R;
|
|
if (m & (1 << EAP_FM_VOL))
|
|
o1 |= AK_M_FM_L | AK_M_FM_R;
|
|
if (m & (1 << EAP_CD_VOL))
|
|
o1 |= AK_M_CD_L | AK_M_CD_R;
|
|
if (m & (1 << EAP_LINE_VOL))
|
|
o1 |= AK_M_LINE_L | AK_M_LINE_R;
|
|
if (m & (1 << EAP_AUX_VOL))
|
|
o2 |= AK_M_AUX_L | AK_M_AUX_R;
|
|
if (m & (1 << EAP_MIC_VOL))
|
|
o1 |= AK_M_MIC;
|
|
eap1370_set_mixer(sc, AK_OUT_MIXER1, o1);
|
|
eap1370_set_mixer(sc, AK_OUT_MIXER2, o2);
|
|
return (0);
|
|
}
|
|
if (cp->dev == EAP_MIC_PREAMP) {
|
|
if (cp->type != AUDIO_MIXER_ENUM)
|
|
return (EINVAL);
|
|
if (cp->un.ord != 0 && cp->un.ord != 1)
|
|
return (EINVAL);
|
|
sc->sc_mic_preamp = cp->un.ord;
|
|
eap1370_set_mixer(sc, AK_MGAIN, cp->un.ord);
|
|
return (0);
|
|
}
|
|
if (cp->type != AUDIO_MIXER_VALUE)
|
|
return (EINVAL);
|
|
if (cp->un.value.num_channels == 1)
|
|
lval = rval = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
|
|
else if (cp->un.value.num_channels == 2) {
|
|
lval = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
|
|
rval = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
|
|
} else
|
|
return (EINVAL);
|
|
ra = -1;
|
|
switch (cp->dev) {
|
|
case EAP_MASTER_VOL:
|
|
l = VOL_TO_ATT5(lval);
|
|
r = VOL_TO_ATT5(rval);
|
|
la = AK_MASTER_L;
|
|
ra = AK_MASTER_R;
|
|
break;
|
|
case EAP_MIC_VOL:
|
|
if (cp->un.value.num_channels != 1)
|
|
return (EINVAL);
|
|
la = AK_MIC;
|
|
goto lr;
|
|
case EAP_VOICE_VOL:
|
|
la = AK_VOICE_L;
|
|
ra = AK_VOICE_R;
|
|
goto lr;
|
|
case EAP_FM_VOL:
|
|
la = AK_FM_L;
|
|
ra = AK_FM_R;
|
|
goto lr;
|
|
case EAP_CD_VOL:
|
|
la = AK_CD_L;
|
|
ra = AK_CD_R;
|
|
goto lr;
|
|
case EAP_LINE_VOL:
|
|
la = AK_LINE_L;
|
|
ra = AK_LINE_R;
|
|
goto lr;
|
|
case EAP_AUX_VOL:
|
|
la = AK_AUX_L;
|
|
ra = AK_AUX_R;
|
|
lr:
|
|
l = VOL_TO_GAIN5(lval);
|
|
r = VOL_TO_GAIN5(rval);
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
eap1370_set_mixer(sc, la, l);
|
|
if (ra >= 0) {
|
|
eap1370_set_mixer(sc, ra, r);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap1370_mixer_get_port(addr, cp)
|
|
void *addr;
|
|
mixer_ctrl_t *cp;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
int la, ra, l, r;
|
|
|
|
switch (cp->dev) {
|
|
case EAP_RECORD_SOURCE:
|
|
if (cp->type != AUDIO_MIXER_SET)
|
|
return (EINVAL);
|
|
cp->un.mask = sc->sc_record_source;
|
|
return (0);
|
|
case EAP_OUTPUT_SELECT:
|
|
if (cp->type != AUDIO_MIXER_SET)
|
|
return (EINVAL);
|
|
cp->un.mask = sc->sc_output_source;
|
|
return (0);
|
|
case EAP_MIC_PREAMP:
|
|
if (cp->type != AUDIO_MIXER_ENUM)
|
|
return (EINVAL);
|
|
cp->un.ord = sc->sc_mic_preamp;
|
|
return (0);
|
|
case EAP_MASTER_VOL:
|
|
l = ATT5_TO_VOL(sc->sc_port[AK_MASTER_L]);
|
|
r = ATT5_TO_VOL(sc->sc_port[AK_MASTER_R]);
|
|
break;
|
|
case EAP_MIC_VOL:
|
|
if (cp->un.value.num_channels != 1)
|
|
return (EINVAL);
|
|
la = ra = AK_MIC;
|
|
goto lr;
|
|
case EAP_VOICE_VOL:
|
|
la = AK_VOICE_L;
|
|
ra = AK_VOICE_R;
|
|
goto lr;
|
|
case EAP_FM_VOL:
|
|
la = AK_FM_L;
|
|
ra = AK_FM_R;
|
|
goto lr;
|
|
case EAP_CD_VOL:
|
|
la = AK_CD_L;
|
|
ra = AK_CD_R;
|
|
goto lr;
|
|
case EAP_LINE_VOL:
|
|
la = AK_LINE_L;
|
|
ra = AK_LINE_R;
|
|
goto lr;
|
|
case EAP_AUX_VOL:
|
|
la = AK_AUX_L;
|
|
ra = AK_AUX_R;
|
|
lr:
|
|
l = GAIN5_TO_VOL(sc->sc_port[la]);
|
|
r = GAIN5_TO_VOL(sc->sc_port[ra]);
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
if (cp->un.value.num_channels == 1)
|
|
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r) / 2;
|
|
else if (cp->un.value.num_channels == 2) {
|
|
cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
|
|
cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
|
|
} else
|
|
return (EINVAL);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
eap1370_query_devinfo(addr, dip)
|
|
void *addr;
|
|
mixer_devinfo_t *dip;
|
|
{
|
|
switch (dip->index) {
|
|
case EAP_MASTER_VOL:
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = EAP_OUTPUT_CLASS;
|
|
dip->prev = dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNmaster);
|
|
dip->un.v.num_channels = 2;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
return (0);
|
|
case EAP_VOICE_VOL:
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->prev = AUDIO_MIXER_LAST;
|
|
dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNdac);
|
|
dip->un.v.num_channels = 2;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
return (0);
|
|
case EAP_FM_VOL:
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->prev = AUDIO_MIXER_LAST;
|
|
dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNfmsynth);
|
|
dip->un.v.num_channels = 2;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
return (0);
|
|
case EAP_CD_VOL:
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->prev = AUDIO_MIXER_LAST;
|
|
dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNcd);
|
|
dip->un.v.num_channels = 2;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
return (0);
|
|
case EAP_LINE_VOL:
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->prev = AUDIO_MIXER_LAST;
|
|
dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNline);
|
|
dip->un.v.num_channels = 2;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
return (0);
|
|
case EAP_AUX_VOL:
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->prev = AUDIO_MIXER_LAST;
|
|
dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNaux);
|
|
dip->un.v.num_channels = 2;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
return (0);
|
|
case EAP_MIC_VOL:
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->prev = AUDIO_MIXER_LAST;
|
|
dip->next = EAP_MIC_PREAMP;
|
|
strcpy(dip->label.name, AudioNmicrophone);
|
|
dip->un.v.num_channels = 1;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
return (0);
|
|
case EAP_RECORD_SOURCE:
|
|
dip->mixer_class = EAP_RECORD_CLASS;
|
|
dip->prev = dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNsource);
|
|
dip->type = AUDIO_MIXER_SET;
|
|
dip->un.s.num_mem = 6;
|
|
strcpy(dip->un.s.member[0].label.name, AudioNmicrophone);
|
|
dip->un.s.member[0].mask = 1 << EAP_MIC_VOL;
|
|
strcpy(dip->un.s.member[1].label.name, AudioNcd);
|
|
dip->un.s.member[1].mask = 1 << EAP_CD_VOL;
|
|
strcpy(dip->un.s.member[2].label.name, AudioNline);
|
|
dip->un.s.member[2].mask = 1 << EAP_LINE_VOL;
|
|
strcpy(dip->un.s.member[3].label.name, AudioNfmsynth);
|
|
dip->un.s.member[3].mask = 1 << EAP_FM_VOL;
|
|
strcpy(dip->un.s.member[4].label.name, AudioNaux);
|
|
dip->un.s.member[4].mask = 1 << EAP_AUX_VOL;
|
|
strcpy(dip->un.s.member[5].label.name, AudioNdac);
|
|
dip->un.s.member[5].mask = 1 << EAP_VOICE_VOL;
|
|
return (0);
|
|
case EAP_OUTPUT_SELECT:
|
|
dip->mixer_class = EAP_OUTPUT_CLASS;
|
|
dip->prev = dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNselect);
|
|
dip->type = AUDIO_MIXER_SET;
|
|
dip->un.s.num_mem = 6;
|
|
strcpy(dip->un.s.member[0].label.name, AudioNmicrophone);
|
|
dip->un.s.member[0].mask = 1 << EAP_MIC_VOL;
|
|
strcpy(dip->un.s.member[1].label.name, AudioNcd);
|
|
dip->un.s.member[1].mask = 1 << EAP_CD_VOL;
|
|
strcpy(dip->un.s.member[2].label.name, AudioNline);
|
|
dip->un.s.member[2].mask = 1 << EAP_LINE_VOL;
|
|
strcpy(dip->un.s.member[3].label.name, AudioNfmsynth);
|
|
dip->un.s.member[3].mask = 1 << EAP_FM_VOL;
|
|
strcpy(dip->un.s.member[4].label.name, AudioNaux);
|
|
dip->un.s.member[4].mask = 1 << EAP_AUX_VOL;
|
|
strcpy(dip->un.s.member[5].label.name, AudioNdac);
|
|
dip->un.s.member[5].mask = 1 << EAP_VOICE_VOL;
|
|
return (0);
|
|
case EAP_MIC_PREAMP:
|
|
dip->type = AUDIO_MIXER_ENUM;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->prev = EAP_MIC_VOL;
|
|
dip->next = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNpreamp);
|
|
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;
|
|
return (0);
|
|
case EAP_OUTPUT_CLASS:
|
|
dip->type = AUDIO_MIXER_CLASS;
|
|
dip->mixer_class = EAP_OUTPUT_CLASS;
|
|
dip->next = dip->prev = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioCoutputs);
|
|
return (0);
|
|
case EAP_RECORD_CLASS:
|
|
dip->type = AUDIO_MIXER_CLASS;
|
|
dip->mixer_class = EAP_RECORD_CLASS;
|
|
dip->next = dip->prev = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioCrecord);
|
|
return (0);
|
|
case EAP_INPUT_CLASS:
|
|
dip->type = AUDIO_MIXER_CLASS;
|
|
dip->mixer_class = EAP_INPUT_CLASS;
|
|
dip->next = dip->prev = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioCinputs);
|
|
return (0);
|
|
}
|
|
return (ENXIO);
|
|
}
|
|
|
|
void *
|
|
eap_malloc(addr, direction, size, pool, flags)
|
|
void *addr;
|
|
int direction;
|
|
size_t size;
|
|
int pool, flags;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
struct eap_dma *p;
|
|
int error;
|
|
|
|
p = malloc(sizeof(*p), pool, flags);
|
|
if (!p)
|
|
return (0);
|
|
error = eap_allocmem(sc, size, 16, p);
|
|
if (error) {
|
|
free(p, pool);
|
|
return (0);
|
|
}
|
|
p->next = sc->sc_dmas;
|
|
sc->sc_dmas = p;
|
|
return (KERNADDR(p));
|
|
}
|
|
|
|
void
|
|
eap_free(addr, ptr, pool)
|
|
void *addr;
|
|
void *ptr;
|
|
int pool;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
struct eap_dma **pp, *p;
|
|
|
|
for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
|
|
if (KERNADDR(p) == ptr) {
|
|
eap_freemem(sc, p);
|
|
*pp = p->next;
|
|
free(p, pool);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t
|
|
eap_round_buffersize(addr, direction, size)
|
|
void *addr;
|
|
int direction;
|
|
size_t size;
|
|
{
|
|
return (size);
|
|
}
|
|
|
|
paddr_t
|
|
eap_mappage(addr, mem, off, prot)
|
|
void *addr;
|
|
void *mem;
|
|
off_t off;
|
|
int prot;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
struct eap_dma *p;
|
|
|
|
if (off < 0)
|
|
return (-1);
|
|
for (p = sc->sc_dmas; p && KERNADDR(p) != mem; p = p->next)
|
|
;
|
|
if (!p)
|
|
return (-1);
|
|
return (bus_dmamem_mmap(sc->sc_dmatag, p->segs, p->nsegs,
|
|
off, prot, BUS_DMA_WAITOK));
|
|
}
|
|
|
|
int
|
|
eap_get_props(addr)
|
|
void *addr;
|
|
{
|
|
return (AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT |
|
|
AUDIO_PROP_FULLDUPLEX);
|
|
}
|
|
|
|
#if NMIDI > 0
|
|
int
|
|
eap_midi_open(addr, flags, iintr, ointr, arg)
|
|
void *addr;
|
|
int flags;
|
|
void (*iintr)__P((void *, int));
|
|
void (*ointr)__P((void *));
|
|
void *arg;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
u_int32_t uctrl;
|
|
|
|
sc->sc_iintr = iintr;
|
|
sc->sc_ointr = ointr;
|
|
sc->sc_arg = arg;
|
|
|
|
EWRITE4(sc, EAP_ICSC, EREAD4(sc, EAP_ICSC) | EAP_UART_EN);
|
|
uctrl = 0;
|
|
if (flags & FREAD)
|
|
uctrl |= EAP_UC_RXINTEN;
|
|
#if 0
|
|
/* I don't understand ../midi.c well enough to use output interrupts */
|
|
if (flags & FWRITE)
|
|
uctrl |= EAP_UC_TXINTEN; */
|
|
#endif
|
|
EWRITE1(sc, EAP_UART_CONTROL, uctrl);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
eap_midi_close(addr)
|
|
void *addr;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
|
|
EWRITE1(sc, EAP_UART_CONTROL, 0);
|
|
EWRITE4(sc, EAP_ICSC, EREAD4(sc, EAP_ICSC) & ~EAP_UART_EN);
|
|
|
|
sc->sc_iintr = 0;
|
|
sc->sc_ointr = 0;
|
|
}
|
|
|
|
int
|
|
eap_midi_output(addr, d)
|
|
void *addr;
|
|
int d;
|
|
{
|
|
struct eap_softc *sc = addr;
|
|
int x;
|
|
|
|
for (x = 0; x != MIDI_BUSY_WAIT; x++) {
|
|
if (EREAD1(sc, EAP_UART_STATUS) & EAP_US_TXRDY) {
|
|
EWRITE1(sc, EAP_UART_DATA, d);
|
|
return (0);
|
|
}
|
|
delay(MIDI_BUSY_DELAY);
|
|
}
|
|
return (EIO);
|
|
}
|
|
|
|
void
|
|
eap_midi_getinfo(addr, mi)
|
|
void *addr;
|
|
struct midi_info *mi;
|
|
{
|
|
mi->name = "AudioPCI MIDI UART";
|
|
mi->props = MIDI_PROP_CAN_INPUT;
|
|
}
|
|
|
|
#endif
|