audioixp.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.
*/
/*
* audioixp Audio Driver
*
* This driver supports audio hardware integrated in ATI IXP400 chipset.
*
* This driver uses the mixer Audio Personality Module to implement
* audio(7I) and mixer(7I) semantics. Both play and record are single
* streaming.
*
* The IXP400 audio core is an AC'97 controller, which has independent
* channels for PCM in, PCM out. The AC'97 controller is a PCI bus master
* 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. You dont need
* to use all these entries. Each entry contains a pointer to a data buffer,
* status, length of the buffer being pointed to and the pointer to the next
* entry. Length of the buffer is in number of bytes. Interrupt will be
* triggered each time a entry is processed by hardware.
*
* 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, audioixp_sample_buf,
* to handle 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. Two DMA buffers are allocated for both playback
* and record, and two BD entries are used. When processing interrupt,
* the current hardware pointer will be check to tell which buffer is
* being processed. It's possible for the hardware to interrupt twice
* for one buffer, this logic is handled in the routine
* audioixp_chunk_processed.
*
* 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 audioixp_chip_init().
*
* 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 audioixp_ad_set_config(audiohdl_t, int, int, int, int, int);
static int audioixp_ad_set_format(audiohdl_t, int, int, int, int, int, int);
static int audioixp_ad_start_play(audiohdl_t, int);
static void audioixp_ad_pause_play(audiohdl_t, int);
static void audioixp_ad_stop_play(audiohdl_t, int);
static int audioixp_ad_start_record(audiohdl_t, int);
static void audioixp_ad_stop_record(audiohdl_t, int);
/*
* interrupt handler
*/
/*
* Local Routine Prototypes
*/
static int audioixp_codec_sync(audioixp_state_t *);
static int audioixp_reset_ac97(audioixp_state_t *);
static void audioixp_unmap_regs(audioixp_state_t *);
static int audioixp_alloc_sample_buf(audioixp_state_t *, int, int);
static void audioixp_free_sample_buf(audioixp_state_t *,
static void audioixp_setup_bdl(audioixp_state_t *);
static void audioixp_start_dma(audioixp_state_t *, int);
static void audioixp_stop_dma(audioixp_state_t *, int);
static int audioixp_chip_init(audioixp_state_t *, int);
static void audioixp_chip_fini(audioixp_state_t *);
static int audioixp_chunk_processed(audioixp_state_t *, int);
static int audioixp_fill_play_buf(audioixp_state_t *);
static int audioixp_prepare_record_buf(audioixp_state_t *);
static void audioixp_reclaim_play_buf(audioixp_state_t *);
static void audioixp_reclaim_record_buf(audioixp_state_t *);
static int audioixp_set_gain(audioixp_state_t *, int, int, int);
static int audioixp_set_port(audioixp_state_t *, int, int);
static int audioixp_set_monitor_gain(audioixp_state_t *, int);
/*
* Global variables, but used only by this file.
*/
/* anchor for soft state structures */
static void *audioixp_statep;
/* driver name, so we don't have to call ddi_driver_name() or hard code strs */
static char *audioixp_name = IXP_NAME;
/*
* STREAMS structures
*/
/* STREAMS driver id and limit value struct */
static struct module_info audioixp_modinfo = {
IXP_IDNUM, /* module ID number */
IXP_NAME, /* module name */
IXP_MINPACKET, /* minimum packet size */
IXP_MAXPACKET, /* maximum packet size */
IXP_HIWATER, /* high water mark */
IXP_LOWATER, /* low water mark */
};
/* STREAMS queue processing procedures structures */
/* read queue */
static struct qinit audioixp_rqueue = {
audio_sup_rput, /* put procedure */
audio_sup_rsvc, /* service procedure */
audio_sup_open, /* open procedure */
audio_sup_close, /* close procedure */
NULL, /* unused */
&audioixp_modinfo, /* module parameters */
NULL /* module statistics */
};
/* write queue */
static struct qinit audioixp_wqueue = {
audio_sup_wput, /* write procedure */
audio_sup_wsvc, /* service procedure */
NULL, /* open procedure */
NULL, /* close procedure */
NULL, /* unused */
&audioixp_modinfo, /* module parameters */
NULL /* module statistics */
};
/* STREAMS entity declaration structure */
static struct streamtab audioixp_str_info = {
&audioixp_rqueue, /* read queue */
&audioixp_wqueue, /* write queue */
NULL, /* mux lower read queue */
NULL, /* mux lower write queue */
};
/*
* DDI Structures
*/
/* Entry points structure */
static struct cb_ops audioixp_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 */
&audioixp_str_info, /* cb_str */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev, /* cb_awrite */
};
/* Device operations structure */
static struct dev_ops audioixp_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
audioixp_getinfo, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
audioixp_attach, /* devo_attach */
audioixp_detach, /* devo_detach */
nodev, /* devo_reset */
&audioixp_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 audioixp_modldrv = {
&mod_driverops, /* drv_modops */
IXP_MOD_NAME, /* drv_linkinfo */
&audioixp_dev_ops, /* drv_dev_ops */
};
/* Module linkage structure */
static struct modlinkage audioixp_modlinkage = {
MODREV_1, /* ml_rev */
(void *)&audioixp_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
static uint_t audioixp_mixer_srs[] = {
};
static uint_t audioixp_min_compat_srs[] = {
};
static uint_t audioixp_compat_srs [] = {
};
static am_ad_sample_rates_t audioixp_mixer_sample_rates = {
};
static am_ad_sample_rates_t audioixp_compat_sample_rates = {
};
/* Some codec, only support 48K sample rate */
};
static uint_t audioixp_channels[] = {
0
};
static am_ad_cap_comb_t audioixp_combinations[] = {
{ 0 }
};
/*
* 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 */
0, /* addr_lo */
0xffffffff, /* addr_hi */
0x0000ffff, /* count_max */
8, /* align, BDL must be aligned on a 8-byte boundary */
0x3c, /* burstsize */
8, /* minxfer, set to the size of a BDlist entry */
0x0000ffff, /* maxxfer */
0x00000fff, /* seg, set to the RAM pagesize of intel platform */
1, /* sgllen, there's no scatter-gather list */
8, /* granular, set to the value of minxfer */
0 /* flags, use virtual address */
};
/*
*/
static ddi_dma_attr_t sample_buf_dma_attr = {
0, /* addr_lo */
0xffffffff, /* addr_hi */
0x0001fffe, /* count_max */
2, /* align, data buffer is aligned on a 2-byte boundary */
0x3c, /* burstsize */
4, /* minxfer, set to the size of a sample data */
0x0001ffff, /* maxxfer */
0x0001ffff, /* seg */
1, /* sgllen, no scatter-gather */
4, /* granular, set to the value of minxfer */
0, /* flags, use virtual address */
};
static am_ad_entry_t audioixp_entry = {
NULL, /* ad_setup() */
NULL, /* ad_teardown() */
audioixp_ad_set_config, /* ad_set_config() */
audioixp_ad_set_format, /* ad_set_format() */
audioixp_ad_start_play, /* ad_start_play() */
audioixp_ad_pause_play, /* ad_pause_play() */
audioixp_ad_stop_play, /* ad_stop_play() */
audioixp_ad_start_record, /* ad_start_record() */
audioixp_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 (audioixp_state_t), 1)) != 0) {
ATRACE("audioixp ddi_soft_state_init() failed",
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 ********************************* */
/*
* audioixp_getinfo()
*/
static int
{
int instance;
int error;
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);
} /* audioixp_getinfo() */
/*
* audioixp_attach()
*
* Description:
* Attach an instance of the audioixp 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;
ATRACE("audioixp_attach() audioixp_statep",
switch (cmd) {
case DDI_ATTACH:
break;
/*
*/
case DDI_RESUME:
"%s%d: audioixp_attach() resume is not supported yet",
return (DDI_FAILURE);
default:
"!%s%d: audioixp_attach() unknown command: 0x%x",
return (DDI_FAILURE);
}
/* we don't support high level interrupts in the driver */
if (ddi_intr_hilevel(dip, 0) != 0) {
"!%s%d: audioixp_attach()"
" unsupported high level interrupt",
return (DDI_FAILURE);
}
/* allocate the soft state structure */
DDI_SUCCESS) {
"!%s%d: audioixp_attach() soft state allocate failed",
return (DDI_FAILURE);
}
NULL) {
"!%s%d: audioixp_attach() soft state failed",
goto error_state;
}
"!%s%d: audioixp_attach() audio_sup_register() failed",
goto error_state;
}
/* save private state */
"!audioixp_attach() init state structure failed");
goto error_audiosup;
}
/* map in the registers, allocate DMA buffers, etc. */
"!audioixp_attach() couldn't map registers");
goto error_destroy;
}
/* set PCI command register */
"!audioixp_attach() couldn't allocate play sample "
"buffers");
goto error_unmap;
}
"!audioixp_attach() couldn't allocate record sample "
"buffers");
goto error_dealloc_play;
}
/* set up kernel statistics */
KSTAT_FLAG_PERSISTENT)) != NULL) {
}
/* set up the interrupt handler */
DDI_SUCCESS) {
"!audioixp_attach() bad interrupt specification ");
goto error_kstat;
}
"!audioixp_attach() failed to init chip");
goto error_intr;
}
/* call the mixer attach() routine */
"!audioixp_attach() am_attach() failed");
goto error_intr;
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
} /* audioixp_attach() */
/*
* audioixp_detach()
*
* Description:
* Detach an instance of the audioixp 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;
ATRACE("audioixp_detach() audioixp_statep",
NULL) {
"!%s%d: audioixp_detach() get soft state failed",
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
/*
*/
case DDI_SUSPEND:
"audioixp_detach() suspend is not supported yet");
return (DDI_FAILURE);
default:
"!audioixp_detach() unknown command: 0x%x", cmd);
return (DDI_FAILURE);
}
/* stop DMA engines */
/* remove the interrupt handler */
/* free DMA memory */
/* free the kernel statistics structure */
}
/* detach audio mixer */
/*
* call the audio support module's detach routine to remove this
* driver completely from the audio driver architecture.
*/
return (DDI_SUCCESS);
} /* audioixp_detach */
/*
* audioixp_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.
*
* There's a hardware pointer which indicate memory location where
* the hardware is processing. We check this pointer to decide whether
* to handle the buffer and how many buffers should be handled.
* Refer to ATI IXP400/450 Register Reference Manual, page 193,194.
*
* 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
{
int intr_claimed = DDI_INTR_UNCLAIMED;
/* PCM in interrupt */
if (sr & IXP_AUDIO_INT_IN_DMA) {
}
}
/* PCM out interrupt */
if (sr & IXP_AUDIO_INT_OUT_DMA) {
(void) audioixp_fill_play_buf(statep);
}
}
/* system is too busy to process the input stream, ignore it */
if (sr & IXP_AUDIO_INT_IN_DMA_OVERFLOW) {
}
/* System is too busy, ignore it */
if (sr & IXP_AUDIO_INT_OUT_DMA_UNDERFLOW) {
}
if (sr & IXP_AUDIO_INT_CODEC0_NOT_READY) {
}
if (sr & IXP_AUDIO_INT_CODEC1_NOT_READY) {
}
if (sr & IXP_AUDIO_INT_CODEC2_NOT_READY) {
}
if (sr & IXP_AUDIO_INT_NEW_FRAME) {
}
if (intr_claimed == DDI_INTR_UNCLAIMED) {
return (DDI_INTR_UNCLAIMED);
}
/* update the kernel interrupt statistics */
}
return (DDI_INTR_CLAIMED);
} /* audioixp_intr() */
/* *********************** Mixer Entry Point Routines ******************* */
/*
* audioixp_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_SUCCESS;
/* get the soft state structure */
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) audioixp_or_ac97(statep,
} else {
(void) audioixp_or_ac97(statep,
}
(void) audioixp_or_ac97(statep,
(void) audioixp_or_ac97(statep,
} else { /* not muted */
/* by setting the port we unmute only active ports */
(void) audioixp_set_port(statep,
}
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) audioixp_or_ac97(statep,
} else { /* disable */
(void) audioixp_and_ac97(statep,
}
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
*/
rc = AUDIO_FAILURE;
ATRACE_32("audioixp_ad_set_config() unsupported command",
command);
break;
}
return (rc);
} /* audioixp_ad_set_config() */
/*
* audioixp_ad_set_format()
*
* Description:
* This routine is used to set a new audio control data format.
*
* 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, 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
{
/* get the soft state structure */
/* codec doesn't support variable sample rate */
if (sample_rate != IXP_SAMPR48000) {
"!audioixp_ad_set_format() bad sample"
" rate %d\n", sample_rate);
return (AUDIO_FAILURE);
}
} else {
switch (sample_rate) {
case IXP_SAMPR8000: break;
case IXP_SAMPR11025: break;
case IXP_SAMPR16000: break;
case IXP_SAMPR22050: break;
case IXP_SAMPR24000: break;
case IXP_SAMPR32000: break;
case IXP_SAMPR44100: break;
case IXP_SAMPR48000: break;
default:
ATRACE_32("audioixp_ad_set_format() bad SR",
return (AUDIO_FAILURE);
}
}
if (dir == AUDIO_PLAY) {
(void) audioixp_write_ac97(statep,
/*
* Some codecs before ac97 2.2, such as YMF753 produced by
* Yamaha LSI, don't have the AC'97 registers indexed range
* from 0x2c to 0x34. So we assume this kind of codec
* supports fixed 48k sample rate.
*/
(void) audioixp_read_ac97(statep,
if (val != sample_rate) {
ATRACE_32("audioixp_ad_set_format()"
" bad out SR", sample_rate);
"!audioixp_ad_set_format() bad out"
" SR %d\n", sample_rate);
return (AUDIO_FAILURE);
}
}
slot &= ~ (IXP_AUDIO_OUT_DMA_SLOT_5
} else {
(void) audioixp_write_ac97(statep,
/*
* Some codecs before ac97 2.2, such as YMF753 produced by
* Yamaha LSI, don't have the AC'97 registers indexed range
* from 0x2c to 0x34. So we assume this kind of codec
* supports fixed 48k sample rate.
*/
(void) audioixp_read_ac97(statep,
if (val != sample_rate) {
ATRACE_32("audioixp_ad_set_format() bad"
" in SR", sample_rate);
"!audioixp_ad_set_format() bad in"
" SR %d\n", sample_rate);
return (AUDIO_FAILURE);
}
}
}
done:
ATRACE_32("audioixp_ad_set_format() returning success", 0);
return (AUDIO_SUCCESS);
} /* audioixp_ad_set_format() */
/*
* audioixp_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_SUCCESS;
goto done;
}
goto done;
}
if (rc == AUDIO_SUCCESS) {
}
done:
return (rc);
} /* audioixp_ad_start_play() */
/*
* audioixp_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
{
return;
}
} /* audioixp_ad_pause_play() */
/*
* audioixp_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
{
} /* audioixp_ad_stop_play() */
/*
* audioixp_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;
return (AUDIO_SUCCESS);
}
if (rc == AUDIO_SUCCESS) {
}
return (rc);
} /* audioixp_ad_start_record() */
/*
* audioixp_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
{
} /* audioixp_ad_stop_record() */
/* *********************** Local Routines *************************** */
/*
* audioixp_init_state()
*
* Description:
* This routine initializes the audio driver's state structure
*
* CAUTION: This routine cannot allocate resources, unless it frees
* them before returning for an error. Also, error_destroy:
* in audioixp_attach() would need to be fixed as well.
*
* Arguments:
* audioixp_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 cdrom;
int mode;
"cdrom", 0);
/* get the mode from the .conf file */
"mixer-mode", 1)) {
} else {
}
if (pints > IXP_MAX_INTS) {
ATRACE_32("audioixp_init_state() "
"play interrupt rate too high, resetting", pints);
"audioixp_init_state() "
"play interrupt rate set too high, %d, resetting to %d",
} else if (pints < IXP_MIN_INTS) {
ATRACE_32("audioixp_init_state() "
"play interrupt rate too low, resetting", pints);
"audioixp_init_state() "
"play interrupt rate set too low, %d, resetting to %d",
}
if (rints > IXP_MAX_INTS) {
ATRACE_32("audioixp_init_state() "
"record interrupt rate too high, resetting", rints);
"audioixp_init_state() "
"record interrupt rate set too high, %d, resetting to "
"%d",
} else if (rints < IXP_MIN_INTS) {
ATRACE_32("audioixp_init_state() "
"record interrupt rate too low, resetting", rints);
"audioixp_init_state() "
"record interrupt rate set too low, %d, resetting to "
"%d",
}
/* fill in the device default state */
if (cdrom) {
}
/*
* fill in the ad_info structure
*/
/* play capabilities */
/* record capabilities */
DDI_SUCCESS) {
"!audioixp_init_state() cannot get iblock cookie");
return (AUDIO_FAILURE);
}
/* fill in device info strings */
return (AUDIO_SUCCESS);
} /* audioixp_init_state() */
/*
* audioixp_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
* audioixp_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS Registers successfully mapped
* AUDIO_FAILURE Registers not successfully mapped
*/
static int
{
statep->ixp_res_flags = 0;
/* map PCI config space */
DDI_FAILURE) {
"!audioixp_map_regs() configuration "
"memory mapping failed");
goto error;
}
/* map audio mixer register */
"!audioixp_map_regs() audio mixer "
"memory mapping failed");
goto error;
}
/*
* now, from here we allocate DMA memory for buffer descriptor list.
* we allocate adjacent DMA memory for all DMA engines.
*/
"!audioixp_map_regs() ddi_dma_alloc_handle(bdlist)"
" failed");
goto error;
}
"!audioixp_map_regs() ddi_dma_mem_alloc(bdlist) "
"failed");
goto error;
}
&count) != DDI_DMA_MAPPED) {
"!audioixp_map_regs() addr_bind_handle failed");
goto error;
}
/*
* there are some bugs in the DDI framework and it is possible to
* get multiple cookies
*/
if (count != 1) {
"!audioixp_map_regs() addr_bind_handle failed,"
" cookies > 1");
goto error;
}
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audioixp_map_regs() */
/*
* audioixp_unmap_regs()
*
* Description:
* This routine unbinds the play and record DMA handles, frees
* the DMA buffers and then unmaps control registers.
*
* Arguments:
* audioixp_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
}
}
}
}
}
} /* audioixp_unmap_regs() */
/*
* audioixp_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:
* audioixp_state_t *state The device's state structure
* int which Which sample buffer, PCM in or PCM out
* IXP_DMA_PCM_IN ---PCM in DMA engine
* IXP_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;
if (which == IXP_DMA_PCM_OUT) {
} else {
}
for (i = 0; i < 2; i++) {
DDI_SUCCESS) {
goto error;
}
goto error;
}
goto error;
}
/*
* there some bugs in the DDI framework and it is possible to
* get multiple cookies
*/
if (count != 1) {
goto error;
}
}
return (AUDIO_SUCCESS);
if (i != 0) {
}
return (AUDIO_FAILURE);
} /* audioixp_alloc_sample_buf() */
/*
* audioixp_free_sample_buf()
*
* Description:
* This routine frees the DMA buffers of the sample buffer. The DMA
* buffers were allocated by calling audioixp_alloc_sample_buf().
*
* Arguments:
* audioixp_state_t *state The device's state structure
* audioixp_sample_buf_t *buf The sample buffer structure
*
* Returns:
* void
*/
static void
{
int i;
for (i = 0; i < 2; i++) {
chunk->acc_handle = 0;
}
} /* audioixp_free_sample_buf() */
/*
* audioixp_setup_bdl()
*
* Description:
* This routine setup the buf descriptor list.
*
* Arguments:
* audioixp_state_t *state The device's state structure
*
* Returns:
* void
*/
{
int i;
/* setup playback bdlist */
for (i = 0; i < IXP_BD_NUMS; i ++) {
->pcm_out[i]);
.addr_phy;
}
/* setup record bdlist */
for (i = 0; i < IXP_BD_NUMS; i ++) {
->pcm_in[i]);
.addr_phy;
}
} /* audioixp_setup_bdl() */
/*
* audioixp_start_dma()
*
* Description:
* This routine is used to put each DMA engine into working state.
*
* Arguments:
* audioixp_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
if (dir == AUDIO_PLAY) {
} else {
}
} /* audioixp_start_dma() */
/*
* audioixp_stop_dma()
*
* Description:
* This routine is used to put each DMA engine into the quiet state.
*
* Arguments:
* audioixp_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
if (dir == AUDIO_PLAY) {
0);
} else {
0);
}
} /* audioixp_stop_dma() */
/*
* audioixp_codec_ready ()
*
* Description:
* This routine checks the state of codecs. This routine is called by
* chip_init before interrupt is enabled. It enables interrupt first,
* then waits a moment for interrupt handler to set the flag according
* to the hardware configuration. Then it checks the flag to confirm
* that primary codec is ready. The original value of interrupt enable
* register is restored.
*
* Arguments:
* audioixp_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS codec is ready
* AUDIO_FAILURE codec is not ready
*/
static int
{
drv_usecwait(1000);
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
}
/*
* audioixp_codec_sync()
*
* Description:
* Serialize access to the AC97 audio mixer registers.
*
* Arguments:
* audioixp_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;
for (i = 0; i < 300; i++) {
if (!(cmd & IXP_AUDIO_OUT_PHY_EN)) {
return (AUDIO_SUCCESS);
}
drv_usecwait(10);
}
return (AUDIO_FAILURE);
} /* audioixp_codec_sync() */
/*
* audioixp_read_ac97()
*
* Description:
* Get the specific AC97 Codec register. It also updates codec_shadow[]
* with the register value.
*
* Arguments:
* audioixp_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);
}
| ((reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT)
*data = 0xffff;
return (AUDIO_FAILURE);
}
for (i = 0; i < 300; i++) {
if (result & IXP_AUDIO_IN_PHY_READY) {
return (AUDIO_SUCCESS);
}
drv_usecwait(10);
}
*data = 0xffff;
return (AUDIO_FAILURE);
} /* audioixp_read_ac97() */
/*
* audioixp_write_ac97()
*
* Description:
* Set the specific AC97 Codec register.
*
* Arguments:
* audioixp_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
{
return (AUDIO_FAILURE);
}
| ((reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT)
| ((data << IXP_AUDIO_OUT_PHY_DATA_SHIFT)
return (AUDIO_SUCCESS);
} /* audioixp_write_ac97() */
/*
* audioixp_and_ac97()
*
* Description:
* Logically AND the value with the specified ac97 codec register
*
* Arguments:
* audioixp_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);
} /* audioixp_and_ac97() */
/*
* audioixp_or_ac97()
*
* Description:
* Logically OR the value with the specified ac97 codec register
*
* Arguments:
* audioixp_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);
} /* audioixp_or_ac97() */
/*
* audioixp_reset_ac97()
*
* Description:
* Reset AC97 Codec register.
*
* Arguments:
* audioixp_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS Reset the codec successfully
* AUDIO_FAILURE Failed to reset the codec
*/
static int
{
int i;
drv_usecwait(10);
/* register reset */
drv_usecwait(10);
0);
/* cold reset */
for (i = 0; i < 300; i++) {
if (cmd & IXP_AUDIO_CMD_AC_ACTIVE) {
return (AUDIO_SUCCESS);
}
(void) IXP_AM_GET32(IXP_AUDIO_CMD);
drv_usecwait(10);
drv_usecwait(10);
}
return (AUDIO_FAILURE);
} /* audioixp_reset_ac97() */
/*
* audioixp_chunk_processed()
*
* Description:
* This routine returns the count of chunk processed. It's called by
* audioixp_reclaim_play_buf and audioixp_reclaim_record_buf
* This routine compares the current hw_point value with its last value,
* there're two cases:
* case 1: new pointer is bigger than the last one and smaller than the
* last one + len of the last chunk, which mean the current
* chunk has not been finished, return 0.
* case 2: the hw_pointer return to the old value, which means both chunks
* have been processed,return 2
* case 3: one chunk is processed, return 1.
*
* Arguments:
* audioixp_state_t *statep The device's state structure
* int dir AUDIO_PLAY or AUDIO_RECORD, if
* direction is important
*
* Returns:
* count of chunk processed
*/
static int
{
int result;
int i;
int retry_count;
retry_count = 0;
while (++retry_count < 100) {
if (dir == AUDIO_PLAY) {
} else {
}
for (i = 0; i < IXP_BD_NUMS; i ++) {
if (dir == AUDIO_PLAY)
else
break;
}
if (i < IXP_BD_NUMS)
break;
}
/*
* cannot get valid hw_pointer, return 0 without updating
* last_hw_pointer
*/
if (retry_count == 100) {
for (i = 0; i < IXP_BD_NUMS; i ++) {
if (dir == AUDIO_PLAY)
else
}
return (0);
}
result = 0;
result = 2;
else /* case 3 */
result = 1;
return (result);
} /* audioixp_chunk_processed() */
/*
* audioixp_fill_play_buf()
*
* Description:
* This routine is called by audioixp_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.
*
* Arguments:
* audioixp_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;
if (!buf->io_started) {
/*
* ready to start PCM out engine
*/
}
return (AUDIO_SUCCESS);
}
/* if not an even number of samples we panic! */
if ((samples & 1) != 0) {
samples++;
}
(buf->io_started)) {
return (AUDIO_FAILURE);
}
if (rs <= 0) {
/*
* 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
*/
return (AUDIO_SUCCESS);
}
/* Finished playing, then stop it */
0);
/* clr the flags getting ready for next start */
return (AUDIO_FAILURE);
} else {
/*
* this time, we use one BD entry with empty
* buffer next time we shut down, if no sound
* again
*/
}
} else {
/* we got at least one sample */
}
/* put the samples into buffer descriptor list entry */
}
/* start PCM out engine */
if (!buf->io_started) {
}
return (AUDIO_SUCCESS);
} /* audioixp_fill_play_buf() */
/*
* audioixp_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:
* audioixp_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
return;
} /* audioixp_reclaim_play_buf() */
/*
* audioixp_prepare_record_buf()
*
* Description:
* This routine is called by audioixp_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:
* audioixp_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;
int i;
if (!buf->io_started) {
/* buffer base */
}
return (AUDIO_SUCCESS);
}
/* if not an even number of samples we panic! */
if ((samples & 1) != 0) {
samples++;
}
for (i = 0; i < 2; i ++) {
}
if (!buf->io_started) {
}
return (AUDIO_SUCCESS);
} /* audioixp_prepare_record_buf() */
/*
* audioixp_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:
* audioixp_state_t *statep The device's state structure
*
* Returns:
* void
*/
static void
{
int samples;
break;
}
}
} /* audioixp_reclaim_record_buf() */
/*
* audioixp_set_gain()
*
* Description:
*
* Arguments:
* audioixp_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
int channel)
{
int regidx;
int max_channel_gain;
if (gain > AUDIO_MAX_GAIN) {
} else if (gain < AUDIO_MIN_GAIN) {
}
if (dir == AUDIO_PLAY) {
} else {
}
if (channel == 0) { /* left channel */
} else { /* right channel */
tmp |= channel_gain;
}
} else {
(void) audioixp_read_ac97(statep,
if (channel == 0) { /* left channel */
tmp &= ~RGR_LEFT_MASK;
} else {
/* right channel */
tmp &= ~RGR_RIGHT_MASK;
}
(void) audioixp_write_ac97(statep,
}
return (AUDIO_SUCCESS);
} /* audioixp_set_gain() */
/*
* audioixp_set_port()
*
* Description:
*
* Arguments:
* audioixp_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_MICROPHONE input from microphone
* AUDIO_LINE_IN input from line in
* 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
{
tmp = 0;
if (port == IXP_PORT_UNMUTE) {
}
if (port & AUDIO_SPEAKER) {
(void) audioixp_and_ac97(statep,
tmp |= AUDIO_SPEAKER;
} else {
(void) audioixp_or_ac97(statep,
}
if (port & AUDIO_LINE_OUT) {
(void) audioixp_and_ac97(statep,
} else {
(void) audioixp_and_ac97(statep,
}
tmp |= AUDIO_LINE_OUT;
} else {
(void) audioixp_or_ac97(statep,
} else {
(void) audioixp_or_ac97(statep,
}
}
if (port & AUDIO_HEADPHONE) {
(void) audioixp_and_ac97(statep,
tmp |= AUDIO_HEADPHONE;
} else {
(void) audioixp_or_ac97(statep,
}
return (AUDIO_FAILURE);
}
} else { /* input port */
switch (port) {
case AUDIO_NONE:
/* set to an unused input */
/* mute the master record input */
(void) audioixp_or_ac97(statep,
if (statep->ixp_monitor_gain) {
if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
} else if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
} else if (statep->ixp_input_port ==
AUDIO_CD) {
(void) audioixp_or_ac97(statep,
}
}
break;
case AUDIO_MICROPHONE:
/* set to the mic input */
if (statep->ixp_monitor_gain) {
if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
} else if (statep->ixp_input_port ==
AUDIO_CD) {
(void) audioixp_or_ac97(statep,
}
(void) audioixp_write_ac97(statep,
}
break;
case AUDIO_LINE_IN:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (statep->ixp_monitor_gain) {
if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
} else if (statep->ixp_input_port ==
AUDIO_CD) {
(void) audioixp_or_ac97(statep,
}
(void) audioixp_write_ac97(statep,
}
break;
case AUDIO_CD:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (statep->ixp_monitor_gain) {
if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
} else if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
}
(void) audioixp_write_ac97(statep,
}
break;
case AUDIO_CODEC_LOOPB_IN:
/* set to the loopback input */
if (statep->ixp_monitor_gain) {
if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
} else if (statep->ixp_input_port ==
(void) audioixp_or_ac97(statep,
} else if (statep->ixp_input_port ==
AUDIO_CD) {
(void) audioixp_or_ac97(statep,
}
}
break;
default:
return (AUDIO_FAILURE);
}
/* select the input */
(void) audioixp_write_ac97(statep,
if ((port != AUDIO_NONE) &&
AC97_RECORD_GAIN_REGISTER)] & RGR_MUTE)) {
(void) audioixp_and_ac97(statep,
}
}
ATRACE_32("audioixp_set_port() returning", 0);
return (AUDIO_SUCCESS);
} /* audioixp_set_port() */
/*
* audioixp_set_monitor_gain()
*
* Description:
* Set the monitor gain.
*
* Arguments:
* audioixp_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->ixp_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:
/*
* MIC input has 20dB boost, we just preserve it
*/
tmp_short |=
(void) audioixp_write_ac97(statep,
break;
case AUDIO_LINE_IN:
(void) audioixp_write_ac97(statep,
break;
case AUDIO_CD:
(void) audioixp_write_ac97(statep,
break;
case AUDIO_CODEC_LOOPB_IN:
/* we already are getting the loopback, so done */
rc = AUDIO_SUCCESS;
goto done;
default:
/* this should never happen! */
ATRACE("audioixp_ad_set_config() monitor gain bad device",
NULL);
rc = AUDIO_FAILURE;
goto done;
}
if (gain == 0) {
statep->ixp_monitor_gain = 0;
} else {
}
done:
return (rc);
} /* audioixp_set_monitor_gain() */
/*
* audioixp_chip_init()
*
* Description:
* This routine initializes ATI IXP 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
*
* Arguments:
* audioixp_state_t *state The device's state structure
* int restore If IXP_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;
/*
* put the audio controller into quiet state, everything off
*/
/* AC97 reset */
"!audioixp_chip_init() AC97 codec reset failed");
return (AUDIO_FAILURE);
}
"!audioixp_chip_init() AC97 codec not ready");
return (AUDIO_FAILURE);
}
if (restore == IXP_INIT_NO_RESTORE) {
for (i = 0; i <= IXP_LAST_AC_REG; i += 2) {
(void) audioixp_read_ac97(statep, i,
&(shadow[IXP_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 */
/* 08h - set master tone control to no modification */
/*
* 0ah - turn pc beep mute off, 0dB
*
* AC'97 Spec does define the optional PC Beep support, that is,
* the BIOS (dependent on hardware design) can use the audio
* hardware for the beep, especially on some laptops, in order
* to save cost. So we have to turn the pc_beep mute off, that
* is, enable the PC Beep support.
*/
/* 0ch - set phone input, mute, 0dB attenuation */
/* 0eh - set mic input, mute, 0dB attenuation */
/* 10h - set line input, mute, 0dB attenuation */
/* 12h - set cd input, mute, 0dB attenuation */
/* 14h - set video 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 */
/* 1eh - set record mic gain to 0dB and not muted */
/* 20h - set GP register, mic 1, everything else off */
/* 22h - set 3D control to NULL */
/*
* The rest we ignore, most are reserved.
*/
}
if (restore == IXP_INIT_RESTORE) {
/* Restore from saved values */
}
/* Now we set the AC97 codec registers to the saved values */
(void) audioixp_write_ac97(statep, i,
shadow[IXP_CODEC_REG(i)]);
if (tmp & RR_HEADPHONE_SUPPORT) {
}
/*
* line-out jack. So far we haven't found which vendors don't
* do that. So we assume that all vendors swap the surr-out
* and the line-out outputs. So we need swap the two outputs.
* But we still internally process the "ad198x-swap-output"
* property. If someday some vendors do not swap the outputs,
* we would set "ad198x-swap-output = 0" in the
* /kernel/drv/audioixp.conf file, and unload and reload the
* audioixp driver (or reboot).
*/
if (vid1 == AD1980_VID1 &&
&tmp);
(void) audioixp_write_ac97(statep,
}
}
/*
* check if the codec implements 6 bit volume register,
* but the ALC202 does not strictly obey the AC'97 Spec
* and it only supports 5 bit volume register, so we
* skip the check for it as a workaround.
*/
(void) audioixp_write_ac97(statep,
(void) audioixp_read_ac97(statep,
}
}
/* resume the master volume to the max */
MVR_MUTE);
/*
* if the codec chip does not support variable sample rate,
* we set the sample rate to 48K
*/
&xid);
"!%s%d: xid=0x%04x, vid1=0x%04x, vid2=0x%04x",
} else { /* variable sample rate supported */
/* set variable rate mode */
(void) audioixp_write_ac97(statep,
/* check the sample rates supported */
for (i = 0, j = 0; audioixp_compat_srs[i] != 0; i++) {
(void) audioixp_write_ac97(statep,
audioixp_compat_srs[i]);
(void) audioixp_read_ac97(statep,
if (sr == audioixp_compat_srs[i]) {
if (i != j) {
audioixp_compat_srs[j] =
}
j++;
}
}
if (j < 1) {
"!No standard sample rate is supported");
return (AUDIO_FAILURE);
}
audioixp_compat_srs[j] = 0;
/*
* if the configuration doesn't support 8K sample rate,
* we modify the default value to the first.
*/
for (i = 0; audioixp_compat_srs[i] != 0; i++) {
if (audioixp_compat_srs[i] == IXP_SAMPR8000) {
break;
}
}
if (audioixp_compat_srs[i] != IXP_SAMPR8000) {
}
}
/* enable interrupts */
return (AUDIO_SUCCESS);
} /* audioixp_chip_init() */
/*
* audioixp_chip_fini()
*
* Description:
* This routine disables hardware interrupts.
*
* Arguments:
* audioixp_state_t *state The device's state structure
*
* Returns:
* void
*/
{
} /* audioixp_chip_fini() */