audio1575.c revision 193974072f41a843678abf5f61979c748687e66b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* audio1575 Audio Driver
*
* The driver is primarily targeted at providing audio support for
* those sparc systems which use the Uli M1575 audio core
* and the Analog Devices AD1981 codec.
*
* This driver uses the mixer Audio Personality Module to implement
* audio(7I) and mixer(7I) semantics. Both play and record are single
* streaming.
*
* The M1575 audio core, in AC'97 controller mode, has independent
* channels for PCM in, PCM out, mic in, modem in, and modem out.
*
* support. Each channel has a DMA engine. Currently, we use only
* the PCM in and PCM out channels. Each DMA engine uses one buffer
* descriptor list. And the buffer descriptor list is an array of up
* to 32 entries, each of which describes a data buffer. Each entry
* contains a pointer to a data buffer, control bits, and the length
* of the buffer being pointed to, where the length is expressed as
* the number of samples. This, combined with the 16-bit sample size,
* gives the actual physical length of the buffer.
*
* We use the BD list (buffer descriptor list) as a round-robin FIFO.
* Both the software and hardware loop around the BD list. For playback,
* the software writes to the buffers pointed by the BD entries of BD
* list, and the hardware sends the data in the buffers out. For record,
* the process is reversed. So we define the struct, m1575_sample_buf,
* to handle the BD. The software uses the head, tail and avail fields of
* this structure to manipulate the FIFO. The head field indicates the
* first valid BD hardware can manipulate. The tail field indicates the
* BD after the last valid BD. And the avail field indicates how many
* buffers are available. There're also two hardware registers to index
* the FIFO, the CIV (current index value) indicating the current BD the
* hardware is transferring, and the LVI (last valid index) indicating
* the last valid BD that contains proper data which the hardware should
* not pass over. Each time a BD is processed, the hardware will issue an
* interrupt. If the system is busy, there can be more than one BD to be
* processed when the OS have chance to handle the interrupt. When an
* interrupt generated, the interrupt handler will first reclaim the BD(s)
* which had been transferred, which will be the limit [head, CIV-1], then
* update the value of the head field to CIV, update the value of avail to
* CIV - head. And then it will process avail BDs from tail, and set the
* LVI to the last processed BD and update tail to LVI + 1, and update the
* avail to 0.
*
* We allocate N blocks of DMA memory, say A and B, for every DMA
* engine, and bind the first, Block A, to the even entries of BDL,
* while bind the second to the odd entries. That's say, for each buffer
* descriptor list of DMA engine, entry 0, 2,,, 30 would be bound to Block
* A, and entry 1, 3,,, 31 would be bound to Block B. Take the playback as
* an example. At the beginning of playback, we set the entry 0 and 1 to
* point to block A and B separately, and tell the DMA engine that the last
* valid entry is entry 1. So the DMA engine doesn't access the entries after
* entry 1. When the first playback interrupt generated, we reclaim entry
* 0, and fill the BD entry 2 with the address of Block A, then set the
* entry 2 to be the last valid entry, and so on. So at any time there are
* at most two entries available for per DMA engine.
*
* Every time we program AC97 codec, we save the value in codec_shadow[].
* This means that register state information is saved for power management
* shutdown (we'll support this later). When the codec is started back up
* we use this saved state to restore codec's state in audio1575_chip_init()
*
* TODO: System power management is not yet supported by the driver.
*
* NOTE:
*/
#include <sys/audiovar.h>
/*
* Module linkage routines for the kernel
*/
/*
* Entry point routine prototypes
*/
static int audio1575_ad_set_config(audiohdl_t, int, int, int, int, int);
static int audio1575_ad_set_format(audiohdl_t, int, int, int, int, int, int);
static int audio1575_ad_start_play(audiohdl_t, int);
static void audio1575_ad_pause_play(audiohdl_t, int);
static void audio1575_ad_stop_play(audiohdl_t, int);
static int audio1575_ad_start_record(audiohdl_t, int);
static void audio1575_ad_stop_record(audiohdl_t, int);
/*
* interrupt handler
*/
/*
* Local Routine Prototypes
*/
static int audio1575_init_ac97(audio1575_state_t *, int);
static int audio1575_reset_ac97(audio1575_state_t *);
static int audio1575_codec_sync(audio1575_state_t *);
static int audio1575_chip_init(audio1575_state_t *, int);
static void audio1575_unmap_regs(audio1575_state_t *);
static int audio1575_alloc_sample_buf(audio1575_state_t *, int, int);
static void audio1575_free_sample_buf(audio1575_state_t *, int);
static void audio1575_dma_stop(audio1575_state_t *);
static int audio1575_fill_play_buf(audio1575_state_t *);
static void audio1575_reclaim_play_buf(audio1575_state_t *);
static int audio1575_prepare_record_buf(audio1575_state_t *);
static void audio1575_reclaim_record_buf(audio1575_state_t *);
static int audio1575_set_gain(audio1575_state_t *, int, int, int);
static int audio1575_set_port(audio1575_state_t *, int, int);
static int audio1575_set_monitor_gain(audio1575_state_t *, int);
static void audio1575_pci_enable(audio1575_state_t *);
static void audio1575_pci_disable(audio1575_state_t *);
static int audio1575_dma_pause(audio1575_state_t *, int);
static int audio1575_dma_resume(audio1575_state_t *, int);
static int audio1575_dma_reset(audio1575_state_t *, int);
/*
* Global variables, but used only by this file.
*/
/* anchor for soft state structures */
static void *audio1575_statep;
/* driver name, so we don't have to call ddi_driver_name() or hard code strs */
static char *audio1575_name = M1575_NAME;
/*
* STREAMS structures
*/
/* STREAMS driver id and limit value struct */
static struct module_info audio1575_modinfo = {
M1575_IDNUM, /* module ID number */
M1575_NAME, /* module name */
M1575_MINPACKET, /* minimum packet size */
M1575_MAXPACKET, /* maximum packet size */
M1575_HIWATER, /* high water mark */
M1575_LOWATER, /* low water mark */
};
/* STREAMS queue processing procedures structures */
/* read queue */
static struct qinit audio1575_rqueue = {
audio_sup_rput, /* put procedure */
audio_sup_rsvc, /* service procedure */
audio_sup_open, /* open procedure */
audio_sup_close, /* close procedure */
NULL, /* unused */
&audio1575_modinfo, /* module parameters */
NULL /* module statistics */
};
/* write queue */
static struct qinit audio1575_wqueue = {
audio_sup_wput, /* write procedure */
audio_sup_wsvc, /* service procedure */
NULL, /* open procedure */
NULL, /* close procedure */
NULL, /* unused */
&audio1575_modinfo, /* module parameters */
NULL /* module statistics */
};
/* STREAMS entity declaration structure */
static struct streamtab audio1575_str_info = {
&audio1575_rqueue, /* read queue */
&audio1575_wqueue, /* write queue */
NULL, /* mux lower read queue */
NULL, /* mux lower write queue */
};
/*
* DDI Structures
*/
/* Entry points structure */
static struct cb_ops audio1575_cb_ops = {
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&audio1575_str_info, /* cb_str */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev, /* cb_awrite */
};
/* Device operations structure */
static struct dev_ops audio1575_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
audio1575_getinfo, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
audio1575_attach, /* devo_attach */
audio1575_detach, /* devo_detach */
nodev, /* devo_reset */
&audio1575_cb_ops, /* devi_cb_ops */
NULL, /* devo_bus_ops */
NULL, /* devo_power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/* Linkage structure for loadable drivers */
static struct modldrv audio1575_modldrv = {
&mod_driverops, /* drv_modops */
M1575_MOD_NAME, /* drv_linkinfo */
&audio1575_dev_ops, /* drv_dev_ops */
};
/* Module linkage structure */
static struct modlinkage audio1575_modlinkage = {
MODREV_1, /* ml_rev */
(void *)&audio1575_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
static uint_t audio1575_mixer_srs[] = {
0
};
static uint_t audio1575_compat_srs [] = {
0
};
static am_ad_sample_rates_t audio1575_mixer_sample_rates = {
};
};
static uint_t audio1575_channels[] = {
0
};
static am_ad_cap_comb_t audio1575_combinations[] = {
{ 0 }
};
/* AD1981B Equalization Biquad IIR Filter coefficient address table */
static m1575_biquad_t filters[] = {
{0x1b, 0x0f}, {0x1a, 0x00}, {0x19, 0x00}, {0x1d, 0x00}, {0x1c, 0x00},
{0x20, 0x0f}, {0x1f, 0x00}, {0x1e, 0x00}, {0x22, 0x00}, {0x21, 0x00},
{0x25, 0x0f}, {0x24, 0x00}, {0x23, 0x00}, {0x27, 0x00}, {0x26, 0x00},
{0x2a, 0x0f}, {0x29, 0x00}, {0x28, 0x00}, {0x2c, 0x00}, {0x2b, 0x00},
{0x2f, 0x0f}, {0x2e, 0x00}, {0x2d, 0x00}, {0x31, 0x00}, {0x30, 0x00},
{0x34, 0x0f}, {0x33, 0x00}, {0x32, 0x00}, {0x36, 0x00}, {0x35, 0x00},
{0x39, 0x0f}, {0x38, 0x00}, {0x37, 0x00}, {0x3b, 0x00}, {0x3a, 0x00},
};
/*
* device access attributes for register mapping
*/
static struct ddi_device_acc_attr dev_attr = {
};
/*
* DMA attributes of buffer descriptor list
*/
static ddi_dma_attr_t bdlist_dma_attr = {
DMA_ATTR_V0, /* version */
0x0000000000000000LL, /* dlim_addr_lo */
0x00000000ffffffffLL, /* dlim_addr_hi */
0x000000000000ffffLL, /* DMA counter register - 64 bits */
0x0000000000000008LL, /* DMA address align must be 8-bytes */
0x0000003c, /* 1 through 64 byte burst sizes */
0x00000008, /* min xfer DMA size BDList entry */
0x00000000000ffffLL, /* max xfer size, 64K */
0x000000000001fffLL, /* seg, set to PAGESIZE */
0x00000001, /* s/g list length, no s/g */
0x00000008, /* granularity of device minxfer */
0 /* DMA flags use virtual address */
};
/*
*/
static ddi_dma_attr_t sample_buf_dma_attr = {
0x0000000000000000LL, /* dlim_addr_lo */
0x00000000ffffffffLL, /* dlim_addr_hi */
0x000000000001fffeLL, /* DMA counter register - 16 bits */
0x0000000000000002LL, /* DMA address align 2-byte boundary */
0x0000003c, /* 1 through 60 byte burst sizes */
0x00000004, /* min xfer DMA size BDList entry */
0x000000000001ffffLL, /* max xfer size, 64K */
0x000000000001ffffLL, /* seg, set to 64K */
0x00000001, /* s/g list length, no s/g */
0x00000004, /* granularity of device minxfer */
0 /* DMA flags use virtual address */
};
static am_ad_entry_t audio1575_entry = {
NULL, /* ad_setup() */
NULL, /* ad_teardown() */
audio1575_ad_set_config, /* ad_set_config() */
audio1575_ad_set_format, /* ad_set_format() */
audio1575_ad_start_play, /* ad_start_play() */
audio1575_ad_pause_play, /* ad_pause_play() */
audio1575_ad_stop_play, /* ad_stop_play() */
audio1575_ad_start_record, /* ad_start_record() */
audio1575_ad_stop_record, /* ad_stop_record() */
NULL, /* ad_ioctl() */
NULL /* ad_iocdata() */
};
/*
* _init()
*
* Description:
* Driver initialization, called when driver is first loaded.
* This is how access is initially given to all the static structures.
*
* Arguments:
* None
*
* Returns:
* ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
* mod_install() status, see mod_install(9f)
*/
int
_init(void)
{
int error;
sizeof (audio1575_state_t), 1)) != 0) {
ATRACE("audio1575 ddi_soft_state_init() failure",
error);
return (error);
}
}
return (error);
} /* _init() */
/*
* _fini()
*
* Description:
* Module de-initialization, called when the driver is to be unloaded.
*
* Arguments:
* None
*
* Returns:
* mod_remove() status, see mod_remove(9f)
*/
int
_fini(void)
{
int error;
return (error);
}
return (0);
} /* _fini() */
/*
* _info()
*
* Description:
* Module information, returns information about the driver.
*
* Arguments:
* modinfo *modinfop Pointer to the opaque modinfo structure
*
* Returns:
* mod_info() status, see mod_info(9f)
*/
int
{
int error;
return (error);
} /* _info() */
/* ******************* Driver Entry Points ********************************* */
/*
* audio1575_getinfo()
*/
static int
{
int instance;
int error = DDI_FAILURE;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
} else {
}
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
} /* audio1575_getinfo() */
/*
* audio1575_attach()
*
* Description:
* Attach an instance of the audio1575 driver. This routine does the
* device dependent attach tasks. When it is completed, it calls
* audio_sup_register() and am_attach() so they may do their work.
*
* NOTE: mutex_init() no longer needs a name string, so set
* to NULL to save kernel space.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_attach_cmd_t cmd Attach command
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
/* we've already allocated the state structure so get ptr */
NULL) {
"!%s%d: attach() RESUME get soft state failure",
return (DDI_FAILURE);
}
/*
* Restore the audio1575 chip's state and
* put the DMA Engines in a known state
*/
ATRACE("audio1575_attach()chip init failure",
return (DDI_FAILURE);
}
/*
* Start playing and recording, if not needed they'll stop
* on their own. But, we don't start them if the hardware has
* failed.
*/
"!attach() audio restart failure");
}
return (DDI_SUCCESS);
default:
"!%s%d: attach() unknown command: 0x%x",
return (DDI_FAILURE);
}
/* allocate the soft state structure */
"!%s%d: attach() soft state allocate failure",
goto error_state;
}
/* get the state structure */
"!%s%d: attach() ddi_get_soft_state() failure",
goto error_state;
}
/* call audiosup module registration routine */
/* register the driver with the audio support module */
"!%s%d: attach() audio_sup_register() failure",
goto error_state;
}
/* map in the audio registers */
"!attach() couldn't map registers");
goto error_audiosup;
}
/* Enable PCI I/O and Memory Spaces */
/*
* clear the interrupt control and status register
* for buggy hardware
*/
intrcr = 0L;
/* initialize the audio state structures */
"!attach() init state structure failure");
goto error_pci_disable;
}
/* from here on, must destroy the mutex on error */
/* allocate play and record sample buffers */
"!attach() couldn't alloc sample buffers");
goto error_destroy;
}
/* initialize audio controller and AC97 codec */
"!attach() failure to init chip");
goto error_dealloc;
}
/* save private state */
/* call the mixer attach() routine */
"!attach() am_attach() failure");
goto error_dealloc;
}
/* set up kernel statistics */
KSTAT_FLAG_PERSISTENT)) != NULL) {
}
/* now add our interrupt handler */
CE_WARN, "!attach() ddi_intr_add_handler() failure");
ATRACE("audio1575_attach() ddi_intr_add_handler() failure",
NULL);
goto error_kstat;
}
/* Enable PCI Interrupts */
/* enable audio interrupts */
"!attach() - ddi_intr_enable() failure");
goto error_intr_enable;
}
/* everything worked out, so report the device */
return (DDI_SUCCESS);
/* Disable PCI Interrupts */
}
return (DDI_FAILURE);
} /* audio1575_attach() */
/*
* audio1575_detach()
*
* Description:
* Detach an instance of the audio1575 driver. After the Codec is detached
* we call am_detach() and audio_sup_register() so they may do their work.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_detach_cmd_t cmd Detach command
*
* Returns:
* DDI_SUCCESS The driver was detached
* DDI_FAILURE The driver couldn't be detached
*/
static int
{
int instance;
"!%s%d: detach() get soft state failure",
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
/* stop all new operations */
/*
* stop all DMA operations
*/
(void) audio1575_dma_stop(statep);
/*
* Save the controller state. The Codec's state is
* already in codec_shadow[].
*/
"!detach() audio save failure");
}
return (DDI_SUCCESS);
default:
"!%s%d: detach() unknown command: 0x%x",
return (DDI_FAILURE);
}
/* stop DMA engines */
/* disable interrupts */
/* Remove the interrupt handler */
/* reset the AD1981B codec */
/* turn off the AC_LINK clock */
/* detach audio mixer */
/*
* call the audio support module's detach routine to remove this
* driver completely from the audio driver architecture.
*/
/* free the interrupt handle */
/* free memory */
/* free DMA memory */
/* free the kernel statistics structure */
}
/* Disable PCI I/O and Memory Spaces */
/* unmap the registers */
/* destroy the state mutex */
/* free the memory for the state pointer */
return (DDI_SUCCESS);
} /* audio1575_detach */
/*
* audio1575_intr()
*
* Description:
* Interrupt service routine for both play and record. For play we
* get the next buffers worth of audio. For record we send it on to
* the mixer.
*
* Each of buffer descriptor has a field IOC(interrupt on completion)
* When both this and the IOC bit of correspondent dma control register
* is set, it means that the controller should issue an interrupt upon
* completion of this buffer. Note that in the clearing of the interrupts
* below that the PCM IN and PCM out interrupts ar cleared by their
* respective control registers and not by writing a '1' to the INTRSR
* the interrupt status register. Only CPRINTR,SPINTR,and GPIOINTR
* require a '1' written to the INTRSR register to clear those
* interrupts. See comments below.
*
* Arguments:
* caddr_t arg Pointer to the interrupting device's state
* structure
*
* Returns:
* DDI_INTR_CLAIMED Interrupt claimed and processed
* DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored
*/
static uint_t
{
/* check if device is interrupting */
if (intrsr == 0) {
/* increment the spurious ino5 interrupt cnt */
}
return (DDI_INTR_UNCLAIMED);
}
/*
* The Uli M1575 generates an interrupt for each interrupt
* type. therefore we only process one interrupt type
* per invocation of the audio1575_intr() routine.
* WARNING: DO NOT attempt to optimize this by looping
* until the INTRSR register is clear as this will
* generate spurious ino5 interrupts.
*/
if (intrsr & M1575_INTRSR_PCMIINTR) {
/* Clear PCM IN interrupt */
/*
* Note: This interrupt is not cleared by writing a '1'
* to the M1575_INTRSR_REG according to the M1575 Super I/O
* data sheet on page 189.
*/
(void) audio1575_prepare_record_buf(statep);
}
} else if (intrsr & M1575_INTRSR_PCMOINTR) {
/* Clear PCM OUT interrupt */
/*
* Note: This interrupt is not cleared by writing a '1'
* to the M1575_INTRSR_REG according to the M1575 Super I/O
* data sheet on page 189.
*/
(void) audio1575_fill_play_buf(statep);
}
} else if (intrsr & M1575_INTRSR_SPRINTR) {
/* Clear Status Register Available Interrupt */
} else if (intrsr & M1575_INTRSR_CPRINTR) {
/* Clear Command Register Available Interrupt */
} else if (intrsr & M1575_INTRSR_GPIOINTR) {
/* Clear General Purpose I/O Register Interrupt */
} else {
/* Clear Unknown Interrupt */
}
/* update the kernel interrupt statistics */
}
return (DDI_INTR_CLAIMED);
} /* audio1575_intr() */
/* *********************** Mixer Entry Point Routines ******************* */
/*
* audio1575_ad_set_config()
*
* Description:
* This routine is used to set new Codec parameters, except the data
* format which has it's own routine. If the Codec doesn't support a
* particular parameter and it is asked to set it then we return
* AUDIO_FAILURE.
*
* Arguments:
* audiohdl_t ahandle Handle to this device
* int stream Stream number for multi-stream Codecs,
* which is not how we program the device
* for now.
* int command The configuration to set
* int dir AUDIO_PLAY or AUDIO_RECORD, if
* direction is important
* int arg1 Argument #1
* int arg2 Argument #2, not always needed
*
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The Codec parameter has not been set,
* or the parameter couldn't be set
*/
static int
{
int rc = AUDIO_FAILURE;
/* get the soft state structure */
/*
* CAUTION: From here on we must goto done to exit.
*/
switch (command) {
case AM_SET_GAIN:
/*
* Set the gain for a channel. The audio mixer calculates the
* impact, if any, of balance on gain.
*
* AUDIO_MIN_GAIN <= gain <= AUDIO_MAX_GAIN
*
* arg1 --> gain
* arg2 --> channel #, 0 == left, 1 == right
*/
break;
case AM_SET_PORT:
/*
* enforces exclusiveness of in ports, as well as which ports
* are modifiable. We just turn on the ports that match the
* bits.
*
* arg1 --> port bit pattern
* arg2 --> not used
*/
break;
case AM_SET_MONITOR_GAIN:
/*
* Set the loopback monitor gain.
*
* AUDIO_MIN_GAIN <= gain <= AUDIO_MAX_GAIN
*
* dir ---> N/A
* arg1 --> gain
* arg2 --> not used
*/
break;
case AM_OUTPUT_MUTE:
/*
* Mute or enable the output.
*
* dir ---> N/A
* arg1 --> ~0 == mute, 0 == enable
* arg2 --> not used
*/
if (arg1) { /* mute */
(void) audio1575_or_ac97(statep,
(void) audio1575_or_ac97(statep,
(void) audio1575_or_ac97(statep,
rc = AUDIO_SUCCESS;
} else { /* not muted */
/* by setting the port we unmute only active ports */
}
break;
case AM_MIC_BOOST:
/*
* Enable or disable the mic's 20 dB boost preamplifier.
*
* dir ---> N/A
* arg1 --> ~0 == enable, 0 == disabled
* arg2 --> not used
*/
if (arg1) { /* enable */
(void) audio1575_or_ac97(statep,
} else { /* disable */
(void) audio1575_and_ac97(statep,
}
rc = AUDIO_SUCCESS;
break;
default:
/*
* We let default catch commands we don't support, as well
* as bad commands.
*
* AM_SET_GAIN_BAL
* AM_SET_MONO_MIC
* AM_BASS_BOOST
* AM_MID_BOOST
* AM_TREBLE_BOOST
* AM_LOUDNESS
*/
break;
}
done:
return (rc);
} /* audio1575_ad_set_config() */
/*
* audio1575_ad_set_format()
*
* Description:
* This routine is used to set a new audio control data format.
* We only support 16 bit signed linear.
*
* Arguments:
* audiohdl_t ahandle Handle to this device
* int stream Stream number
* int dir AUDIO_PLAY or AUDIO_RECORD
* int sample_rate Data sample rate
* int channels Number of channels, 1 or 2
* int precision Bits per sample, 16
* int encoding Encoding method, linear
*
* Returns:
* AUDIO_SUCCESS The Codec data format has been set
* AUDIO_FAILURE The Codec data format has not been set, or the
* data format couldn't be set
*/
static int
{
int rc = AUDIO_FAILURE;
if (encoding != AUDIO_ENCODING_LINEAR) {
return (rc);
}
/* get the soft state structure */
switch (sample_rate) {
case M1575_SAMPR8000:
case M1575_SAMPR9600:
case M1575_SAMPR11025:
case M1575_SAMPR16000:
case M1575_SAMPR18900:
case M1575_SAMPR22050:
case M1575_SAMPR27420:
case M1575_SAMPR32000:
case M1575_SAMPR33075:
case M1575_SAMPR37800:
case M1575_SAMPR44100:
case M1575_SAMPR48000:
rc = AUDIO_SUCCESS;
break;
default:
ATRACE("audio1575_ad_set_format() bad sample rate",
goto done;
}
switch (dir) {
case AUDIO_PLAY:
break;
case AUDIO_RECORD:
break;
default:
rc = AUDIO_FAILURE;
break;
}
done:
return (rc);
} /* audio1575_ad_set_format() */
/*
* audio1575_ad_start_play()
*
* Description:
* This routine starts the playback DMA engine
*
* Arguments:
* audiohdl_t ahandle Handle to this device
* int stream Stream number for multi-stream Codecs,
* which is not how we program the device
* for now.
* Returns:
*/
static int
{
int rc = AUDIO_FAILURE;
/* get ptr to play buffer */
/* we are already PLAYING audio */
rc = AUDIO_SUCCESS;
goto done;
}
/* If play was PAUSED then Start Playing */
goto done;
}
rc = AUDIO_SUCCESS;
goto done;
}
/* we are here for the first time to play audio */
if (!buf->io_started) {
/* Prepare the DMA Engine */
goto done;
}
goto done;
}
/* set last valid index to zero */
/* set the buffer base */
/* Set the IOCE bit */
}
if ((audio1575_fill_play_buf(statep)) ==
} else {
rc = AUDIO_SUCCESS;
}
done:
return (rc);
} /* audio1575_ad_start_play() */
/*
* audio1575_ad_pause_play()
*
* Description:
* This routine pauses the play DMA engine.
*
* Arguments:
* audiohdl_t ahandle Handle to this device
* int stream Stream number for multi-stream Codecs,
* which is not how we program the device
* for now.
*
* Returns:
* void
*/
static void
{
/* do nothing if not running */
if ((dmacr & M1575_DMACR_PCMOSTART) !=
ATRACE("audio1575_pause_play() DMA engine "
"already stopped", statep);
goto done;
}
}
/* Stop the DMA and set DMA pause flag */
goto done;
}
done:
} /* audio1575_ad_pause_play() */
/*
* audio1575_ad_stop_play()
*
* Description:
* This routine stops the playback DMA engine.
*
* Arguments:
* audiohdl_t ahandle Handle for this driver
* int stream Stream number for multi-stream Codecs,
* which is not how we program the device
* for now.
*
* Returns:
* void
*/
static void
{
/* reset the DMA Play engine */
goto done;
}
/* Clear the PCM Out Control Register */
/* clear the play started and paused flags */
done:
} /* audio1575_ad_stop_play() */
/*
* audio1575_ad_start_record()
*
* Description:
* This routine starts the PCM in DMA engine
*
* Arguments:
* audiohdl_t ahandle Handle to this device
* int stream Stream number for multi-stream Codecs,
* which isn't going to apply for record
*
* Returns:
* AUDIO_SUCCESS Recording successfully started
* AUDIO_FAILURE Record not started
*/
static int
{
int rc = AUDIO_FAILURE;
/* get our record buffer ptr */
rc = AUDIO_SUCCESS;
goto done;
}
/* Prepare DMA engine */
goto done;
}
/* DMA Engine reset was successful */
/* Set the IOCE bit */
/* set last valid index to 0 */
/* setup the Base Address Register */
if ((audio1575_prepare_record_buf(statep)) ==
rc = AUDIO_SUCCESS;
} else {
}
done:
return (rc);
} /* audio1575_ad_start_record() */
/*
* audio1575_ad_stop_record()
*
* Description:
* This routine stops the PCM in DMA engine
*
* Arguments:
* audiohdl_t ahandle Handle for this driver
* int stream Stream number for multi-stream
* Codecs, which isn't going to apply
* for record
*
* Returns:
* void
*/
static void
{
/* reset the DMA input registers */
goto done;
}
/* clear the PCM In Control Register */
/* clear the record started flag */
done:
} /* audio1575_ad_stop_record() */
/* *********************** Local Routines *************************** */
/*
* audio1575_init_state()
*
* Description:
* This routine initializes the audio driver's state structure
*
* CAUTION: This routine cannot allocate resources, unless it frees
* them before returning from an error. Also, error_destroy:
* in audio1575_attach() would need to be fixed as well.
*
* Arguments:
* audio1575_state_t *state The device's state structure
* dev_info_t *dip Pointer to the device's
* dev_info struct
* Returns:
* AUDIO_SUCCESS State structure initialized
* AUDIO_FAILURE State structure not initialized
*/
static int
{
int rints;
int pints;
int count = 0;
int actual = 0;
int rc = AUDIO_SUCCESS;
/* default to 5 bit volume */
/* see if we support internal CDROM */
DDI_PROP_DONTPASS, "cdrom", 0);
/* get the mode from the .conf file */
"mixer-mode", AM_MIXER_MODE)) {
} else {
}
/* get play interrupts */
if (pints > M1575_MAX_INTS) {
ATRACE("audio1575_init_state() "
"play interrupt rate too high, resetting", pints);
"init_state() "
"play interrupt rate set too high, %d, resetting to %d",
pints, M1575_INTS);
pints = M1575_INTS;
} else if (pints < M1575_MIN_INTS) {
ATRACE("audio1575_init_state() "
"play interrupt rate too low, resetting", pints);
"init_state() "
"play interrupt rate set too low, %d, resetting to %d",
pints, M1575_INTS);
pints = M1575_INTS;
}
/* get record interrupts */
if (rints > M1575_MAX_INTS) {
ATRACE("audio1575_init_state() "
"record interrupt rate too high, resetting", rints);
"init_state() "
"record interrupt rate set too high, %d, resetting to %d",
rints, M1575_INTS);
rints = M1575_INTS;
} else if (rints < M1575_MIN_INTS) {
ATRACE("audio1575_init_state() "
"record interrupt rate too low, resetting", rints);
"init_state() "
"record interrupt rate set too low, %d "
rints = M1575_INTS;
}
/* get supported interrupt types */
if ((rc != DDI_SUCCESS) ||
CE_WARN, "!init_state() Fixed type interrupts not "
"supported");
ATRACE("audio1575_init_state() fixed type INTR not supported",
NULL);
goto error_intr;
}
/* make sure we only have one fixed type interrupt */
"!init_state() no fixed interrupts");
ATRACE("audio1575_init_state() no fixed interrupts",
NULL);
goto error_intr;
}
/* allocate interrupt table */
KM_SLEEP);
"!init_state() ddi_intr_alloc() failure");
goto error_alloc;
}
/* Get the interrupt priority for initializing the m1575_intr_mutex */
"!init_state() ddi_intr_get_pri() failure");
ATRACE("audio1575_init_state() ddi_intr_get_pri() failure",
NULL);
goto error_free;
}
/* test for a high level interrupt */
"!init_state() unsupported high level interrupt");
ATRACE("audio1575_init_state() high level interrupt not "
"supported", NULL);
goto error_free;
}
/* fill in the device default state */
if (statep->m1575_cdrom) {
}
/*
* fill in the ad_info structure
*/
/* play capabilities */
/* record capabilities */
/* fill in device info strings */
/* compute play and record buffer sizes */
statep->m1575_flags = 0;
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audio1575_init_state() */
/*
* audio1575_map_regs()
*
* Description:
* This routine allocates the DMA handles and the memory for the
* DMA engines to use. Finally, the registers are mapped in.
*
* CAUTION: Make sure all errors call audio_sup_log().
*
* Arguments:
* dev_info_t *dip Pointer to the device's devinfo
*
* Returns:
* AUDIO_SUCCESS Registers successfully mapped
* AUDIO_FAILURE Registers not successfully mapped
*/
static int
{
statep->m1575_res_flags = 0;
/* Check for fault management capabilities */
}
/* map the M1575 Audio PCI Cfg Space */
"!audio1575_map_regs() PCI Config mapping failure");
goto error;
}
/* map the M1575 Audio registers in PCI IO Space */
"!audio1575_map_regs() Audio IO mapping failure");
goto error;
}
/* map the M1575 Audio registers in PCI MEM32 Space */
"!audio1575_map_regs() Audio MEM32 mapping failure");
goto error;
}
/*
* Here we allocate DMA memory for the buffer descriptor list.
* we allocate adjacent DMA memory for all DMA engines.
*/
"!audio1575_map_regs() ddi_dma_alloc_handle "
"(bdlist) failure");
ATRACE("audio1575_map_regs() ddi_dma_alloc_handle() failure",
NULL);
goto error;
}
/*
* we allocate all buffer descriptors lists in contiguous dma memory.
*/
"!audio1575_map_regs() ddi_dma_mem_alloc(bdlist) failure");
ATRACE("audio1575_map_regs() ddi_dma_mem_alloc(bdlist) failure",
NULL);
goto error;
}
&count) != DDI_DMA_MAPPED) {
"!audio1575_map_regs() addr_bind_handle failure");
ATRACE("audio1575_map_regs() ddi_dma_addr_bind_handle failure",
NULL);
goto error;
}
sizeof (m1575_bd_entry_t) * M1575_BD_NUMS;
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audio1575_map_regs() */
/*
* audio1575_unmap_regs()
*
* Description:
* This routine unbinds the play and record DMA handles, frees
* the DMA buffers and the unmaps control registers.
*
* Arguments:
* audio1575_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
}
}
}
}
}
}
} /* audio1575_unmap_regs() */
/*
* audio1575_alloc_sample_buf()
*
* Description:
* This routine allocates DMA buffers for the sample buffer. It
* allocates two DMA chunks (buffers) to the specified DMA engine
* (sample buffer structure). The two data chunks will be bound
* to the buffer descriptor entries of corresponding buffer
* descriptor list, and be used to transfer audio sample data to
* and from the audio controller.
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int which Which sample buffer, PCM in or PCM out
* M1575_DMA_PCM_IN ---PCM in DMA engine
* M1575_DMA_PCM_OUT---PCM out DMA engine
* int len The length of the DMA buffers
*
* Returns:
* AUDIO_SUCCESS Allocating DMA buffers successfully
* AUDIO_FAILURE Failed to allocate dma buffers
*/
static int
{
int i;
int j;
int handle_cnt = 0;
int buf_cnt = 0;
int bind_cnt = 0;
switch (which) {
case M1575_DMA_PCM_OUT:
for (i = 0; i < M1575_PLAY_BUFS; i++) {
goto error;
}
handle_cnt++;
DDI_SUCCESS) {
goto error;
}
buf_cnt++;
goto error;
}
bind_cnt++;
}
break;
case M1575_DMA_PCM_IN:
for (i = 0; i < M1575_REC_BUFS; i++) {
DDI_SUCCESS) {
goto error;
}
handle_cnt++;
goto error;
}
buf_cnt++;
goto error;
}
bind_cnt++;
}
break;
default:
ATRACE("audio1575_alloc_sample_buf() unknown buffer type",
which);
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
for (j = 0; j < bind_cnt; j++) {
}
for (j = 0; j < buf_cnt; j++) {
}
for (j = 0; j < handle_cnt; j++) {
}
return (AUDIO_FAILURE);
} /* audio1575_alloc_sample_buf() */
/*
* audio1575_free_sample_buf()
*
* Description:
* This routine frees the DMA buffers of the sample buffer. The DMA
* buffers were allocated by calling audio1575_alloc_sample_buf().
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int which Which sample buffer, PCM in or PCM out
*
* Returns:
* void
*/
static void
{
int i;
switch (which) {
case M1575_DMA_PCM_IN:
for (i = 0; i < M1575_REC_BUFS; i++) {
}
break;
case M1575_DMA_PCM_OUT:
for (i = 0; i < M1575_PLAY_BUFS; i++) {
}
break;
default:
ATRACE("audio1575_free_sample() unknown buffer type",
which);
break;
}
} /* audio1575_free_sample_buf() */
/*
* audio1575_reclaim_play_buf()
*
* Description:
* When the audio controller finishes fetching the data from DMA
* buffers, this routine will be called by interrupt handler to
* reclaim the DMA buffers.
*
* Arguments:
* audio1575_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
/* get the play buf ptr */
/* get out current index value */
}
} /* audio1575_reclaim_play_buf() */
/*
* audio1575_init_ac97()
*
* Description:
* This routine initializes the AC97 codec.
* The AC97 codec registers are programmed from codec_shadow[].
* If we are not doing a restore, we initialize codec_shadow[], otherwise
* we use the current values of shadow
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int restore If M1575_INIT_RESTORE then
* restore from codec_shadow[]
* Returns:
* AUDIO_SUCCESS The hardware was initialized properly
* AUDIO_FAILURE The hardware couldn't be initialized properly
*/
static int
{
int i;
/* AC97 register reset */
return (AUDIO_FAILURE);
}
/* turn on the AD1981B codec power and wait for analog i/o be ready */
(void) audio1575_read_ac97(statep,
(void) audio1575_write_ac97(statep,
/*
* Wait 1 sec for the analog section to power up
* checking every 10 ms.
*/
for (i = 0; i < M1575_LOOP_CTR; i++) {
(void) audio1575_read_ac97(statep,
break;
}
#ifndef __lock_lint
#endif
}
/* return failure if the codec did not power up */
if (i >= M1575_LOOP_CTR) {
"!failure to power up the AC97 Codec");
return (AUDIO_FAILURE);
}
/* point to our codec shadow registers */
if (restore == M1575_INIT_NO_RESTORE) {
for (i = 0; i < M1575_LAST_AC_REG; i += 2) {
(void) audio1575_read_ac97(statep, i,
&(shadow[M1575_CODEC_REG(i)]));
}
/* 02h - set master line out volume, muted, 0dB */
/* 04h - set alternate line out volume, muted, 0dB */
/* 06h - set master mono volume, muted, 0dB */
/* 0ch - set phone input, mute, 0dB attenuation */
/*
* 0eh - set mic input, mute, +20dB attenuation
* actually this is 30dB when MICVR_20dB_Boost
* is set. (see misc. register 0x76 setting
* of MIC_30dB_GAIN below.)
*/
/* 10h - set line input, mute, 0dB attenuation */
/* 12h - set cd input, mute, 0dB attenuation */
/* 16h - set aux input, mute, 0dB attenuation */
/* 18h - set PCM out input, NOT muted, 0dB gain */
/* 1ah - set input device as mic */
/* 1ch - set record gain to 0dB and not muted */
/* 20h - set GP register, mic 1, everything else off */
/* 28h - set to use Primary Codec channels 1 & 2 */
/* 2ah - set to use Primary Codec channels 1 & 2 */
= 0;
/* 2ch - PCM Front DAC Sample Rate */
/* 32h - PCM ADC Sample Rate */
/* 64h - Mixer ADC Gain Register */
/* 76h - Misc. Control Bit Register */
}
/* Now we set the AC97 codec registers to the saved values */
(void) audio1575_write_ac97(statep, i,
shadow[M1575_CODEC_REG(i)]);
}
/*
* Now we setup the EQ register to scale 16 bit linear pcm
* values into 20 bit DAC values.
*/
/* 60h - EQ Control Register */
/*
* Here we set up the 6 biquad IIR filters to use
* coefficient a0 as our scaling factor. The equation for
* this filter is:
*
* Y(n) = a0(a1(y(n-1))+a2(y(n-2))+b2(x(n-2))+b1(x(n-1)))
* we use the following coefficient values:
*
* a0 = 0x000f;
* a1 = 0x0001;
* a2 = 0x0001;
* b1 = 0x0001;
* b2 = 0x0001;
*/
for (i = 0; i < AD1981_MAX_FILTERS; i++) {
}
/* The AD1981B Codec only implements 5 bit volume register */
}
/* resume the master volume to mute */
MVR_MUTE);
/*
* if the codec chip do support variable sample rate,
* we set the sample rate to 48K
*/
"!AD8191B codec does not support VRA");
return (AUDIO_FAILURE);
} else {
/* set variable sample rate mode */
(void) audio1575_write_ac97(statep,
}
ATRACE("audio1575_ac97_init() sample rate",
return (AUDIO_SUCCESS);
} /* audio1575_init_ac97() */
/*
* audio1575_chip_init()
*
* Description:
* This routine initializes the M1575 AC97 audio controller and the AC97
* codec. The AC97 codec registers are programmed from codec_shadow[].
* If we are not doing a restore, we initialize codec_shadow[], otherwise
* we use the current values of shadow. This routine expects that the
* PCI IO and Memory spaces have been mapped and enabled already.
* Arguments:
* audio1575_state_t *state The device's state structure
* int restore If M1575_INIT_RESTORE then
* restore from codec_shadow[]
* Returns:
* AUDIO_SUCCESS The hardware was initialized properly
* AUDIO_FAILURE The hardware couldn't be initialized properly
*/
static int
{
int i;
int j;
int rc;
/*
* SADA only supports stereo, so we set the channel bits
* to "00" to select 2 channels.
* will also set the following:
*
* Disable double rate enable
* no SPDIF output selected
* 16 bit audio record mode
* 16 bit pcm out mode
* PCM Out 6 chan mode FL FR CEN BL BR LFE
* PCM Out 2 channel mode (00)
*/
for (i = 0; i < M1575_LOOP_CTR; i++) {
/* Reset the AC97 Codec and default to 2 channel 16 bit mode */
#ifndef __lock_lint
#endif
/* Read the System Status Reg */
/* make sure and release the blocked reset bit */
if (ssr & M1575_SSR_RSTBLK) {
#ifndef __lock_lint
#endif
/* Read the System Status Reg */
/* make sure and release the blocked reset bit */
if (ssr & M1575_SSR_RSTBLK) {
ATRACE("audio1575_chip_init()! RSTBLK failure",
ssr);
return (AUDIO_FAILURE);
}
/* Reset the controller */
#ifndef __lock_lint
#endif
}
/* according AC'97 spec, wait for codec reset */
for (j = 0; j < M1575_LOOP_CTR; j++) {
if ((scr & M1575_SCR_COLDRST) == 0) {
break;
}
#ifndef __lock_lint
#endif
}
/* codec reset failed */
if (j >= M1575_LOOP_CTR) {
"!failure to reset codec");
ATRACE("audio1575_chip_init() "
"!AC97 COLDRST failure", scr);
return (AUDIO_FAILURE);
}
/*
* Wait for FACRDY First codec ready. The hardware can
* provide the state of
* codec ready bit on SDATA_IN[0] and as reflected in
* the Recv Tag Slot Reg.
*/
if (rtsr & M1575_RTSR_FACRDY) {
break;
} else { /* reset the status and wait for new status to set */
drv_usecwait(10);
}
}
/* if we could not reset the AC97 codec then report failure */
if (i >= M1575_LOOP_CTR) {
"!chip_init() no codec ready signal received");
ATRACE("audio1575_chip_init() AC97 Codec init "
"failure - BAD Board", rtsr);
return (AUDIO_FAILURE);
}
/* Magic code from ULi to Turn on the AC_LINK clock */
(void) M1575_PCI_GET8(M1575_PCIACD_REG);
if (clk_detect != 1) {
"!chip_init() No AC97 Clock Detected");
ATRACE("audio1575_chip_init() no AC97 Clock detected",
return (AUDIO_FAILURE);
}
/* Magic code from Uli to Init FIFO1 and FIFO2 */
/* Disable SPDIF Output */
/* Disable Everyone */
/* now initialize the AC97 codec */
return (rc);
} /* audio1575_chip_init() */
/*
* audio1575_dma_stop()
*
* Description:
* This routine is used to put each DMA engine into the quiet state.
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*
* Returns:
* void
*/
static void
{
int i;
/* pause bus master (needed for the following reset register) */
for (i = 0; i < M1575_LOOP_CTR; i++) {
if (dmacr & M1575_DMACR_PAUSE_ALL) {
break;
}
drv_usecwait(10);
}
if (i >= M1575_LOOP_CTR) {
"!dma_stop() failed to stop DMA engines");
ATRACE("audio1575_dma_stop() failure "
"to stop DMA engines", dmacr);
return;
}
/* Pause bus master (needed for the following reset register) */
/* Reset the bus master registers for all DMA engines */
/* Reset FIFOS */
/* Clear Interrupts */
/*
* clear the interrupt control and status register
* for buggy hardware
*/
intrcr = 0L;
/* clear all flags except DMA suspend flag */
} /* audio1575_dma_stop() */
/*
* audio1575_set_gain()
*
* Description:
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int dir AUDIO_PLAY or AUDIO_RECORD, if
* direction is important
* int arg1 The gain to set
* int arg2 The channel, 0 == left
* or 1 == right
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The gain has not been set
*/
static int
{
if (gain > AUDIO_MAX_GAIN) {
} else if (gain < AUDIO_MIN_GAIN) {
}
switch (dir) {
case AUDIO_PLAY:
/*
* For play we use PCM so all volumes change with just
* one write. This way we get line out, headphone and
* internal speaker in one shot.
*
* The AC97 Codec goes from -34.5 dB (11111) to 0 dB
* (01000) to +12.0 dB (00000). We turn gain into attenuation
* The AD1981B codec uses attenuation instead of gain as well.
* abs(-34.5dB)+12.dB = 46.5dB of dynamic range/1.5dB per bit
* gives a range of 0x00-0x1f (0-31) values.
*/
/* read the play gain register */
&tmp);
tmp &= ~PCMOVR_MUTE;
if (channel == 0) { /* left channel */
tmp &= ~PCMOVR_LEFT_GAIN_MASK;
} else { /* right channel */
}
/* update the play gain register */
(void) audio1575_write_ac97(statep,
&tmp);
break;
case AUDIO_RECORD:
/*
* For record we use the master record gain with all
* of the inputs set to 0dB.
* The AC97 Codec goes from 0 dB (0000) to +22.5 dB
* (1111) 22.5 dB/1.5dB per bit = range of 0x0-0xf;
* Note: this is gain not attenuation.
*/
/* read the record gain register */
(void) audio1575_read_ac97(statep,
if (channel == 0) { /* left channel */
tmp &= ~RGR_LEFT_MASK;
} else { /* right channel */
tmp &= ~RGR_RIGHT_MASK;
}
/* update the record gain register */
(void) audio1575_write_ac97(statep,
(void) audio1575_read_ac97(statep,
break;
default:
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_set_gain() */
/*
* audio1575_set_port()
*
* Description:
*
* Arguments:
* audio1575_state_t *state The device's state structure
* which is not how we program
* the device for now.
* int dir AUDIO_PLAY or AUDIO_RECORD,
* if direction is important
* int port The port to set:
* AUDIO_SPEAKER output to built-in speaker
* AUDIO_HEADPHONE output to headphone
* AUDIO_LINE_OUT output to line out
*
* AUDIO_MICROPHONE input from microphone
* AUDIO_LINE_IN input from line in
* AUDIO_CD input from internal CD
* AUDIO_CODEC_LOOPB_IN input from Codec
* internal loopback
*
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The port could not been set
*/
static int
{
switch (dir) {
case AUDIO_PLAY: /* output port */
if (port == M1575_PORT_UNMUTE) {
}
if (port & AUDIO_SPEAKER) {
(void) audio1575_and_ac97(statep,
tmp |= AUDIO_SPEAKER;
} else {
(void) audio1575_or_ac97(statep,
}
if (port & AUDIO_LINE_OUT) {
(void) audio1575_and_ac97(statep,
tmp |= AUDIO_LINE_OUT;
} else {
(void) audio1575_or_ac97(statep,
}
if (port & AUDIO_HEADPHONE) {
(void) audio1575_and_ac97(statep,
tmp |= AUDIO_HEADPHONE;
} else {
(void) audio1575_or_ac97(statep,
}
intfcr |= (M1575_INTFCR_PCMOENB);
return (AUDIO_FAILURE);
}
break;
case AUDIO_RECORD: /* input port */
switch (port) {
case AUDIO_NONE:
/* set to an unused input */
/* mute the master record input */
(void) audio1575_or_ac97(statep,
if (statep->m1575_monitor_gain) {
if (statep->m1575_input_port ==
(void) audio1575_or_ac97(statep,
} else if (statep->m1575_input_port ==
(void) audio1575_or_ac97(statep,
} else if (statep->m1575_input_port ==
AUDIO_CD) {
(void) audio1575_or_ac97(statep,
}
}
break;
case AUDIO_MICROPHONE:
/* set to the mic input */
if (statep->m1575_monitor_gain) {
(void) audio1575_or_ac97(statep,
} else if (statep->m1575_input_port ==
AUDIO_CD) {
(void) audio1575_or_ac97(statep,
}
(void) audio1575_write_ac97(statep,
}
/* Enable the MIC input on AC Link Channel 3 */
intfcr |= (M1575_INTFCR_PCMIENB);
break;
case AUDIO_LINE_IN:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (statep->m1575_monitor_gain) {
if (statep->m1575_input_port ==
(void) audio1575_or_ac97(statep,
} else if (statep->m1575_input_port ==
AUDIO_CD) {
(void) audio1575_or_ac97(statep,
}
(void) audio1575_write_ac97(statep,
}
intfcr &= ~(M1575_INTFCR_MICENB);
intfcr |= (M1575_INTFCR_PCMIENB);
break;
case AUDIO_CD:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (statep->m1575_monitor_gain) {
if (statep->m1575_input_port ==
(void) audio1575_or_ac97(statep,
} else if (statep->m1575_input_port ==
(void) audio1575_or_ac97(statep,
}
(void) audio1575_write_ac97(statep,
}
intfcr &= ~(M1575_INTFCR_MICENB);
intfcr |= (M1575_INTFCR_PCMIENB);
break;
case AUDIO_CODEC_LOOPB_IN:
/* set to the loopback input */
if (statep->m1575_monitor_gain) {
(void) audio1575_or_ac97(statep,
} else if (statep->m1575_input_port ==
(void) audio1575_or_ac97(statep,
} else if (statep->m1575_input_port ==
AUDIO_CD) {
(void) audio1575_or_ac97(statep,
}
}
intfcr |= (M1575_INTFCR_PCMIENB);
break;
default:
return (AUDIO_FAILURE);
}
/* select the input */
(void) audio1575_write_ac97(statep,
if ((port != AUDIO_NONE) &&
AC97_RECORD_GAIN_REGISTER)] & RGR_MUTE)) {
(void) audio1575_and_ac97(statep,
}
break;
}
return (AUDIO_SUCCESS);
} /* audio1575_set_port() */
/*
* audio1575_set_monitor_gain()
*
* Description:
* Set the monitor gain.
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int gain The gain to set
*
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The gain has not been set
*/
static int
{
int rc = AUDIO_SUCCESS;
}
if (gain == 0) {
/* disable loopbacks when gain == 0 */
} else {
/* Adjust the value of gain to the requirement of AC'97 */
}
switch (statep->m1575_input_port) {
case AUDIO_NONE:
/*
* It is possible to set the value of gain before any input
* is selected. So, we just save the gain and then return
* SUCCESS.
*/
break;
case AUDIO_MICROPHONE:
(void) audio1575_write_ac97(statep,
break;
case AUDIO_LINE_IN:
(void) audio1575_write_ac97(statep,
break;
case AUDIO_CD:
(void) audio1575_write_ac97(statep,
break;
case AUDIO_CODEC_LOOPB_IN:
/* we already are setting the loopback, so done */
goto done;
default:
/* this should never happen! */
ATRACE("audio1575_ad_set_config() monitor gain bad device",
rc = AUDIO_FAILURE;
goto done;
}
if (gain == 0) {
statep->m1575_monitor_gain = 0;
} else {
}
done:
return (rc);
} /* audio1575_set_monitor_gain() */
/*
* audio1575_codec_sync()
*
* Description:
* Serialize access to the AC97 audio mixer registers.
*
* Arguments:
* audio1575_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS Ready for an I/O access to the codec
* AUDIO_FAILURE An I/O access is currently in progress, can't
* perform another I/O access.
*/
static int
{
int i;
int j;
/* do the Uli Shuffle ... */
for (i = 0; i < M1575_LOOP_CTR; i++) {
/* Read the semaphore */
/* loop till we own the semaphore */
if ((casr & 1) == 0) {
for (j = 0; j < M1575_LOOP_CTR; j++) {
/* Wait for CWRSUCC 0x8 */
if ((cspsr & M1575_CSPSR_SUCC) ==
return (AUDIO_SUCCESS);
}
drv_usecwait(1);
}
}
drv_usecwait(10);
}
return (AUDIO_FAILURE);
} /* audio1575_codec_sync() */
/*
* audio1575_and_ac97()
*
* Description:
* Logically AND the value with the specified ac97 codec register
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int reg AC97 register number
* uint16_t data The value to AND
*
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The Codec parameter has not been set
*/
static int
{
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_and_ac97() */
/*
* audio1575_or_ac97()
*
* Description:
* Logically OR the value with the specified ac97 codec register
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int reg AC97 register number
* uint16_t data The value to OR
*
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The Codec parameter has not been set
*/
static int
{
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_or_ac97() */
/*
* audio1575_write_ac97()
*
* Description:
* Set the specific AC97 Codec register.
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int reg AC97 register number
* uint16_t data The data want to be set
*
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The Codec parameter has not been set
*/
static int
{
int i;
return (AUDIO_FAILURE);
}
/* write the data to WRITE to the lo word of the CPR register */
/* write the address to WRITE to the hi word of the CPR register */
/* wait until command is completed sucessfully */
for (i = 0; i < M1575_LOOP_CTR; i++) {
/* Wait for Write Ready 0x01 */
break;
}
drv_usecwait(1);
}
if (i >= M1575_LOOP_CTR) {
ATRACE("audio1575_write_ac97() failure", i);
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_write_ac97() */
/*
* audio1575_read_ac97()
*
* Description:
* Get the specific AC97 Codec register. It also updates codec_shadow[]
* with the register value.
*
* Arguments:
* audio1575_state_t *state The device's state structure
* int reg AC97 register number
* uint16_t *data The data to be returned
*
* Returns:
* AUDIO_SUCCESS Reading the codec register successfully
* AUDIO_FAILURE Failed to read the register
*/
static int
{
int i;
*data = 0xffff;
return (AUDIO_FAILURE);
}
/*
* at this point we has the CASR semaphore
* and the codec is r/w ready
* OR in the READ opcode into the address field
*/
/* write the address to READ to the hi word of the CPR register */
/* wait until command is completed sucessfully */
for (i = 0; i < M1575_LOOP_CTR; i++) {
/* Wait for Read Ready 0x02 */
break;
}
drv_usecwait(1);
}
if (i >= M1575_LOOP_CTR) {
*data = 0xffff;
return (AUDIO_FAILURE);
}
/* read back the data and address */
return (AUDIO_FAILURE);
}
/* store new value in codec shadow register */
return (AUDIO_SUCCESS);
} /* audio1575_read_ac97() */
/*
* audio1575_reset_ac97()
*
* Description:
* Reset AC97 Codec register.
*
* Arguments:
* audio1575_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS Reset the codec successfully
* AUDIO_FAILURE Failed to reset the codec
*/
static int
{
return (AUDIO_FAILURE);
}
return (AUDIO_FAILURE);
}
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_reset_ac97() */
/*
* audio1575_fill_play_buf()
*
* Description:
* This routine is called by m1575_ad_start_play() and the interrupt
* handler. It fills playback samples into the DMA memory, sets the
* BDL entries, and starts the playback DMA engine.
* the m1575_intr_mutex must be held on entry.
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*
* Returns:
* AUDIO_SUCCESS Starting PCM out engine successfully
* AUDIO_FAILURE Failed to start PCM out engine.
*/
static int
{
int samples;
int rs;
int bufcount = 0;
int rc = AUDIO_SUCCESS;
/* get the play buffer pointer */
return (AUDIO_SUCCESS);
}
/* compute number of samples */
/* if not an even number of samples we panic! */
/* if stereo & sr = 11025 & ints = 50 then 441 samples, bad! - so fix */
if ((samples & 1) != 0) {
samples++;
}
(buf->io_started)) {
"!fill_play_buf() Err:PLAY started and IO started");
ATRACE("audio1575_fill_play_buf() Err:PLAY STARTED "
"and IO STARTED", NULL);
return (AUDIO_FAILURE);
}
/* no more samples to play */
if (rs <= 0) {
if (bufcount != 0) {
break;
}
/*
* Clear the flag so if audio is restarted while
* in am_play_shutdown() we can detect it and
* not mess things up.
*/
/* shutdown the mixer */
/*
* Make sure playing wasn't restarted when lock
* lost if reopened, should return success
*/
if (statep->m1575_flags &
return (AUDIO_SUCCESS);
}
/* Finished playing, then stop it */
if (rc == AUDIO_FAILURE) {
ATRACE("audio1575_fill_play_buf() "
"dma_reset failure", rc);
return (rc);
}
/* clear the PCM Out ctrl reg */
/* clr the flags getting ready for next start */
/* return the value for m1575_ad_start_play() */
return (AUDIO_FAILURE);
} else {
/* M1575_DMA_PLAY_EMPTY */
}
} else {
/* !M1575_DMA_PLAY_EMPTY */
bufcount++;
}
/* put the samples into buffer descriptor list entry */
}
/* start PCM out engine */
!(dmacr & M1575_DMACR_PCMOSTART)) {
}
return (AUDIO_SUCCESS);
} /* audio1575_fill_play_buf() */
/*
* audio1575_prepare_record_buf()
*
* Description:
* This routine is called by audio1575_ad_start_record(). It prepares DMA
* memory for PCM in engine, sets the buffer descriptor entries for PCM
* in engine, and starts PCM in engine for recording.
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*
* Returns:
* AUDIO_SUCCESS Started PCM in engine successfully
* AUDIO_FAILURE Failed to start PCM in engine.
*/
static int
{
int samples;
/* get the record buf ptr */
return (AUDIO_SUCCESS);
}
/* if not an even number of samples we panic! */
/* if stereo & sr = 11025 & ints = 50 then 441 samples, bad! - so fix */
if ((samples & 1) != 0) {
samples++;
}
}
if (!buf->io_started) {
}
/* start PCM In engine */
ATRACE("audio1575_prepare_record_buf() "
"RECORD DMA STARTED", dmacr);
}
return (AUDIO_SUCCESS);
} /* audio1575_prepare_record_buf() */
/*
* audio1575_reclaim_record_buf()
*
* Description:
* This routine is called by the interrupt handler. It sends the PCM
* samples (record data) up to the mixer module by calling am_send_audio(),
* and reclaims the buffer descriptor entries for PCM in engine.
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*
* Returns:
* void
*/
static void
{
int samples;
/* get record buf ptr */
/* get our current index value */
/* get number of samples */
/* While we have record buffers to process */
break;
}
}
} /* audio1575_reclaim_record_buf() */
/*
* audio1575_pci_enable()
*
* Description:
* This routine Enables all PCI IO and MEMORY accesses
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*
* Returns:
* void
*/
static void
{
} /* audio1575_pci_enable() */
/*
* audio1575_pci_disable()
*
* Description:
* This routine Disables all PCI IO and MEMORY accesses
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*
* Returns:
* void
*/
static void
{
} /* audio1575_pci_disable() */
/*
* audio1575_dma_pause()
*
* Description:
* This routine pauses DMA on a particular channel
*
* Arguments:
* audio1575_state_t *statep The device's state structure
* int chan The DMA channel PCM_IN,PCM_OUT,MIC_IN
*
* Returns:
* AUDIO_SUCCESS DMA paused successfully
* AUDIO_FAILURE DMA failed to pause
*/
static int
{
switch (chan) {
case M1575_DMA_PCM_IN:
break;
case M1575_DMA_PCM_OUT:
break;
default:
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_dma_pause() */
/*
* audio1575_dma_resume()
*
* Description:
* This routine resumes DMA on a particular channel
* It does not resume if the BDL list is empty.
*
* Arguments:
* audio1575_state_t *statep The device's state structure
* int chan The DMA channel
* PCM_IN,PCM_OUT,MIC_IN
*
* Returns:
* AUDIO_SUCCESS DMA resumed successfully
* AUDIO_FAILURE DMA failed to resume
*/
static int
{
switch (chan) {
case M1575_DMA_PCM_IN:
/* ULi says do fifo resets here */
break;
case M1575_DMA_PCM_OUT:
break;
default:
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_dma_resume() */
/*
* audio1575_dma reset()
*
* Description:
* This routine resets the DMA on a particular channel
* All DMA registers are RESET.
*
* Arguments:
* audio1575_state_t *statep The device's state structure
* int chan The DMA channel PCM_IN,PCM_OUT,MIC_IN
*
* Returns:
* AUDIO_SUCCESS DMA reset successfully
* AUDIO_FAILURE DMA failed to reset
*/
static int
{
switch (chan) {
case M1575_DMA_PCM_IN:
/* Uli FIFO madness ... */
cr |= M1575_PCMICR_RR;
break;
case M1575_DMA_PCM_OUT:
/* Uli FIFO madness ... */
cr |= M1575_PCMOCR_RR;
break;
default:
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio1575_dma_reset() */