NetBSD/sys/dev/isa/pss.c

1546 lines
36 KiB
C

/* $NetBSD: pss.c,v 1.56 2001/07/18 20:39:53 thorpej Exp $ */
/*
* Copyright (c) 1994 John Brezak
* Copyright (c) 1991-1993 Regents of the University of California.
* 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 the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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) 1993 Analog Devices Inc. All rights reserved
*
* Portions provided by Marc.Hoffman@analog.com and
* Greg.Yukna@analog.com .
*
*/
/*
* Todo:
* - Provide PSS driver to access DSP
* - Provide MIDI driver to access MPU
* - Finish support for CD drive (Sony and SCSI)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/pio.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
#include <dev/isa/isavar.h>
#include <dev/isa/isadmavar.h>
#include <dev/isa/ad1848var.h>
#include <dev/isa/wssreg.h>
#include <dev/isa/pssreg.h>
/* XXX Default WSS base */
#define WSS_BASE_ADDRESS 0x0530
/*
* Mixer devices
*/
#define PSS_MIC_IN_LVL 0
#define PSS_LINE_IN_LVL 1
#define PSS_DAC_LVL 2
#define PSS_REC_LVL 3
#define PSS_MONITOR_LVL 4
#define PSS_MASTER_VOL 5
#define PSS_MASTER_TREBLE 6
#define PSS_MASTER_BASS 7
#define PSS_MIC_IN_MUTE 8
#define PSS_LINE_IN_MUTE 9
#define PSS_DAC_MUTE 10
#define PSS_MONITOR_MUTE 11
#define PSS_OUTPUT_MODE 12
#define PSS_SPKR_MONO 0
#define PSS_SPKR_STEREO 1
#define PSS_SPKR_PSEUDO 2
#define PSS_SPKR_SPATIAL 3
#define PSS_RECORD_SOURCE 13
/* Classes */
#define PSS_INPUT_CLASS 14
#define PSS_RECORD_CLASS 15
#define PSS_MONITOR_CLASS 16
#define PSS_OUTPUT_CLASS 17
struct pss_softc {
struct device sc_dev; /* base device */
void *sc_ih; /* interrupt vectoring */
int sc_iobase; /* I/O port base address */
int sc_drq; /* dma channel */
struct ad1848_softc *ad1848_sc;
int out_port;
struct ad1848_volume master_volume;
int master_mode;
int monitor_treble;
int monitor_bass;
int mic_mute, cd_mute, dac_mute;
};
#ifdef notyet
struct mpu_softc {
struct device sc_dev; /* base device */
void *sc_ih; /* interrupt vectoring */
int sc_iobase; /* MIDI I/O port base address */
int sc_irq; /* MIDI interrupt */
};
struct pcd_softc {
struct device sc_dev; /* base device */
void *sc_ih; /* interrupt vectoring */
int sc_iobase; /* CD I/O port base address */
int sc_irq; /* CD interrupt */
};
#endif
#ifdef AUDIO_DEBUG
#define DPRINTF(x) if (pssdebug) printf x
int pssdebug = 0;
#else
#define DPRINTF(x)
#endif
int pssprobe __P((struct device *, struct cfdata *, void *));
void pssattach __P((struct device *, struct device *, void *));
static int pssfind __P((struct device *, struct pss_softc *,
struct isa_attach_args *));
int spprobe __P((struct device *, struct cfdata *, void *));
void spattach __P((struct device *, struct device *, void *));
static int spfind __P((struct device *, struct ad1848_isa_softc *,
struct isa_attach_args *));
#ifdef notyet
int mpuprobe __P((struct device *, struct cfdata *, void *));
void mpuattach __P((struct device *, struct device *, void *));
int pcdprobe __P((struct device *, struct cfdata *, void *));
void pcdattach __P((struct device *, struct device *, void *));
#endif
int pssintr __P((void *));
#ifdef notyet
int mpuintr __P((void *));
#endif
int pss_speaker_ctl __P((void *, int));
int pss_getdev __P((void *, struct audio_device *));
int pss_mixer_set_port __P((void *, mixer_ctrl_t *));
int pss_mixer_get_port __P((void *, mixer_ctrl_t *));
int pss_query_devinfo __P((void *, mixer_devinfo_t *));
#ifdef PSS_DSP
void pss_dspwrite __P((struct pss_softc *, int));
#endif
void pss_setaddr __P((int, int));
int pss_setint __P((int, int));
int pss_setdma __P((int, int));
int pss_testirq __P((struct pss_softc *, int));
int pss_testdma __P((struct pss_softc *, int));
#ifdef notyet
int pss_reset_dsp __P((struct pss_softc *));
int pss_download_dsp __P((struct pss_softc *, u_char *, int));
#endif
#ifdef AUDIO_DEBUG
void pss_dump_regs __P((struct pss_softc *));
#endif
int pss_set_master_gain __P((struct pss_softc *, struct ad1848_volume *));
int pss_set_master_mode __P((struct pss_softc *, int));
int pss_set_treble __P((struct pss_softc *, u_int));
int pss_set_bass __P((struct pss_softc *, u_int));
int pss_get_master_gain __P((struct pss_softc *, struct ad1848_volume *));
int pss_get_master_mode __P((struct pss_softc *, u_int *));
int pss_get_treble __P((struct pss_softc *, u_char *));
int pss_get_bass __P((struct pss_softc *, u_char *));
#ifdef AUDIO_DEBUG
void wss_dump_regs __P((struct ad1848_isa_softc *));
#endif
/*
* Define our interface to the higher level audio driver.
*/
struct audio_hw_if pss_audio_if = {
ad1848_isa_open,
ad1848_isa_close,
NULL,
ad1848_query_encoding,
ad1848_set_params,
ad1848_round_blocksize,
ad1848_commit_settings,
NULL,
NULL,
NULL,
NULL,
ad1848_isa_halt_output,
ad1848_isa_halt_input,
pss_speaker_ctl,
pss_getdev,
NULL,
pss_mixer_set_port,
pss_mixer_get_port,
pss_query_devinfo,
ad1848_isa_malloc,
ad1848_isa_free,
ad1848_isa_round_buffersize,
ad1848_isa_mappage,
ad1848_isa_get_props,
ad1848_isa_trigger_output,
ad1848_isa_trigger_input,
};
/* Interrupt translation for WSS config */
static u_char wss_interrupt_bits[16] = {
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x08,
0xff, 0x10, 0x18, 0x20,
0xff, 0xff, 0xff, 0xff
};
/* ditto for WSS DMA channel */
static u_char wss_dma_bits[4] = {1, 2, 0, 3};
struct cfattach pss_ca = {
sizeof(struct pss_softc), pssprobe, pssattach
};
struct cfattach sp_ca = {
sizeof(struct ad1848_isa_softc), spprobe, spattach
};
#ifdef notyet
struct cfattach mpu_ca = {
sizeof(struct mpu_softc), mpuprobe, mpuattach
};
struct cfattach pcd_ca = {
sizeof(struct pcd_softc), pcdprobe, pcdattach
};
#endif
struct audio_device pss_device = {
"pss,ad1848",
"",
"PSS"
};
#ifdef PSS_DSP
void
pss_dspwrite(sc, data)
struct pss_softc *sc;
int data;
{
int i;
int pss_base = sc->sc_iobase;
/*
* Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
* called while interrupts are disabled. This means that the timer is
* disabled also. However the timeout situation is a abnormal condition.
* Normally the DSP should be ready to accept commands after just couple of
* loops.
*/
for (i = 0; i < 5000000; i++) {
if (inw(pss_base+PSS_STATUS) & PSS_WRITE_EMPTY) {
outw(pss_base+PSS_DATA, data);
return;
}
}
printf ("pss: DSP Command (%04x) Timeout.\n", data);
}
#endif /* PSS_DSP */
void
pss_setaddr(addr, configAddr)
int addr;
int configAddr;
{
int val;
val = inw(configAddr);
val &= ADDR_MASK;
val |= (addr << 4);
outw(configAddr,val);
}
/* pss_setint
* This function sets the correct bits in the
* configuration register to
* enable the chosen interrupt.
*/
int
pss_setint(intNum, configAddress)
int intNum;
int configAddress;
{
int val;
switch(intNum) {
case 3:
val = inw(configAddress);
val &= INT_MASK;
val |= INT_3_BITS;
break;
case 5:
val = inw(configAddress);
val &= INT_MASK;
val |= INT_5_BITS;
break;
case 7:
val = inw(configAddress);
val &= INT_MASK;
val |= INT_7_BITS;
break;
case 9:
val = inw(configAddress);
val &= INT_MASK;
val |= INT_9_BITS;
break;
case 10:
val = inw(configAddress);
val &= INT_MASK;
val |= INT_10_BITS;
break;
case 11:
val = inw(configAddress);
val &= INT_MASK;
val |= INT_11_BITS;
break;
case 12:
val = inw(configAddress);
val &= INT_MASK;
val |= INT_12_BITS;
break;
default:
DPRINTF(("pss_setint: invalid irq (%d)\n", intNum));
return 1;
}
outw(configAddress,val);
return 0;
}
int
pss_setdma(dmaNum, configAddress)
int dmaNum;
int configAddress;
{
int val;
switch(dmaNum) {
case 0:
val = inw(configAddress);
val &= DMA_MASK;
val |= DMA_0_BITS;
break;
case 1:
val = inw(configAddress);
val &= DMA_MASK;
val |= DMA_1_BITS;
break;
case 3:
val = inw(configAddress);
val &= DMA_MASK;
val |= DMA_3_BITS;
break;
case 5:
val = inw(configAddress);
val &= DMA_MASK;
val |= DMA_5_BITS;
break;
case 6:
val = inw(configAddress);
val &= DMA_MASK;
val |= DMA_6_BITS;
break;
case 7:
val = inw(configAddress);
val &= DMA_MASK;
val |= DMA_7_BITS;
break;
default:
DPRINTF(("pss_setdma: invalid drq (%d)\n", dmaNum));
return 1;
}
outw(configAddress, val);
return 0;
}
/*
* This function tests an interrupt number to see if
* it is available. It takes the interrupt button
* as its argument and returns TRUE if the interrupt
* is ok.
*/
int
pss_testirq(struct pss_softc *sc, int intNum)
{
int config = sc->sc_iobase + PSS_CONFIG;
int val;
int ret;
int i;
/* Set the interrupt bits */
switch(intNum) {
case 3:
val = inw(config);
val &= INT_MASK; /* Special: 0 */
break;
case 5:
val = inw(config);
val &= INT_MASK;
val |= INT_TEST_BIT | INT_5_BITS;
break;
case 7:
val = inw(config);
val &= INT_MASK;
val |= INT_TEST_BIT | INT_7_BITS;
break;
case 9:
val = inw(config);
val &= INT_MASK;
val |= INT_TEST_BIT | INT_9_BITS;
break;
case 10:
val = inw(config);
val &= INT_MASK;
val |= INT_TEST_BIT | INT_10_BITS;
break;
case 11:
val = inw(config);
val &= INT_MASK;
val |= INT_TEST_BIT | INT_11_BITS;
break;
case 12:
val = inw(config);
val &= INT_MASK;
val |= INT_TEST_BIT | INT_12_BITS;
break;
default:
DPRINTF(("pss_testirq: invalid irq (%d)\n", intNum));
return 0;
}
outw(config, val);
/* Check if the interrupt is in use */
/* Do it a few times in case there is a delay */
ret = 0;
for (i = 0; i < 5; i++) {
val = inw(config);
if (val & INT_TEST_PASS) {
ret = 1;
break;
}
}
/* Clear the Test bit and the interrupt bits */
val = inw(config);
val &= INT_TEST_BIT_MASK & INT_MASK;
outw(config, val);
return(ret);
}
/*
* This function tests a dma channel to see if
* it is available. It takes the DMA channel button
* as its argument and returns TRUE if the channel
* is ok.
*/
int
pss_testdma(sc, dmaNum)
struct pss_softc *sc;
int dmaNum;
{
int config = sc->sc_iobase + PSS_CONFIG;
int val;
int i, ret;
switch (dmaNum) {
case 0:
val = inw(config);
val &= DMA_MASK;
val |= DMA_TEST_BIT | DMA_0_BITS;
break;
case 1:
val = inw(config);
val &= DMA_MASK;
val |= DMA_TEST_BIT | DMA_1_BITS;
break;
case 3:
val = inw(config);
val &= DMA_MASK;
val |= DMA_TEST_BIT | DMA_3_BITS;
break;
case 5:
val = inw(config);
val &= DMA_MASK;
val |= DMA_TEST_BIT | DMA_5_BITS;
break;
case 6:
val = inw(config);
val &= DMA_MASK;
val |= DMA_TEST_BIT | DMA_6_BITS;
break;
case 7:
val = inw(config);
val &= DMA_MASK;
val |= DMA_TEST_BIT | DMA_7_BITS;
break;
default:
DPRINTF(("pss_testdma: invalid drq (%d)\n", dmaNum));
return 0;
}
outw(config, val);
/* Check if the DMA channel is in use */
/* Do it a few times in case there is a delay */
ret = 0;
for (i = 0; i < 3; i++) {
val = inw(config);
if (val & DMA_TEST_PASS) {
ret = 1;
break;
}
}
/* Clear the Test bit and the DMA bits */
val = inw(config);
val &= DMA_TEST_BIT_MASK & DMA_MASK;
outw(config, val);
return(ret);
}
#ifdef notyet
int
pss_reset_dsp(sc)
struct pss_softc *sc;
{
u_long i;
int pss_base = sc->sc_iobase;
outw(pss_base+PSS_CONTROL, PSS_RESET);
for (i = 0; i < 32768; i++)
inw(pss_base+PSS_CONTROL);
outw(pss_base+PSS_CONTROL, 0);
return 1;
}
/*
* This function loads an image into the PSS
* card. The function loads the file by
* resetting the dsp and feeding it the boot bytes.
* First you feed the ASIC the first byte of
* the boot sequence. The ASIC waits until it
* detects a BMS and RD and asserts BR
* and outputs the byte. The host must poll for
* the BG signal. It then feeds the ASIC another
* byte which removes BR.
*/
int
pss_download_dsp(sc, block, size)
struct pss_softc *sc;
u_char *block;
int size;
{
int i, val, count;
int pss_base = sc->sc_iobase;
DPRINTF(("pss: downloading boot code..."));
/* Warn DSP software that a boot is coming */
outw(pss_base+PSS_DATA, 0x00fe);
for (i = 0; i < 32768; i++)
if (inw(pss_base+PSS_DATA) == 0x5500)
break;
outw(pss_base+PSS_DATA, *block++);
pss_reset_dsp(sc);
DPRINTF(("start "));
count = 1;
while(1) {
int j;
for (j=0; j<327670; j++) {
/* Wait for BG to appear */
if (inw(pss_base+PSS_STATUS) & PSS_FLAG3)
break;
}
if (j==327670) {
/* It's ok we timed out when the file was empty */
if (count >= size)
break;
else {
printf("\npss: DownLoad timeout problems, byte %d=%d\n",
count, size);
return 0;
}
}
/* Send the next byte */
outw(pss_base+PSS_DATA, *block++);
count++;
}
outw(pss_base+PSS_DATA, 0);
for (i = 0; i < 32768; i++)
(void) inw(pss_base+PSS_STATUS);
DPRINTF(("downloaded\n"));
for (i = 0; i < 32768; i++) {
val = inw(pss_base+PSS_STATUS);
if (val & PSS_READ_FULL)
break;
}
/* now read the version */
for (i = 0; i < 32000; i++) {
val = inw(pss_base+PSS_STATUS);
if (val & PSS_READ_FULL)
break;
}
if (i == 32000)
return 0;
(void) inw(pss_base+PSS_DATA);
return 1;
}
#endif /* notyet */
#ifdef AUDIO_DEBUG
void
wss_dump_regs(sc)
struct ad1848_isa_softc *sc;
{
printf("WSS reg: status=%02x\n",
(u_char)inb(sc->sc_iobase-WSS_CODEC+WSS_STATUS));
}
void
pss_dump_regs(sc)
struct pss_softc *sc;
{
printf("PSS regs: status=%04x vers=%04x ",
(u_short)inw(sc->sc_iobase+PSS_STATUS),
(u_short)inw(sc->sc_iobase+PSS_ID_VERS));
printf("config=%04x wss_config=%04x\n",
(u_short)inw(sc->sc_iobase+PSS_CONFIG),
(u_short)inw(sc->sc_iobase+PSS_WSS_CONFIG));
}
#endif
/*
* Probe for the PSS hardware.
*/
int
pssprobe(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
struct pss_softc probesc, *sc = &probesc;
memset(sc, 0, sizeof *sc);
sc->sc_dev.dv_cfdata = match;
strcpy(sc->sc_dev.dv_xname, "pas");
return pssfind(parent, sc, aux);
}
static int
pssfind(parent, sc, ia)
struct device *parent;
struct pss_softc *sc;
struct isa_attach_args *ia;
{
int iobase = ia->ia_iobase;
if (!PSS_BASE_VALID(iobase)) {
printf("pss: configured iobase %x invalid\n", iobase);
return 0;
}
/* Need to probe for iobase when IOBASEUNK {0x220 0x240} */
if (iobase == IOBASEUNK) {
iobase = 0x220;
if ((inw(iobase+PSS_ID_VERS) & 0xff00) == 0x4500)
goto pss_found;
iobase = 0x240;
if ((inw(iobase+PSS_ID_VERS) & 0xff00) == 0x4500)
goto pss_found;
DPRINTF(("pss: no PSS found (at 0x220 or 0x240)\n"));
return 0;
}
else if ((inw(iobase+PSS_ID_VERS) & 0xff00) != 0x4500) {
DPRINTF(("pss: not a PSS - %x\n", inw(iobase+PSS_ID_VERS)));
return 0;
}
pss_found:
sc->sc_iobase = iobase;
/* Clear WSS config */
pss_setaddr(WSS_BASE_ADDRESS, sc->sc_iobase+PSS_WSS_CONFIG); /* XXX! */
outb(WSS_BASE_ADDRESS+WSS_CONFIG, 0);
/* Clear config registers (POR reset state) */
outw(sc->sc_iobase+PSS_CONFIG, 0);
outw(sc->sc_iobase+PSS_WSS_CONFIG, 0);
outw(sc->sc_iobase+SB_CONFIG, 0);
outw(sc->sc_iobase+MIDI_CONFIG, 0);
outw(sc->sc_iobase+CD_CONFIG, 0);
if (ia->ia_irq == IRQUNK) {
int i;
for (i = 0; i < 16; i++) {
if (pss_testirq(sc, i) != 0)
break;
}
if (i == 16) {
printf("pss: unable to locate free IRQ channel\n");
return 0;
}
else {
ia->ia_irq = i;
printf("pss: found IRQ %d free\n", i);
}
}
else {
if (pss_testirq(sc, ia->ia_irq) == 0) {
printf("pss: configured IRQ unavailable (%d)\n", ia->ia_irq);
return 0;
}
}
/* XXX Need to deal with DRQUNK */
if (pss_testdma(sc, ia->ia_drq) == 0) {
printf("pss: configured DMA channel unavailable (%d)\n", ia->ia_drq);
return 0;
}
ia->ia_iosize = PSS_NPORT;
/* Initialize PSS irq and dma */
pss_setint(ia->ia_irq, sc->sc_iobase+PSS_CONFIG);
pss_setdma(sc->sc_drq, sc->sc_iobase+PSS_CONFIG);
#ifdef notyet
/* Setup the Game port */
#ifdef PSS_GAMEPORT
DPRINTF(("Turning Game Port On.\n"));
outw(sc->sc_iobase+PSS_STATUS, inw(sc->sc_iobase+PSS_STATUS) | GAME_BIT);
#else
outw(sc->sc_iobase+PSS_STATUS, inw(sc->sc_iobase+PSS_STATUS) & GAME_BIT_MASK);
#endif
/* Reset DSP */
pss_reset_dsp(sc);
#endif /* notyet */
return 1;
}
/*
* Probe for the Soundport (ad1848)
*/
int
spprobe(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
struct ad1848_isa_softc probesc, *sc = &probesc;
memset(sc, 0, sizeof *sc);
sc->sc_ad1848.sc_dev.dv_cfdata = match;
return spfind(parent, sc, aux);
}
static int
spfind(parent, sc, ia)
struct device *parent;
struct ad1848_isa_softc *sc;
struct isa_attach_args *ia;
{
struct pss_softc *pc = (void *) parent;
struct cfdata *cf = (void *)sc->sc_ad1848.sc_dev.dv_cfdata;
u_char bits;
int i;
sc->sc_ad1848.sc_iot = ia->ia_iot;
/* Set WSS io address */
pss_setaddr(cf->cf_iobase, pc->sc_iobase+PSS_WSS_CONFIG);
/* Is there an ad1848 chip at the WSS iobase ? */
if (ad1848_isa_mapprobe(sc, cf->cf_iobase + WSS_CODEC) == 0) {
DPRINTF(("sp: no ad1848 ? iobase=%x\n", sc->sc_iobase));
return 0;
}
/* Setup WSS interrupt and DMA if auto */
if (cf->cf_irq == IRQUNK) {
/* Find unused IRQ for WSS */
for (i = 0; i < 12; i++) {
if (wss_interrupt_bits[i] != 0xff) {
if (pss_testirq(pc, i))
break;
}
}
if (i == 12) {
printf("sp: unable to locate free IRQ for WSS\n");
return 0;
}
else {
cf->cf_irq = i;
sc->sc_irq = i;
DPRINTF(("sp: found IRQ %d free\n", i));
}
}
else {
sc->sc_irq = cf->cf_irq;
if (pss_testirq(pc, sc->sc_irq) == 0) {
printf("sp: configured IRQ unavailable (%d)\n", sc->sc_irq);
return 0;
}
}
if (cf->cf_drq == DRQUNK) {
/* Find unused DMA channel for WSS */
for (i = 0; i < 4; i++) {
if (wss_dma_bits[i]) {
if (pss_testdma(pc, i))
break;
}
}
if (i == 4) {
printf("sp: unable to locate free DMA channel for WSS\n");
return 0;
}
else {
sc->sc_playdrq = cf->cf_drq = i;
DPRINTF(("sp: found DMA %d free\n", i));
}
}
else {
if (pss_testdma(pc, sc->sc_playdrq) == 0) {
printf("sp: configured DMA channel unavailable (%d)\n", sc->sc_playdrq);
return 0;
}
sc->sc_playdrq = cf->cf_drq;
}
sc->sc_recdrq = sc->sc_playdrq;
/* Set WSS config registers */
if ((bits = wss_interrupt_bits[sc->sc_irq]) == 0xff) {
printf("sp: invalid interrupt configuration (irq=%d)\n", sc->sc_irq);
return 0;
}
outb(sc->sc_iobase+WSS_CONFIG, (bits | 0x40));
if ((inb(sc->sc_iobase+WSS_STATUS) & 0x40) == 0) /* XXX What do these bits mean ? */
DPRINTF(("sp: IRQ %x\n", inb(sc->sc_iobase+WSS_STATUS)));
outb(sc->sc_iobase+WSS_CONFIG, (bits | wss_dma_bits[sc->sc_playdrq]));
pc->ad1848_sc = (struct ad1848_softc *)sc;
sc->sc_ad1848.parent = pc;
return 1;
}
#ifdef notyet
int
mpuprobe(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
return(0);
}
int
pcdprobe(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
return(0);
}
#endif /* notyet */
/*
* Attach hardware to driver, attach hardware driver to audio
* pseudo-device driver .
*/
void
pssattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct pss_softc *sc = (struct pss_softc *)self;
struct isa_attach_args *ia = (struct isa_attach_args *)aux;
int iobase = ia->ia_iobase;
u_char vers;
struct ad1848_volume vol = {150, 150};
if (!pssfind(parent, sc, ia)) {
printf("%s: pssfind failed\n", sc->sc_dev.dv_xname);
return;
}
sc->sc_iobase = iobase;
sc->sc_drq = ia->ia_drq;
/* Setup interrupt handler for PSS */
sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, IPL_AUDIO,
pssintr, sc);
vers = (inw(sc->sc_iobase+PSS_ID_VERS)&0xff) - 1;
printf(": ESC614%c\n", (vers > 0)?'A'+vers:' ');
(void)config_found(self, ia, NULL); /* XXX */
sc->out_port = PSS_MASTER_VOL;
(void)pss_set_master_mode(sc, PSS_SPKR_STEREO);
(void)pss_set_master_gain(sc, &vol);
(void)pss_set_treble(sc, AUDIO_MAX_GAIN/2);
(void)pss_set_bass(sc, AUDIO_MAX_GAIN/2);
audio_attach_mi(&pss_audio_if, sc->ad1848_sc, &sc->ad1848_sc->sc_dev);
}
void
spattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct ad1848_isa_softc *sc = (struct ad1848_isa_softc *)self;
struct cfdata *cf = (void *)sc->sc_ad1848.sc_dev.dv_cfdata;
struct isa_attach_args *ia = (struct isa_attach_args *)aux;
isa_chipset_tag_t ic = ia->ia_ic;
int iobase = cf->cf_iobase;
if (!spfind(parent, sc, ia)) {
printf("%s: spfind failed\n", sc->sc_ad1848.sc_dev.dv_xname);
return;
}
sc->sc_iobase = iobase;
sc->sc_playdrq = cf->cf_drq;
sc->sc_ih = isa_intr_establish(ic, cf->cf_irq, IST_EDGE, IPL_AUDIO,
ad1848_isa_intr, sc);
sc->sc_ic = ic;
ad1848_attach(&sc->sc_ad1848);
}
#ifdef notyet
void
mpuattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct mpu_softc *sc = (struct mpu_softc *)self;
struct cfdata *cf = (void *)sc->sc_dev.dv_cfdata;
isa_chipset_tag_t ic = aux; /* XXX */
int iobase = cf->cf_iobase;
sc->sc_iobase = iobase;
sc->sc_ih = isa_intr_establish(ic, cf->cf_irq, IST_EDGE, IPL_AUDIO,
mpuintr, sc, sc->sc_dev.dv_xname);
/* XXX might use pssprint func ?? */
printf(" port 0x%x-0x%x irq %d\n",
sc->sc_iobase, sc->sc_iobase+MIDI_NPORT,
cf->cf_irq);
}
void
pcdattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct pcd_softc *sc = (struct pcd_softc *)self;
struct cfdata *cf = (void *)sc->sc_dev.dv_cfdata;
int iobase = cf->cf_iobase;
/*
* The pss driver simply enables the cd interface. The CD
* appropriate driver - scsi (aic6360) or Sony needs to be
* used after this to handle the device.
*/
sc->sc_iobase = iobase;
/* XXX might use pssprint func ?? */
printf(" port 0x%x-0x%x irq %d\n",
sc->sc_iobase, sc->sc_iobase+2,
cf->cf_irq);
}
#endif /* notyet */
int
pss_set_master_gain(sc, gp)
struct pss_softc *sc;
struct ad1848_volume *gp;
{
DPRINTF(("pss_set_master_gain: %d:%d\n", gp->left, gp->right));
#ifdef PSS_DSP
if (gp->left > PHILLIPS_VOL_MAX)
gp->left = PHILLIPS_VOL_MAX;
if (gp->left < PHILLIPS_VOL_MIN)
gp->left = PHILLIPS_VOL_MIN;
if (gp->right > PHILLIPS_VOL_MAX)
gp->right = PHILLIPS_VOL_MAX;
if (gp->right < PHILLIPS_VOL_MIN)
gp->right = PHILLIPS_VOL_MIN;
pss_dspwrite(sc, SET_MASTER_COMMAND);
pss_dspwrite(sc, MASTER_VOLUME_LEFT|(PHILLIPS_VOL_CONSTANT + gp->left / PHILLIPS_VOL_STEP));
pss_dspwrite(sc, SET_MASTER_COMMAND);
pss_dspwrite(sc, MASTER_VOLUME_RIGHT|(PHILLIPS_VOL_CONSTANT + gp->right / PHILLIPS_VOL_STEP));
#endif
sc->master_volume = *gp;
return(0);
}
int
pss_set_master_mode(sc, mode)
struct pss_softc *sc;
int mode;
{
short phillips_mode;
DPRINTF(("pss_set_master_mode: %d\n", mode));
if (mode == PSS_SPKR_STEREO)
phillips_mode = PSS_STEREO;
else if (mode == PSS_SPKR_PSEUDO)
phillips_mode = PSS_PSEUDO;
else if (mode == PSS_SPKR_SPATIAL)
phillips_mode = PSS_SPATIAL;
else if (mode == PSS_SPKR_MONO)
phillips_mode = PSS_MONO;
else
return (EINVAL);
#ifdef PSS_DSP
pss_dspwrite(sc, SET_MASTER_COMMAND);
pss_dspwrite(sc, MASTER_SWITCH | mode);
#endif
sc->master_mode = mode;
return(0);
}
int
pss_set_treble(sc, treb)
struct pss_softc *sc;
u_int treb;
{
DPRINTF(("pss_set_treble: %d\n", treb));
#ifdef PSS_DSP
if (treb > PHILLIPS_TREBLE_MAX)
treb = PHILLIPS_TREBLE_MAX;
if (treb < PHILLIPS_TREBLE_MIN)
treb = PHILLIPS_TREBLE_MIN;
pss_dspwrite(sc, SET_MASTER_COMMAND);
pss_dspwrite(sc, MASTER_TREBLE|(PHILLIPS_TREBLE_CONSTANT + treb / PHILLIPS_TREBLE_STEP));
#endif
sc->monitor_treble = treb;
return(0);
}
int
pss_set_bass(sc, bass)
struct pss_softc *sc;
u_int bass;
{
DPRINTF(("pss_set_bass: %d\n", bass));
#ifdef PSS_DSP
if (bass > PHILLIPS_BASS_MAX)
bass = PHILLIPS_BASS_MAX;
if (bass < PHILLIPS_BASS_MIN)
bass = PHILLIPS_BASS_MIN;
pss_dspwrite(sc, SET_MASTER_COMMAND);
pss_dspwrite(sc, MASTER_BASS|(PHILLIPS_BASS_CONSTANT + bass / PHILLIPS_BASS_STEP));
#endif
sc->monitor_bass = bass;
return(0);
}
int
pss_get_master_gain(sc, gp)
struct pss_softc *sc;
struct ad1848_volume *gp;
{
*gp = sc->master_volume;
return(0);
}
int
pss_get_master_mode(sc, mode)
struct pss_softc *sc;
u_int *mode;
{
*mode = sc->master_mode;
return(0);
}
int
pss_get_treble(sc, tp)
struct pss_softc *sc;
u_char *tp;
{
*tp = sc->monitor_treble;
return(0);
}
int
pss_get_bass(sc, bp)
struct pss_softc *sc;
u_char *bp;
{
*bp = sc->monitor_bass;
return(0);
}
int
pss_speaker_ctl(addr, newstate)
void *addr;
int newstate;
{
return(0);
}
int
pssintr(arg)
void *arg;
{
struct pss_softc *sc = arg;
u_short sr;
sr = inw(sc->sc_iobase+PSS_STATUS);
DPRINTF(("pssintr: sc=%p st=%x\n", sc, sr));
/* Acknowledge intr */
outw(sc->sc_iobase+PSS_IRQ_ACK, 0);
/* Is it one of ours ? */
if (sr & (PSS_WRITE_EMPTY|PSS_READ_FULL|PSS_IRQ|PSS_DMQ_TC)) {
/* XXX do something */
return 1;
}
return 0;
}
#ifdef notyet
int
mpuintr(arg)
void *arg;
{
struct mpu_softc *sc = arg;
u_char sr;
sr = inb(sc->sc_iobase+MIDI_STATUS_REG);
printf("mpuintr: sc=%p sr=%x\n", sc, sr);
/* XXX Need to clear intr */
return 1;
}
#endif
int
pss_getdev(addr, retp)
void *addr;
struct audio_device *retp;
{
DPRINTF(("pss_getdev: retp=%p\n", retp));
*retp = pss_device;
return 0;
}
static ad1848_devmap_t mappings[] = {
{ PSS_MIC_IN_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL },
{ PSS_LINE_IN_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL },
{ PSS_DAC_LVL, AD1848_KIND_LVL, AD1848_DAC_CHANNEL },
{ PSS_MONITOR_LVL, AD1848_KIND_LVL, AD1848_MONO_CHANNEL },
{ PSS_MIC_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL },
{ PSS_LINE_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL },
{ PSS_DAC_MUTE, AD1848_KIND_MUTE, AD1848_DAC_CHANNEL },
{ PSS_MONITOR_MUTE, AD1848_KIND_MUTE, AD1848_MONO_CHANNEL },
{ PSS_REC_LVL, AD1848_KIND_RECORDGAIN, -1 },
{ PSS_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1}
};
static int nummap = sizeof(mappings) / sizeof(mappings[0]);
int
pss_mixer_set_port(addr, cp)
void *addr;
mixer_ctrl_t *cp;
{
struct ad1848_softc *ac = addr;
struct pss_softc *sc = ac->parent;
struct ad1848_volume vol;
int error = ad1848_mixer_set_port(ac, mappings, nummap, cp);
if (error != ENXIO)
return (error);
switch (cp->dev) {
case PSS_MASTER_VOL: /* master volume */
if (cp->type == AUDIO_MIXER_VALUE) {
if (ad1848_to_vol(cp, &vol))
error = pss_set_master_gain(sc, &vol);
}
break;
case PSS_OUTPUT_MODE:
if (cp->type == AUDIO_MIXER_ENUM)
error = pss_set_master_mode(sc, cp->un.ord);
break;
case PSS_MASTER_TREBLE: /* master treble */
if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1)
error = pss_set_treble(sc, (u_char)cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
break;
case PSS_MASTER_BASS: /* master bass */
if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1)
error = pss_set_bass(sc, (u_char)cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
break;
default:
return ENXIO;
/*NOTREACHED*/
}
return 0;
}
int
pss_mixer_get_port(addr, cp)
void *addr;
mixer_ctrl_t *cp;
{
struct ad1848_softc *ac = addr;
struct pss_softc *sc = ac->parent;
struct ad1848_volume vol;
u_char eq;
int error = ad1848_mixer_get_port(ac, mappings, nummap, cp);
if (error != ENXIO)
return (error);
error = EINVAL;
switch (cp->dev) {
case PSS_MASTER_VOL: /* master volume */
if (cp->type == AUDIO_MIXER_VALUE) {
error = pss_get_master_gain(sc, &vol);
if (!error)
ad1848_from_vol(cp, &vol);
}
break;
case PSS_MASTER_TREBLE: /* master treble */
if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
error = pss_get_treble(sc, &eq);
if (!error)
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = eq;
}
break;
case PSS_MASTER_BASS: /* master bass */
if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
error = pss_get_bass(sc, &eq);
if (!error)
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = eq;
}
break;
case PSS_OUTPUT_MODE:
if (cp->type == AUDIO_MIXER_ENUM)
error = pss_get_master_mode(sc, &cp->un.ord);
break;
default:
error = ENXIO;
break;
}
return(error);
}
int
pss_query_devinfo(addr, dip)
void *addr;
mixer_devinfo_t *dip;
{
DPRINTF(("pss_query_devinfo: index=%d\n", dip->index));
switch(dip->index) {
case PSS_MIC_IN_LVL: /* Microphone */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = PSS_MIC_IN_MUTE;
strcpy(dip->label.name, AudioNmicrophone);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case PSS_LINE_IN_LVL: /* line/CD */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = PSS_LINE_IN_MUTE;
strcpy(dip->label.name, AudioNcd);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case PSS_DAC_LVL: /* dacout */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = PSS_DAC_MUTE;
strcpy(dip->label.name, AudioNdac);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case PSS_REC_LVL: /* record level */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_RECORD_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = PSS_RECORD_SOURCE;
strcpy(dip->label.name, AudioNrecord);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case PSS_MONITOR_LVL: /* monitor level */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_MONITOR_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = PSS_MONITOR_MUTE;
strcpy(dip->label.name, AudioNmonitor);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case PSS_MASTER_VOL: /* master volume */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_OUTPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = PSS_OUTPUT_MODE;
strcpy(dip->label.name, AudioNmaster);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case PSS_MASTER_TREBLE: /* master treble */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_OUTPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNtreble);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNtreble);
break;
case PSS_MASTER_BASS: /* master bass */
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = PSS_OUTPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNbass);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNbass);
break;
case PSS_OUTPUT_CLASS: /* output class descriptor */
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = PSS_OUTPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCoutputs);
break;
case PSS_INPUT_CLASS: /* input class descriptor */
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = PSS_INPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCinputs);
break;
case PSS_MONITOR_CLASS: /* monitor class descriptor */
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = PSS_MONITOR_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCmonitor);
break;
case PSS_RECORD_CLASS: /* record source class */
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = PSS_RECORD_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCrecord);
break;
case PSS_MIC_IN_MUTE:
dip->mixer_class = PSS_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = PSS_MIC_IN_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case PSS_LINE_IN_MUTE:
dip->mixer_class = PSS_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = PSS_LINE_IN_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case PSS_DAC_MUTE:
dip->mixer_class = PSS_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = PSS_DAC_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case PSS_MONITOR_MUTE:
dip->mixer_class = PSS_MONITOR_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = PSS_MONITOR_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 PSS_OUTPUT_MODE:
dip->mixer_class = PSS_OUTPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = PSS_MASTER_VOL;
dip->next = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNmode);
dip->un.e.num_mem = 4;
strcpy(dip->un.e.member[0].label.name, AudioNmono);
dip->un.e.member[0].ord = PSS_SPKR_MONO;
strcpy(dip->un.e.member[1].label.name, AudioNstereo);
dip->un.e.member[1].ord = PSS_SPKR_STEREO;
strcpy(dip->un.e.member[2].label.name, AudioNpseudo);
dip->un.e.member[2].ord = PSS_SPKR_PSEUDO;
strcpy(dip->un.e.member[3].label.name, AudioNspatial);
dip->un.e.member[3].ord = PSS_SPKR_SPATIAL;
break;
case PSS_RECORD_SOURCE:
dip->mixer_class = PSS_RECORD_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = PSS_REC_LVL;
dip->next = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNsource);
dip->un.e.num_mem = 3;
strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
dip->un.e.member[0].ord = PSS_MIC_IN_LVL;
strcpy(dip->un.e.member[1].label.name, AudioNcd);
dip->un.e.member[1].ord = PSS_LINE_IN_LVL;
strcpy(dip->un.e.member[2].label.name, AudioNdac);
dip->un.e.member[2].ord = PSS_DAC_LVL;
break;
default:
return ENXIO;
/*NOTREACHED*/
}
DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
return 0;
}