1857 lines
42 KiB
C
1857 lines
42 KiB
C
/* $NetBSD: pss.c,v 1.1 1995/02/21 04:15:03 brezak 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.
|
|
*
|
|
* $Id: pss.c,v 1.1 1995/02/21 04:15:03 brezak Exp $
|
|
*/
|
|
|
|
/*
|
|
* 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/errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/device.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/buf.h>
|
|
|
|
#include <machine/cpu.h>
|
|
#include <machine/pio.h>
|
|
|
|
#include <sys/audioio.h>
|
|
#include <dev/audio_if.h>
|
|
|
|
#include <i386/isa/isavar.h>
|
|
#include <i386/isa/dmavar.h>
|
|
#include <i386/isa/icu.h>
|
|
|
|
#include <i386/isa/ad1848var.h>
|
|
#include <i386/isa/wssreg.h>
|
|
#include <i386/isa/pssreg.h>
|
|
|
|
/*
|
|
* 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_MON_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_OUTPUT_MODE 11
|
|
#define PSS_SPKR_MONO 0
|
|
#define PSS_SPKR_STEREO 1
|
|
#define PSS_SPKR_PSEUDO 2
|
|
#define PSS_SPKR_SPATIAL 3
|
|
|
|
#define PSS_RECORD_SOURCE 12
|
|
|
|
/* Classes */
|
|
#define PSS_INPUT_CLASS 13
|
|
#define PSS_RECORD_CLASS 14
|
|
#define PSS_MONITOR_CLASS 15
|
|
#define PSS_OUTPUT_CLASS 16
|
|
|
|
|
|
struct pss_softc {
|
|
struct device sc_dev; /* base device */
|
|
struct isadev sc_id; /* ISA device */
|
|
struct intrhand sc_ih; /* interrupt vectoring */
|
|
|
|
u_short sc_iobase; /* I/O port base address */
|
|
u_short 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;
|
|
};
|
|
|
|
struct mpu_softc {
|
|
struct device sc_dev; /* base device */
|
|
struct isadev sc_id; /* ISA device */
|
|
struct intrhand sc_ih; /* interrupt vectoring */
|
|
|
|
u_short sc_iobase; /* MIDI I/O port base address */
|
|
u_short sc_irq; /* MIDI interrupt */
|
|
};
|
|
|
|
struct cd_softc {
|
|
struct device sc_dev; /* base device */
|
|
struct isadev sc_id; /* ISA device */
|
|
struct intrhand sc_ih; /* interrupt vectoring */
|
|
|
|
u_short sc_iobase; /* CD I/O port base address */
|
|
u_short sc_irq; /* CD interrupt */
|
|
};
|
|
|
|
#define DEBUG /*XXX*/
|
|
#ifdef DEBUG
|
|
#define DPRINTF(x) if (pssdebug) printf x
|
|
int pssdebug = 0;
|
|
#else
|
|
#define DPRINTF(x)
|
|
#endif
|
|
|
|
int pssprobe();
|
|
void pssattach __P((struct device *, struct device *, void *));
|
|
|
|
int spprobe();
|
|
void spattach __P((struct device *, struct device *, void *));
|
|
|
|
int mpuprobe();
|
|
void mpuattach __P((struct device *, struct device *, void *));
|
|
|
|
int pcdprobe();
|
|
void pcdattach __P((struct device *, struct device *, void *));
|
|
|
|
int spopen __P((dev_t, int));
|
|
|
|
int pssintr __P((struct pss_softc *));
|
|
int mpuintr __P((struct mpu_softc *));
|
|
|
|
int pss_speaker_ctl __P((caddr_t, int));
|
|
|
|
int pss_getdev __P((caddr_t, struct audio_device *));
|
|
int pss_setfd __P((caddr_t, int));
|
|
|
|
int pss_set_out_port __P((caddr_t, int));
|
|
int pss_get_out_port __P((caddr_t));
|
|
int pss_set_in_port __P((caddr_t, int));
|
|
int pss_get_in_port __P((caddr_t));
|
|
int pss_mixer_set_port __P((caddr_t, mixer_ctrl_t *));
|
|
int pss_mixer_get_port __P((caddr_t, mixer_ctrl_t *));
|
|
int pss_query_devinfo __P((caddr_t, mixer_devinfo_t *));
|
|
|
|
/*
|
|
* Define our interface to the higher level audio driver.
|
|
*/
|
|
|
|
struct audio_hw_if pss_audio_if = {
|
|
spopen,
|
|
ad1848_close,
|
|
NULL,
|
|
ad1848_set_in_sr,
|
|
ad1848_get_in_sr,
|
|
ad1848_set_out_sr,
|
|
ad1848_get_out_sr,
|
|
ad1848_query_encoding,
|
|
ad1848_set_encoding,
|
|
ad1848_get_encoding,
|
|
ad1848_set_precision,
|
|
ad1848_get_precision,
|
|
ad1848_set_channels,
|
|
ad1848_get_channels,
|
|
ad1848_round_blocksize,
|
|
pss_set_out_port,
|
|
pss_get_out_port,
|
|
pss_set_in_port,
|
|
pss_get_in_port,
|
|
ad1848_commit_settings,
|
|
ad1848_get_silence,
|
|
NULL,
|
|
NULL,
|
|
ad1848_dma_output,
|
|
ad1848_dma_input,
|
|
ad1848_halt_out_dma,
|
|
ad1848_halt_in_dma,
|
|
ad1848_cont_out_dma,
|
|
ad1848_cont_in_dma,
|
|
pss_speaker_ctl,
|
|
pss_getdev,
|
|
pss_setfd,
|
|
pss_mixer_set_port,
|
|
pss_mixer_get_port,
|
|
pss_query_devinfo,
|
|
0, /* not full-duplex */
|
|
0
|
|
};
|
|
|
|
|
|
/* Interrupt translation for WSS config */
|
|
static u_char wss_interrupt_bits[12] = {
|
|
0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0x08,
|
|
0xff, 0x10, 0x18, 0x20
|
|
};
|
|
/* ditto for WSS DMA channel */
|
|
static u_char wss_dma_bits[4] = {1, 2, 0, 3};
|
|
|
|
#ifndef NEWCONFIG
|
|
#define at_dma(flags, ptr, cc, chan) isa_dmastart(flags, ptr, cc, chan)
|
|
#endif
|
|
|
|
struct cfdriver psscd = {
|
|
NULL, "pss", pssprobe, pssattach, DV_DULL, sizeof(struct pss_softc)
|
|
};
|
|
|
|
struct cfdriver spcd = {
|
|
NULL, "sp", spprobe, spattach, DV_DULL, sizeof(struct ad1848_softc)
|
|
};
|
|
|
|
struct cfdriver mpucd = {
|
|
NULL, "mpu", mpuprobe, mpuattach, DV_DULL, sizeof(struct mpu_softc)
|
|
};
|
|
|
|
struct cfdriver pcdcd = {
|
|
NULL, "pcd", pcdprobe, pcdattach, DV_DULL, sizeof(struct cd_softc)
|
|
};
|
|
|
|
struct audio_device pss_device = {
|
|
"pss,ad1848",
|
|
"",
|
|
"PSS"
|
|
};
|
|
|
|
void
|
|
pss_dspwrite(struct pss_softc *sc, int data)
|
|
{
|
|
int i;
|
|
u_short 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);
|
|
}
|
|
|
|
void
|
|
pss_setaddr(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(int intNum, int configAddress)
|
|
{
|
|
int val;
|
|
switch(intNum) {
|
|
case 0:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 3:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
val |= INT_3_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 5:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
val |= INT_5_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 7:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
val |= INT_7_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 9:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
val |= INT_9_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 10:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
val |= INT_10_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 11:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
val |= INT_11_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 12:
|
|
val = inw(configAddress);
|
|
val &= INT_MASK;
|
|
val |= INT_12_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
default:
|
|
printf("pss_setint unkown int\n");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pss_setdma(int dmaNum, int configAddress)
|
|
{
|
|
int val;
|
|
|
|
switch(dmaNum) {
|
|
case 0:
|
|
val = inw(configAddress);
|
|
val &= DMA_MASK;
|
|
val |= DMA_0_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 1:
|
|
val = inw(configAddress);
|
|
val &= DMA_MASK;
|
|
val |= DMA_1_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 3:
|
|
val = inw(configAddress);
|
|
val &= DMA_MASK;
|
|
val |= DMA_3_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 5:
|
|
val = inw(configAddress);
|
|
val &= DMA_MASK;
|
|
val |= DMA_5_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 6:
|
|
val = inw(configAddress);
|
|
val &= DMA_MASK;
|
|
val |= DMA_6_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
case 7:
|
|
val = inw(configAddress);
|
|
val &= DMA_MASK;
|
|
val |= DMA_7_BITS;
|
|
outw(configAddress,val);
|
|
break;
|
|
default:
|
|
printf("PSS ERROR! pss_setdma: unknown_dma\n");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function tests an interrupt number to see if
|
|
* it is availible. It takes the interrupt button
|
|
* as it's argument and returns TRUE if the interrupt
|
|
* is ok.
|
|
*/
|
|
static int
|
|
pss_testirq(struct pss_softc *sc, int intNum)
|
|
{
|
|
u_short iobase = sc->sc_iobase;
|
|
int val;
|
|
int ret;
|
|
int i;
|
|
|
|
/* Set the interrupt bits */
|
|
switch(intNum) {
|
|
case 3:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_MASK; /* Special: 0 */
|
|
outw(iobase+PSS_CONFIG, val);
|
|
break;
|
|
case 5:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_MASK;
|
|
val |= INT_5_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 7:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_MASK;
|
|
val |= INT_7_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 9:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_MASK;
|
|
val |= INT_9_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 10:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_MASK;
|
|
val |= INT_10_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 11:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_MASK;
|
|
val |= INT_11_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 12:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_MASK;
|
|
val |= INT_12_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
default:
|
|
DPRINTF(("pss: unknown IRQ (%d)\n", intNum));
|
|
return 0;
|
|
}
|
|
|
|
/* Set the interrupt test bit */
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val |= INT_TEST_BIT;
|
|
outw(iobase+PSS_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(iobase+PSS_CONFIG);
|
|
if (val & INT_TEST_PASS) {
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Clear the Test bit and the interrupt bits */
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= INT_TEST_BIT_MASK;
|
|
val &= INT_MASK;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
return(ret);
|
|
}
|
|
|
|
/*
|
|
* This function tests a dma channel to see if
|
|
* it is availible. It takes the DMA channel button
|
|
* as it's argument and returns TRUE if the channel
|
|
* is ok.
|
|
*/
|
|
int
|
|
pss_testdma(struct pss_softc *sc, int dmaNum)
|
|
{
|
|
u_short iobase = sc->sc_iobase;
|
|
int val;
|
|
int i,ret;
|
|
|
|
switch (dmaNum) {
|
|
case 0:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= DMA_MASK;
|
|
val |= DMA_0_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 1:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= DMA_MASK;
|
|
val |= DMA_1_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 3:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= DMA_MASK;
|
|
val |= DMA_3_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 5:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= DMA_MASK;
|
|
val |= DMA_5_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 6:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= DMA_MASK;
|
|
val |= DMA_6_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
case 7:
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= DMA_MASK;
|
|
val |= DMA_7_BITS;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
break;
|
|
default:
|
|
DPRINTF(("pss: unknown DMA channel (%d)\n", dmaNum));
|
|
return 0;
|
|
}
|
|
|
|
/* Set the DMA test bit */
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val |= DMA_TEST_BIT;
|
|
outw(iobase+PSS_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(iobase+PSS_CONFIG);
|
|
if (val & DMA_TEST_PASS) {
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Clear the Test bit and the DMA bits */
|
|
val = inw(iobase+PSS_CONFIG);
|
|
val &= DMA_TEST_BIT_MASK;
|
|
val &= DMA_MASK;
|
|
outw(iobase+PSS_CONFIG,val);
|
|
return(ret);
|
|
}
|
|
|
|
int
|
|
pss_reset_dsp(struct pss_softc *sc)
|
|
{
|
|
u_long i;
|
|
u_short 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 putting
|
|
* reseting 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(struct pss_softc *sc, u_char *block, int size)
|
|
{
|
|
int i, val, count;
|
|
u_short 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;
|
|
}
|
|
|
|
void
|
|
wss_dump_regs(struct ad1848_softc *sc)
|
|
{
|
|
printf("WSS regs: config=%x version=%x\n",
|
|
(u_char)inb(sc->sc_iobase+WSS_CONFIG),
|
|
(u_char)inb(sc->sc_iobase+WSS_STATUS));
|
|
}
|
|
|
|
void
|
|
pss_dump_regs(struct pss_softc *sc)
|
|
{
|
|
printf("PSS regs: status=%x vers=%x ",
|
|
(u_short)inw(sc->sc_iobase+PSS_STATUS),
|
|
(u_short)inw(sc->sc_iobase+PSS_ID_VERS));
|
|
|
|
printf("config=%x wss_config=%x\n",
|
|
(u_short)inw(sc->sc_iobase+PSS_CONFIG),
|
|
(u_short)inw(sc->sc_iobase+PSS_WSS_CONFIG));
|
|
}
|
|
|
|
/*
|
|
* Probe for the PSS hardware.
|
|
*/
|
|
int
|
|
pssprobe(parent, self, aux)
|
|
struct device *parent, *self;
|
|
void *aux;
|
|
{
|
|
struct pss_softc *sc = (void *)self;
|
|
struct isa_attach_args *ia = aux;
|
|
u_short iobase = ia->ia_iobase;
|
|
int i;
|
|
|
|
if (!PSS_BASE_VALID(iobase)) {
|
|
printf("pss: configured iobase %d invalid\n", iobase);
|
|
return 0;
|
|
}
|
|
|
|
/* Need to probe for iobase when IOBASEUNK {0x220 0x240} */
|
|
if (iobase == (u_short)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);
|
|
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 = (1<<i);
|
|
printf("pss: found IRQ %d free\n", i);
|
|
}
|
|
}
|
|
else {
|
|
if (pss_testirq(sc, (ffs(ia->ia_irq) - 1)) == 0) {
|
|
printf("pss: configured IRQ unavailable (%d)\n", (ffs(ia->ia_irq) - 1));
|
|
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((ffs(ia->ia_irq) - 1), sc->sc_iobase+PSS_CONFIG);
|
|
pss_setdma(sc->sc_drq, sc->sc_iobase+PSS_CONFIG);
|
|
|
|
|
|
/* 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);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Probe for the Soundport (ad1848)
|
|
*/
|
|
int
|
|
spprobe(parent, match, aux)
|
|
struct device *parent;
|
|
void *match, *aux;
|
|
{
|
|
struct ad1848_softc *sc = (void *)match;
|
|
struct pss_softc *pc = (void *)parent;
|
|
struct cfdata *cf = (void *)sc->sc_dev.dv_cfdata;
|
|
u_char bits;
|
|
int i;
|
|
|
|
sc->sc_iobase = cf->cf_iobase;
|
|
|
|
/* Set WSS io address */
|
|
pss_setaddr(sc->sc_iobase, pc->sc_iobase+PSS_WSS_CONFIG);
|
|
|
|
/* Is there an ad1848 chip at the WSS iobase ? */
|
|
if (ad1848_probe(sc) == 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_drq = cf->cf_drq = i;
|
|
DPRINTF(("sp: found DMA %d free\n", i));
|
|
}
|
|
}
|
|
else {
|
|
if (pss_testdma(pc, sc->sc_drq) == 0) {
|
|
printf("sp: configured DMA channel unavailable (%d)\n", sc->sc_drq);
|
|
return 0;
|
|
}
|
|
sc->sc_drq = cf->cf_drq;
|
|
}
|
|
|
|
/* 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_drq]));
|
|
|
|
pc->ad1848_sc = sc;
|
|
sc->parent = (caddr_t)pc;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
mpuprobe(parent, match, aux)
|
|
struct device *parent;
|
|
void *match, *aux;
|
|
{
|
|
struct mpu_softc *sc = (void *)match;
|
|
struct pss_softc *pc = (void *)parent;
|
|
struct cfdata *cf = (void *)sc->sc_dev.dv_cfdata;
|
|
|
|
/* Check if midi is enabled; if it is check the interrupt */
|
|
sc->sc_iobase = cf->cf_iobase;
|
|
|
|
if (cf->cf_irq == IRQUNK) {
|
|
int i;
|
|
for (i = 0; i < 16; i++) {
|
|
if (pss_testirq(pc, i) != 0)
|
|
break;
|
|
}
|
|
if (i == 16) {
|
|
printf("mpu: unable to locate free IRQ channel for MIDI\n");
|
|
return 0;
|
|
}
|
|
else {
|
|
cf->cf_irq = i;
|
|
sc->sc_irq = i;
|
|
DPRINTF(("mpu: found IRQ %d free\n", i));
|
|
}
|
|
}
|
|
else {
|
|
sc->sc_irq = cf->cf_irq;
|
|
|
|
if (pss_testirq(pc, sc->sc_irq) == 0) {
|
|
printf("pss: configured MIDI IRQ unavailable (%d)\n", sc->sc_irq);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
outw(pc->sc_iobase+MIDI_CONFIG,0);
|
|
DPRINTF(("pss: mpu port 0x%x irq %d\n", sc->sc_iobase, sc->sc_irq));
|
|
pss_setaddr(sc->sc_iobase, pc->sc_iobase+MIDI_CONFIG);
|
|
pss_setint(sc->sc_irq, pc->sc_iobase+MIDI_CONFIG);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
pcdprobe(parent, match, aux)
|
|
struct device *parent;
|
|
void *match, *aux;
|
|
{
|
|
struct cd_softc *sc = (void *)match;
|
|
struct pss_softc *pc = (void *)parent;
|
|
struct cfdata *cf = (void *)sc->sc_dev.dv_cfdata;
|
|
u_short val;
|
|
|
|
sc->sc_iobase = cf->cf_iobase;
|
|
|
|
pss_setaddr(sc->sc_iobase, pc->sc_iobase+CD_CONFIG);
|
|
|
|
/* Set the correct irq polarity. */
|
|
val = inw(pc->sc_iobase+CD_CONFIG);
|
|
outw(pc->sc_iobase+CD_CONFIG, 0);
|
|
val &= CD_POL_MASK;
|
|
val | CD_POL_BIT; /* XXX if (pol) */
|
|
outw(pc->sc_iobase+CD_CONFIG, val);
|
|
|
|
if (cf->cf_irq == IRQUNK) {
|
|
int i;
|
|
for (i = 0; i < 16; i++) {
|
|
if (pss_testirq(pc, i) != 0)
|
|
break;
|
|
}
|
|
if (i == 16) {
|
|
printf("pcd: unable to locate free IRQ channel for CD\n");
|
|
return 0;
|
|
}
|
|
else {
|
|
cf->cf_irq = i;
|
|
sc->sc_irq = i;
|
|
DPRINTF(("pcd: found IRQ %d free\n", i));
|
|
}
|
|
}
|
|
else {
|
|
sc->sc_irq = cf->cf_irq;
|
|
|
|
if (pss_testirq(pc, sc->sc_irq) == 0) {
|
|
printf("pcd: configured CD IRQ unavailable (%d)\n", sc->sc_irq);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
pss_setint(sc->sc_irq, pc->sc_iobase+CD_CONFIG);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
u_short iobase = ia->ia_iobase;
|
|
u_char vers;
|
|
struct ad1848_volume vol = {150, 150};
|
|
|
|
sc->sc_iobase = iobase;
|
|
sc->sc_drq = ia->ia_drq;
|
|
|
|
#ifdef NEWCONFIG
|
|
isa_establish(&sc->sc_id, &sc->sc_dev);
|
|
#endif
|
|
|
|
/* Setup interrupt handler for PSS */
|
|
sc->sc_ih.ih_fun = pssintr;
|
|
sc->sc_ih.ih_arg = sc;
|
|
sc->sc_ih.ih_level = IPL_CLOCK;
|
|
intr_establish(ia->ia_irq, IST_EDGE, &sc->sc_ih);
|
|
|
|
vers = (inw(sc->sc_iobase+PSS_ID_VERS)&0xff) - 1;
|
|
printf(": esc614%c\n", (vers > 0)?'A'+vers:' ');
|
|
|
|
(void)config_found(self, NULL, NULL);
|
|
|
|
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);
|
|
|
|
if (audio_hardware_attach(&pss_audio_if, (caddr_t)sc->ad1848_sc) != 0)
|
|
printf("pss: could not attach to audio pseudo-device driver\n");
|
|
}
|
|
|
|
void
|
|
spattach(parent, self, aux)
|
|
struct device *parent, *self;
|
|
void *aux;
|
|
{
|
|
struct ad1848_softc *sc = (struct ad1848_softc *)self;
|
|
struct cfdata *cf = (void *)sc->sc_dev.dv_cfdata;
|
|
u_short iobase = cf->cf_iobase;
|
|
|
|
sc->sc_iobase = iobase;
|
|
sc->sc_drq = cf->cf_drq;
|
|
|
|
#ifdef NEWCONFIG
|
|
isa_establish(&sc->sc_id, &sc->sc_dev);
|
|
#endif
|
|
|
|
sc->sc_ih.ih_fun = ad1848_intr;
|
|
sc->sc_ih.ih_arg = sc;
|
|
sc->sc_ih.ih_level = IPL_CLOCK;
|
|
intr_establish(cf->cf_irq, IST_EDGE, &sc->sc_ih);
|
|
|
|
/* XXX might use pssprint func ?? */
|
|
printf(" port 0x%x-0x%x irq %d drq %d",
|
|
sc->sc_iobase, sc->sc_iobase+AD1848_NPORT,
|
|
cf->cf_irq, cf->cf_drq);
|
|
|
|
ad1848_attach(sc);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
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;
|
|
u_short iobase = cf->cf_iobase;
|
|
|
|
sc->sc_iobase = iobase;
|
|
|
|
#ifdef NEWCONFIG
|
|
isa_establish(&sc->sc_id, &sc->sc_dev);
|
|
#endif
|
|
|
|
sc->sc_ih.ih_fun = mpuintr;
|
|
sc->sc_ih.ih_arg = sc;
|
|
sc->sc_ih.ih_level = IPL_CLOCK;
|
|
intr_establish(cf->cf_irq, IST_EDGE, &sc->sc_ih);
|
|
|
|
/* 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 cd_softc *sc = (struct cd_softc *)self;
|
|
struct cfdata *cf = (void *)sc->sc_dev.dv_cfdata;
|
|
u_short 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;
|
|
|
|
#ifdef NEWCONFIG
|
|
isa_establish(&sc->sc_id, &sc->sc_dev);
|
|
#endif
|
|
|
|
/* XXX might use pssprint func ?? */
|
|
printf(" port 0x%x-0x%x irq %d\n",
|
|
sc->sc_iobase, sc->sc_iobase+2,
|
|
cf->cf_irq);
|
|
}
|
|
|
|
static int
|
|
pss_to_vol(cp, vol)
|
|
mixer_ctrl_t *cp;
|
|
struct ad1848_volume *vol;
|
|
{
|
|
if (cp->un.value.num_channels == 1) {
|
|
vol->left = vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
|
|
return(1);
|
|
}
|
|
else if (cp->un.value.num_channels == 2) {
|
|
vol->left = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
|
|
vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
pss_from_vol(cp, vol)
|
|
mixer_ctrl_t *cp;
|
|
struct ad1848_volume *vol;
|
|
{
|
|
if (cp->un.value.num_channels == 1) {
|
|
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol->left;
|
|
return(1);
|
|
}
|
|
else if (cp->un.value.num_channels == 2) {
|
|
cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol->left;
|
|
cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol->right;
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
spopen(dev, flags)
|
|
dev_t dev;
|
|
int flags;
|
|
{
|
|
struct ad1848_softc *sc;
|
|
int unit = AUDIOUNIT(dev);
|
|
|
|
if (unit >= spcd.cd_ndevs)
|
|
return ENODEV;
|
|
|
|
sc = spcd.cd_devs[unit];
|
|
if (!sc)
|
|
return ENXIO;
|
|
|
|
return ad1848_open(sc, dev, flags);
|
|
}
|
|
|
|
int
|
|
pss_set_master_gain(sc, gp)
|
|
register 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)
|
|
register 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)
|
|
register struct pss_softc *sc;
|
|
u_char 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)
|
|
register 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)
|
|
register struct pss_softc *sc;
|
|
struct ad1848_volume *gp;
|
|
{
|
|
*gp = sc->master_volume;
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
pss_get_master_mode(sc, mode)
|
|
register struct pss_softc *sc;
|
|
u_int *mode;
|
|
{
|
|
*mode = sc->master_mode;
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
pss_get_treble(sc, tp)
|
|
register struct pss_softc *sc;
|
|
u_char *tp;
|
|
{
|
|
*tp = sc->monitor_treble;
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
pss_get_bass(sc, bp)
|
|
register struct pss_softc *sc;
|
|
u_char *bp;
|
|
{
|
|
*bp = sc->monitor_bass;
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
pss_speaker_ctl(addr, newstate)
|
|
caddr_t addr;
|
|
int newstate;
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
pssintr(sc)
|
|
register struct pss_softc *sc;
|
|
{
|
|
u_short sr;
|
|
|
|
sr = inw(sc->sc_iobase+PSS_STATUS);
|
|
|
|
DPRINTF(("pssintr: sc=%x 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;
|
|
}
|
|
|
|
int
|
|
mpuintr(sc)
|
|
register struct mpu_softc *sc;
|
|
{
|
|
u_char sr;
|
|
|
|
sr = inb(sc->sc_iobase+MIDI_STATUS_REG);
|
|
|
|
printf("mpuintr: sc=%x sr=%x\n", sc, sr);
|
|
|
|
/* XXX Need to clear intr */
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
pss_getdev(addr, retp)
|
|
caddr_t addr;
|
|
struct audio_device *retp;
|
|
{
|
|
DPRINTF(("pss_getdev: retp=0x%x\n", retp));
|
|
|
|
*retp = pss_device;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pss_setfd(addr, flag)
|
|
caddr_t addr;
|
|
int flag;
|
|
{
|
|
/* Can't do full-duplex */
|
|
return(ENOTTY);
|
|
}
|
|
|
|
int
|
|
pss_set_out_port(addr, port)
|
|
caddr_t addr;
|
|
int port;
|
|
{
|
|
register struct ad1848_softc *ac = (struct ad1848_softc *)addr;
|
|
register struct pss_softc *sc = (struct pss_softc *)ac->parent;
|
|
|
|
DPRINTF(("pss_set_out_port: %d\n", port));
|
|
|
|
if (port != PSS_MASTER_VOL)
|
|
return(EINVAL);
|
|
|
|
sc->out_port = port;
|
|
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
pss_get_out_port(addr)
|
|
caddr_t addr;
|
|
{
|
|
register struct ad1848_softc *ac = (struct ad1848_softc *)addr;
|
|
register struct pss_softc *sc = (struct pss_softc *)ac->parent;
|
|
|
|
DPRINTF(("pss_get_out_port: %d\n", sc->out_port));
|
|
|
|
return(sc->out_port);
|
|
}
|
|
|
|
int
|
|
pss_set_in_port(addr, port)
|
|
caddr_t addr;
|
|
int port;
|
|
{
|
|
register struct ad1848_softc *ac = (struct ad1848_softc *)addr;
|
|
register struct pss_softc *sc = (struct pss_softc *)ac->parent;
|
|
|
|
DPRINTF(("pss_set_in_port: %d\n", port));
|
|
|
|
switch(port) {
|
|
case PSS_MIC_IN_LVL:
|
|
port = MIC_IN_PORT;
|
|
break;
|
|
case PSS_LINE_IN_LVL:
|
|
port = LINE_IN_PORT;
|
|
break;
|
|
case PSS_DAC_LVL:
|
|
port = DAC_IN_PORT;
|
|
break;
|
|
default:
|
|
return(EINVAL);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
return(ad1848_set_rec_port(ac, port));
|
|
}
|
|
|
|
int
|
|
pss_get_in_port(addr)
|
|
caddr_t addr;
|
|
{
|
|
register struct ad1848_softc *ac = (struct ad1848_softc *)addr;
|
|
register struct pss_softc *sc = (struct pss_softc *)ac->parent;
|
|
int port = PSS_MIC_IN_LVL;
|
|
|
|
switch(ad1848_get_rec_port(ac)) {
|
|
case MIC_IN_PORT:
|
|
port = PSS_MIC_IN_LVL;
|
|
break;
|
|
case LINE_IN_PORT:
|
|
port = PSS_LINE_IN_LVL;
|
|
break;
|
|
case DAC_IN_PORT:
|
|
port = PSS_DAC_LVL;
|
|
break;
|
|
}
|
|
|
|
DPRINTF(("pss_get_in_port: %d\n", port));
|
|
|
|
return(port);
|
|
}
|
|
|
|
int
|
|
pss_mixer_set_port(addr, cp)
|
|
caddr_t addr;
|
|
mixer_ctrl_t *cp;
|
|
{
|
|
register struct ad1848_softc *ac = (struct ad1848_softc *)addr;
|
|
register struct pss_softc *sc = (struct pss_softc *)ac->parent;
|
|
struct ad1848_volume vol;
|
|
u_char eq;
|
|
int error = EINVAL;
|
|
|
|
DPRINTF(("pss_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
|
|
|
|
switch (cp->dev) {
|
|
case PSS_MIC_IN_LVL: /* Microphone */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
if (pss_to_vol(cp, &vol))
|
|
error = ad1848_set_aux2_gain(ac, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_MIC_IN_MUTE: /* Microphone */
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
sc->mic_mute = cp->un.ord;
|
|
DPRINTF(("mic mute %d\n", cp->un.ord));
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case PSS_LINE_IN_LVL: /* linein/CD */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
if (pss_to_vol(cp, &vol))
|
|
error = ad1848_set_aux1_gain(ac, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_LINE_IN_MUTE: /* linein/CD */
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
sc->cd_mute = cp->un.ord;
|
|
DPRINTF(("CD mute %d\n", cp->un.ord));
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case PSS_DAC_LVL: /* dac out */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
if (pss_to_vol(cp, &vol))
|
|
error = ad1848_set_out_gain(ac, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_DAC_MUTE: /* dac out */
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
sc->dac_mute = cp->un.ord;
|
|
DPRINTF(("DAC mute %d\n", cp->un.ord));
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case PSS_REC_LVL: /* record level */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
if (pss_to_vol(cp, &vol))
|
|
error = ad1848_set_rec_gain(ac, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_RECORD_SOURCE:
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
error = ad1848_set_rec_port(ac, cp->un.ord);
|
|
}
|
|
break;
|
|
|
|
case PSS_MON_LVL:
|
|
if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
|
|
vol.left = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
|
|
error = ad1848_set_mon_gain(ac, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_MASTER_VOL: /* master volume */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
if (pss_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)
|
|
caddr_t addr;
|
|
mixer_ctrl_t *cp;
|
|
{
|
|
register struct ad1848_softc *ac = (struct ad1848_softc *)addr;
|
|
register struct pss_softc *sc = (struct pss_softc *)ac->parent;
|
|
struct ad1848_volume vol;
|
|
u_char eq;
|
|
int error = EINVAL;
|
|
|
|
DPRINTF(("pss_mixer_get_port: port=%d\n", cp->dev));
|
|
|
|
switch (cp->dev) {
|
|
case PSS_MIC_IN_LVL: /* Microphone */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
error = ad1848_get_aux2_gain(ac, &vol);
|
|
if (!error)
|
|
pss_from_vol(cp, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_MIC_IN_MUTE:
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
cp->un.ord = sc->mic_mute;
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case PSS_LINE_IN_LVL: /* linein/CD */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
error = ad1848_get_aux1_gain(ac, &vol);
|
|
if (!error)
|
|
pss_from_vol(cp, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_LINE_IN_MUTE:
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
cp->un.ord = sc->cd_mute;
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case PSS_DAC_LVL: /* dac out */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
error = ad1848_get_out_gain(ac, &vol);
|
|
if (!error)
|
|
pss_from_vol(cp, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_DAC_MUTE:
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
cp->un.ord = sc->dac_mute;
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case PSS_REC_LVL: /* record level */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
error = ad1848_get_rec_gain(ac, &vol);
|
|
if (!error)
|
|
pss_from_vol(cp, &vol);
|
|
}
|
|
break;
|
|
|
|
case PSS_RECORD_SOURCE:
|
|
if (cp->type == AUDIO_MIXER_ENUM) {
|
|
cp->un.ord = ad1848_get_rec_port(ac);
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case PSS_MON_LVL: /* monitor level */
|
|
if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
|
|
error = ad1848_get_mon_gain(ac, &vol);
|
|
if (!error)
|
|
cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol.left;
|
|
}
|
|
break;
|
|
|
|
case PSS_MASTER_VOL: /* master volume */
|
|
if (cp->type == AUDIO_MIXER_VALUE) {
|
|
error = pss_get_master_gain(sc, &vol);
|
|
if (!error)
|
|
pss_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)
|
|
caddr_t addr;
|
|
register mixer_devinfo_t *dip;
|
|
{
|
|
register struct ad1848_softc *ac = (struct ad1848_softc *)addr;
|
|
register struct pss_softc *sc = (struct pss_softc *)ac->parent;
|
|
|
|
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_MON_LVL: /* monitor level */
|
|
dip->type = AUDIO_MIXER_VALUE;
|
|
dip->mixer_class = PSS_MONITOR_CLASS;
|
|
dip->next = dip->prev = AUDIO_MIXER_LAST;
|
|
strcpy(dip->label.name, AudioNmonitor);
|
|
dip->un.v.num_channels = 1;
|
|
strcpy(dip->un.v.units.name, AudioNvolume);
|
|
break;
|
|
|
|
case 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, AudioNvolume);
|
|
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, AudioNspeaker);
|
|
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, AudioNmonitor);
|
|
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, AudioNrecord);
|
|
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;
|
|
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;
|
|
}
|