audio810.c revision 2df1fe9ca32bb227b9158c67f5c00b54c20b10fd
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* audio810 Audio Driver
*
* The driver is primarily targeted at providing audio support for
* the W1100z and W2100z systems, which use the AMD 8111 audio core
* and the Realtek ALC 655 codec. The ALC 655 chip supports only
* fixed 48k sample rate. However, the audio core of AMD 8111 is
* completely compatible to the Intel ICHx chips (Intel 8x0 chipsets),
* so the driver can work for the ICHx. In order to support more
* chipsets, the driver supports variable sample rates, rather than
* fixed 48k, but it does not support the rates below 8k because some
* codec chips do not support the sample rates in that scope. Therefore
* the option of loading the driver in compat mode through the .conf
* file on the W1100z and W2100z systems is not supported and the
* "mixer-mode" property has been removed from that file.
*
* This driver uses the mixer Audio Personality Module to implement
* audio(7I) and mixer(7I) semantics. Both play and record are single
* streaming.
*
* The AMD 8111 audio core, as an AC'97 controller, 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, i810_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. 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 only 2 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 audio810_chip_init()
*
* A workaround for the AD1980 and AD1985 codec:
* Most vendors connect the surr-out of the codecs to the 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/audio810.conf file, and unload and reload the audio810
* driver (or reboot).
*
* 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 audio810_ad_set_config(audiohdl_t, int, int, int, int, int);
static int audio810_ad_set_format(audiohdl_t, int, int, int, int, int, int);
static int audio810_ad_start_play(audiohdl_t, int);
static void audio810_ad_pause_play(audiohdl_t, int);
static void audio810_ad_stop_play(audiohdl_t, int);
static int audio810_ad_start_record(audiohdl_t, int);
static void audio810_ad_stop_record(audiohdl_t, int);
/*
* interrupt handler
*/
/*
* Local Routine Prototypes
*/
static void audio810_set_busy(audio810_state_t *);
static void audio810_set_idle(audio810_state_t *);
static int audio810_codec_sync(audio810_state_t *);
static int audio810_reset_ac97(audio810_state_t *);
static void audio810_unmap_regs(audio810_state_t *);
static int audio810_alloc_sample_buf(audio810_state_t *, int, int);
static void audio810_stop_dma(audio810_state_t *);
static int audio810_chip_init(audio810_state_t *, int);
static int audio810_fill_play_buf(audio810_state_t *);
static int audio810_prepare_record_buf(audio810_state_t *);
static void audio810_reclaim_play_buf(audio810_state_t *);
static void audio810_reclaim_record_buf(audio810_state_t *);
static int audio810_set_gain(audio810_state_t *, int, int, int);
static int audio810_set_port(audio810_state_t *, int, int);
static int audio810_set_monitor_gain(audio810_state_t *, int);
/*
* Global variables, but used only by this file.
*/
/* anchor for soft state structures */
static void *audio810_statep;
/* driver name, so we don't have to call ddi_driver_name() or hard code strs */
static char *audio810_name = I810_NAME;
/*
* STREAMS structures
*/
/* STREAMS driver id and limit value struct */
static struct module_info audio810_modinfo = {
I810_IDNUM, /* module ID number */
I810_NAME, /* module name */
I810_MINPACKET, /* minimum packet size */
I810_MAXPACKET, /* maximum packet size */
I810_HIWATER, /* high water mark */
I810_LOWATER, /* low water mark */
};
/* STREAMS queue processing procedures structures */
/* read queue */
static struct qinit audio810_rqueue = {
audio_sup_rput, /* put procedure */
audio_sup_rsvc, /* service procedure */
audio_sup_open, /* open procedure */
audio_sup_close, /* close procedure */
NULL, /* unused */
&audio810_modinfo, /* module parameters */
NULL /* module statistics */
};
/* write queue */
static struct qinit audio810_wqueue = {
audio_sup_wput, /* write procedure */
audio_sup_wsvc, /* service procedure */
NULL, /* open procedure */
NULL, /* close procedure */
NULL, /* unused */
&audio810_modinfo, /* module parameters */
NULL /* module statistics */
};
/* STREAMS entity declaration structure */
static struct streamtab audio810_str_info = {
&audio810_rqueue, /* read queue */
&audio810_wqueue, /* write queue */
NULL, /* mux lower read queue */
NULL, /* mux lower write queue */
};
/*
* DDI Structures
*/
/* Entry points structure */
static struct cb_ops audio810_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 */
&audio810_str_info, /* cb_str */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev, /* cb_awrite */
};
/* Device operations structure */
static struct dev_ops audio810_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
audio810_getinfo, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
audio810_attach, /* devo_attach */
audio810_detach, /* devo_detach */
nodev, /* devo_reset */
&audio810_cb_ops, /* devi_cb_ops */
NULL, /* devo_bus_ops */
NULL /* devo_power */
};
/* Linkage structure for loadable drivers */
static struct modldrv audio810_modldrv = {
&mod_driverops, /* drv_modops */
&audio810_dev_ops, /* drv_dev_ops */
};
/* Module linkage structure */
static struct modlinkage audio810_modlinkage = {
MODREV_1, /* ml_rev */
(void *)&audio810_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
static uint_t audio810_mixer_srs[] = {
};
static uint_t audio810_min_compat_srs[] = {
};
static uint_t audio810_compat_srs [] = {
0
};
static am_ad_sample_rates_t audio810_mixer_sample_rates = {
};
static am_ad_sample_rates_t audio810_compat_sample_rates = {
};
/* Some codec, such as the ALC 655, only support 48K sample rate */
};
/* now, only support stereo */
static uint_t audio810_channels[] = {
0
};
static am_ad_cap_comb_t audio810_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 audio810_entry = {
NULL, /* ad_setup() */
NULL, /* ad_teardown() */
audio810_ad_set_config, /* ad_set_config() */
audio810_ad_set_format, /* ad_set_format() */
audio810_ad_start_play, /* ad_start_play() */
audio810_ad_pause_play, /* ad_pause_play() */
audio810_ad_stop_play, /* ad_stop_play() */
audio810_ad_start_record, /* ad_start_record() */
audio810_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 (audio810_state_t), 1)) != 0) {
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 ********************************* */
/*
* audio810_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);
} /* audio810_getinfo() */
/*
* audio810_attach()
*
* Description:
* Attach an instance of the audio810 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:
NULL) {
"!attach() DDI_RESUME get soft state failed");
return (DDI_FAILURE);
}
/* Restore the audio810 chip's state */
"!attach() DDI_RESUME failed to init chip");
return (DDI_FAILURE);
}
/* Resume playing and recording, if required */
"!attach() DDI_RESUME audio restart failed");
}
return (DDI_SUCCESS);
default:
"!%s%d: 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: attach() unsupported high level interrupt",
return (DDI_FAILURE);
}
/* allocate the soft state structure */
"!%s%d: attach() soft state allocate failed",
return (DDI_FAILURE);
}
"!%s%d: attach() soft state failed",
goto error_state;
}
"!%s%d: attach() audio_sup_register() failed",
goto error_state;
}
/* save private state */
"!attach() init state structure failed");
goto error_audiosup;
}
/* map in the registers, allocate DMA buffers, etc. */
"!attach() couldn't map registers");
goto error_destroy;
}
/* set PCI command register */
"!attach() couldn't allocate sample buffers");
goto error_unmap;
}
/* initialize audio controller and AC97 codec */
"!attach() failed to init chip");
goto error_dealloc;
}
/* call the mixer attach() routine */
"!attach() am_attach() failed");
goto error_dealloc;
}
/* set up kernel statistics */
KSTAT_FLAG_PERSISTENT)) != NULL) {
}
/* set up the interrupt handler */
DDI_SUCCESS) {
"!attach() bad interrupt specification ");
goto error_kstat;
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
} /* audio810_attach() */
/*
* audio810_detach()
*
* Description:
* Detach an instance of the audio810 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 failed",
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
/* wait for current operations to complete */
while (statep->i810_busy_cnt != 0)
/* stop DMA engines */
"!detach() DDI_SUSPEND audio save failed");
}
return (DDI_SUCCESS);
default:
"!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);
} /* audio810_detach */
/*
* audio810_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.
* (AMD 8111 hypertransport I/O hub data sheet. 3.8.3 page 71)
*
* 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
{
return (DDI_INTR_UNCLAIMED);
}
/* check if device is interrupting */
if ((gsr & I810_GSR_USE_INTR) == 0) {
return (DDI_INTR_UNCLAIMED);
}
/* PCM in interrupt */
if (gsr & I810_GSR_INTR_PIN) {
(void) audio810_prepare_record_buf(statep);
}
}
/* PCM out interrupt */
if (gsr & I810_GSR_INTR_POUT) {
(void) audio810_fill_play_buf(statep);
}
}
/* update the kernel interrupt statistics */
}
return (DDI_INTR_CLAIMED);
} /* audio810_intr() */
/*
* audio810_set_busy()
*
* Description:
* This routine is called whenever a routine needs to guarantee
* that it will not be suspended. It will also block any routine
* while a suspend is going on.
*
* CAUTION: This routine cannot be called by routines that will
* block. Otherwise DDI_SUSPEND will be blocked for a
* long time. And that is the wrong thing to do.
*
* Arguments:
* audio810_state_t *statep The device's state structure
*
* Returns:
* void
*/
static void
{
/* get the lock so we are safe */
/* block if we are suspended */
}
/*
* Okay, we aren't suspended, so mark as busy.
* This will keep us from being suspended when we release the lock.
*/
statep->i810_busy_cnt++;
} /* audio810_set_busy() */
/*
* audio810_set_idle()
*
* Description:
* This routine reduces the busy count. It then does a cv_broadcast()
* if the count is 0 so a waiting DDI_SUSPEND will continue forward.
*
* Arguments:
* audio810_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
/* get the lock so we are safe */
/* decrement the busy count */
statep->i810_busy_cnt--;
/* if no longer busy, then we wake up a waiting SUSPEND */
if (statep->i810_busy_cnt == 0) {
}
/* we're done, so unlock */
} /* audio810_set_idle() */
/* *********************** Mixer Entry Point Routines ******************* */
/*
* audio810_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) audio810_or_ac97(statep,
(void) audio810_or_ac97(statep,
(void) audio810_or_ac97(statep,
} else { /* not muted */
/* by setting the port we unmute only active ports */
(void) audio810_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) audio810_or_ac97(statep,
} else { /* disable */
(void) audio810_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("i810_ad_set_config() unsupported command",
command);
break;
}
return (rc);
} /* audio810_ad_set_config() */
/*
* audio810_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 (AUDIO_FAILURE);
}
/* get the soft state structure */
/* codec doesn't support variable sample rate */
if (sample_rate != I810_SAMPR48000) {
"!ad_set_format() bad sample rate %d\n",
goto done;
}
} else {
switch (sample_rate) {
case I810_SAMPR8000: break;
case I810_SAMPR9600: break;
case I810_SAMPR11025: break;
case I810_SAMPR16000: break;
case I810_SAMPR18900: break;
case I810_SAMPR22050: break;
case I810_SAMPR27420: break;
case I810_SAMPR32000: break;
case I810_SAMPR33075: break;
case I810_SAMPR37800: break;
case I810_SAMPR44100: break;
case I810_SAMPR48000: break;
default:
goto done;
}
}
if (dir == AUDIO_PLAY) {
(void) audio810_write_ac97(statep,
(void) audio810_write_ac97(statep,
(void) audio810_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) audio810_read_ac97(statep,
if (val != sample_rate) {
ATRACE_32("ad_set_format() bad out SR",
"!set_format() bad output sample rate %d",
goto done;
}
}
} else {
(void) audio810_write_ac97(statep,
(void) audio810_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) audio810_read_ac97(statep,
if (val != sample_rate) {
ATRACE_32("ad_set_format() bad input SR",
"!set_format() bad input sample rate %d",
goto done;
}
}
}
rc = AUDIO_SUCCESS;
done:
return (rc);
} /* audio810_ad_set_format() */
/*
* audio810_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;
cr |= I810_BM_CR_RUN;
goto done;
}
goto done;
}
if (rc == AUDIO_FAILURE) {
} else {
}
done:
return (rc);
} /* audio810_ad_start_play() */
/*
* audio810_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
{
goto done;
cr &= ~I810_BM_CR_RUN;
done:
} /* audio810_ad_pause_play() */
/*
* audio810_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
{
/* pause bus master */
/* reset registers */
} /* audio810_ad_stop_play() */
/*
* audio810_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_SUCCESS;
goto done;
if (rc == AUDIO_SUCCESS) {
}
done:
return (rc);
} /* audio810_ad_start_record() */
/*
* audio810_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
{
/* pause bus master */
/* reset registers */
} /* audio810_ad_stop_record() */
/* *********************** Local Routines *************************** */
/*
* audio810_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 audio810_attach() would need to be fixed as well.
*
* Arguments:
* audio810_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 > I810_MAX_INTS) {
ATRACE_32("i810_init_state() "
"play interrupt rate too high, resetting", pints);
"init_state() "
"play interrupt rate set too high, %d, resetting to %d",
} else if (pints < I810_MIN_INTS) {
ATRACE_32("i810_init_state() "
"play interrupt rate too low, resetting", pints);
"init_state() "
"play interrupt rate set too low, %d, resetting to %d",
}
if (rints > I810_MAX_INTS) {
ATRACE_32("i810_init_state() "
"record interrupt rate too high, resetting", rints);
"init_state() "
"record interrupt rate set too high, %d, resetting to %d",
} else if (rints < I810_MIN_INTS) {
ATRACE_32("i810_init_state() "
"record interrupt rate too low, resetting", rints);
"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) {
"!init_state() cannot get iblock cookie");
return (AUDIO_FAILURE);
}
/* fill in device info strings */
return (AUDIO_SUCCESS);
} /* audio810_init_state */
/*
* audio810_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
{
int *regs_list;
int i;
int pciBar1 = 0;
int pciBar2 = 0;
int pciBar3 = 0;
int pciBar4 = 0;
statep->i810_res_flags = 0;
/* map PCI config space */
DDI_FAILURE) {
"!map_regs() configuration memory mapping failed");
goto error;
}
/* check the "reg" property to get the length of memory-mapped I/O */
"!map_regs() inquire regs property failed");
goto error;
}
/*
* and 0x14 BAR separately for native audio mixer BAR and native bus
* mastering BAR. More advanced hardwares, such as Intel ICH4 and ICH5,
* support PCI memory BAR, via PCI 0x18 and 0x1C BAR, that allows for
* higher performance access to the controller register. All features
* can be accessed via this BAR making the I/O BAR (PCI 0x10 and 0x14
* BAR) capabilities obsolete. However, these controller maintain the
* I/O BAR capability to allow for the reuse of legacy code maintaining
* backward compatibility. The I/O BAR is disabled unless system BIOS
* enables the simultaneous backward compatible capability on the 0x41
* register.
*
* When I/O BAR is enabled, the value of "reg" property should be like
* this,
* phys_hi phys_mid phys_lo size_hi size_lo
* --------------------------------------------------------
* 0000fd00 00000000 00000000 00000000 00000000
* 0100fd10 00000000 00000000 00000000 00000100
* 0100fd14 00000000 00000000 00000000 00000040
* 0200fd18 00000000 00000000 00000000 00000200
* 0200fd1c 00000000 00000000 00000000 00000100
*
* When I/O BAR is disabled, the "reg" property of the device node does
* not consist of the description for the I/O BAR. The following example
* illustrates the vaule of "reg" property,
*
* phys_hi phys_mid phys_lo size_hi size_lo
* --------------------------------------------------------
* 0000fd00 00000000 00000000 00000000 00000000
* 0200fd18 00000000 00000000 00000000 00000200
* 0200fd1c 00000000 00000000 00000000 00000100
*
* If the hardware has memory-mapped I/O access, first try to use
* this facility, otherwise we will try I/O access.
*/
case 0x10:
pciBar1 = i;
break;
case 0x14:
pciBar2 = i;
break;
case 0x18:
pciBar3 = i;
break;
case 0x1c:
pciBar4 = i;
break;
default: /* we don't care others */
break;
}
}
/* map audio mixer registers */
"!map_regs() memory am mapping failed, len=0x%08x",
goto error;
}
/* map bus master register */
"!map_regs() memory bm mapping failed, len=0x%08x",
goto error;
}
/* map audio mixer registers */
"!map_regs() I/O am mapping failed, len=0x%08x",
goto error;
}
/* map bus master register */
"!map_regs() I/O bm mapping failed, len=: 0x%08x",
goto error;
}
} else {
"!map_reg() pci BAR error");
goto error;
}
/*
* now, from here we allocate DMA memory for buffer descriptor list.
* we allocate adjacent DMA memory for all DMA engines.
*/
"!map_regs() ddi_dma_alloc_handle(bdlist) failed ");
goto error;
}
/*
* we allocate all buffer descriptors lists in continuous dma memory.
*/
"!map_regs() ddi_dma_mem_alloc(bdlist) failed");
goto error;
}
&count) != DDI_DMA_MAPPED) {
"!map_regs() addr_bind_handle failed");
goto error;
}
/*
* there some bugs in the DDI framework and it is possible to
* get multiple cookies
*/
if (count != 1) {
"!map_regs() addr_bind_handle failed, cookies > 1");
goto error;
}
sizeof (i810_bd_entry_t) * I810_BD_NUMS;
return (AUDIO_SUCCESS);
if (nregs > 0) {
}
return (AUDIO_FAILURE);
} /* audio810_map_regs() */
/*
* audio810_unmap_regs()
*
* Description:
* This routine unbinds the play and record DMA handles, frees
* the DMA buffers and the unmaps control registers.
*
* Arguments:
* audio810_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
}
}
}
}
}
}
} /* audio810_unmap_regs() */
/*
* audio810_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:
* audio810_state_t *state The device's state structure
* int which Which sample buffer, PCM in or PCM out
* I810_DMA_PCM_IN ---PCM in DMA engine
* I810_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 == I810_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);
} /* audio810_alloc_sample_buf() */
/*
* audio810_free_sample_buf()
*
* Description:
* This routine frees the DMA buffers of the sample buffer. The DMA
* buffers were allocated by calling audio810_alloc_sample_buf().
*
* Arguments:
* audio810_state_t *state The device's state structure
* i810_sample_buf_t *buf The sample buffer structure
*
* Returns:
* void
*/
static void
{
int i;
for (i = 0; i < 2; i++) {
chunk->acc_handle = 0;
}
} /* audio810_free_sample_buf() */
/*
* audio810_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:
* audio810_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
}
}
} /* audio810_reclaim_play_buf() */
/*
* audio810_chip_init()
*
* Description:
* This routine initializes the AMD 8111 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:
* audio810_state_t *state The device's state structure
* int restore If I810_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 loop;
int i;
int j;
/*
* SADA only supports stereo, so we set the channel bits
* to "00" to select 2 channels.
*/
/*
* Datasheet(ICH5, document number of Intel: 252751-001):
* 3.6.5.5(page 37)
* if reset bit(bit1) is "0", driver must set it
* to "1" to de-assert the AC_RESET# signal in AC
* link, thus completing a cold reset. But if the
* bit is "1", then a warm reset is required.
*/
/* according AC'97 spec, wait for codec reset */
if ((gcr & I810_GCR_WARM_RST) == 0) {
break;
}
}
/* codec reset failed */
if (loop < 0) {
"!Failed to reset codec");
return (AUDIO_FAILURE);
}
/*
* Wait for codec ready. The hardware can provide the state of
* codec ready bit on SDATA_IN[0], SDATA_IN[1] or SDATA_IN[2]
*/
if ((gsr & codec_ready) != 0) {
break;
}
}
if (loop < 0) {
"!No codec ready signal received");
return (AUDIO_FAILURE);
}
/*
* put the audio controller into quiet state, everything off
*/
/* AC97 register reset */
return (AUDIO_FAILURE);
}
if (restore == I810_INIT_NO_RESTORE) {
for (i = 0; i < I810_LAST_AC_REG; i += 2) {
(void) audio810_read_ac97(statep, i,
&(shadow[I810_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 - open pc beep, 0dB */
/* 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 */
/*
* 26h - set EAPD to 1 for devices with ac97-invert-amp
* property.
*
* According to AC'97 spec, EAPD (PR7) independently controls
* an output pin that manages an optional external audio
* amplifier. AC'97 compliance requires the implementation of
* a dedicated output pin for external audio amplifier control.
* The pin is controlled via the ��EAPD��(External Amplifier
* (formerly PR7). EAPD = 0 places a 0 on the output pin,
* enabling an external audio amplifier, EAPD = 1 shuts it
* down. Audio amplifier devices that operate with reverse
* polarity may require an external inverter. By default,
* EAPD = 0 is to enable external audio amplifier, but for
* some Sony Vaio laptops, we need to revert polarity to
* enable external amplifier.
*/
case -1:
/* not attempt to flip EAPD */
break;
case 0:
/* set EAPD to 0 */
break;
case 1:
/* set EAPD to 1 */
break;
default:
/* invalid */
"!Invalid value for ac97-invert-amp property");
break;
}
/*
* The rest we ignore, most are reserved.
*/
}
if (restore == I810_INIT_RESTORE) {
/* Restore from saved values */
}
/* Now we set the AC97 codec registers to the saved values */
(void) audio810_write_ac97(statep, i,
shadow[I810_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/audio810.conf file, and unload and reload the
* audio810 driver (or reboot).
*/
if (vid1 == AD1980_VID1 &&
&tmp);
}
}
/* check if the codec implements 6 bit volume register */
}
/* 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
*/
"!%s%d: xid=0x%04x, vid1=0x%04x, vid2=0x%04x",
} else { /* variable sample rate supported */
/* set variable rate mode */
(void) audio810_write_ac97(statep,
/* check the sample rates supported */
for (i = 0, j = 0; audio810_compat_srs[i] != 0; i++) {
(void) audio810_write_ac97(statep,
audio810_compat_srs[i]);
(void) audio810_read_ac97(statep,
if (sr == audio810_compat_srs[i]) {
if (i != j) {
audio810_compat_srs[j] =
}
j++;
}
}
if (j < 1) {
"!No standard sample rate is supported");
return (AUDIO_FAILURE);
}
audio810_compat_srs[j] = 0;
/*
* if the configuration doesn't support 8K sample rate,
* we modify the default value to the first.
*/
if (audio810_compat_srs[0] != I810_SAMPR8000) {
}
}
return (AUDIO_SUCCESS);
} /* audio810_chip_init() */
/*
* audio810_stop_dma()
*
* Description:
* This routine is used to put each DMA engine into the quiet state.
*
* Arguments:
* audio810_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
/* pause bus master (needed for the following reset register) */
/* and then reset the bus master registers for a three DMA engines */
/*
* XXXX Not sure what these declarations are for, but I brought them from
* the PM gate.
*/
} /* audio810_stop_dma() */
/*
* audio810_set_gain()
*
* Description:
*
* Arguments:
* audio810_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 regidx;
if (gain > AUDIO_MAX_GAIN) {
} else if (gain < AUDIO_MIN_GAIN) {
}
mask = 0x3f;
else
mask = 0x1f;
if (dir == AUDIO_PLAY) {
} else {
}
if (channel == 0) { /* left channel */
} else { /* right channel */
tmp |= channel_gain;
}
} else {
(void) audio810_read_ac97(statep,
if (channel == 0) { /* left channel */
tmp &= ~RGR_LEFT_MASK;
} else {
/* right channel */
tmp &= ~RGR_RIGHT_MASK;
}
(void) audio810_write_ac97(statep,
}
return (AUDIO_SUCCESS);
} /* audio810_set_gain() */
/*
* audio810_set_port()
*
* Description:
*
* Arguments:
* audio810_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 == I810_PORT_UNMUTE) {
}
if (port & AUDIO_SPEAKER) {
(void) audio810_and_ac97(statep,
tmp |= AUDIO_SPEAKER;
} else {
(void) audio810_or_ac97(statep,
}
if (port & AUDIO_LINE_OUT) {
(void) audio810_and_ac97(statep,
} else {
(void) audio810_and_ac97(statep,
}
tmp |= AUDIO_LINE_OUT;
} else {
(void) audio810_or_ac97(statep,
} else {
(void) audio810_or_ac97(statep,
}
}
if (port & AUDIO_HEADPHONE) {
(void) audio810_and_ac97(statep,
tmp |= AUDIO_HEADPHONE;
} else {
(void) audio810_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) audio810_or_ac97(statep,
if (statep->i810_monitor_gain) {
if (statep->i810_input_port ==
(void) audio810_or_ac97(statep,
} else if (statep->i810_input_port ==
(void) audio810_or_ac97(statep,
} else if (statep->i810_input_port ==
AUDIO_CD) {
(void) audio810_or_ac97(statep,
}
}
break;
case AUDIO_MICROPHONE:
/* set to the mic input */
if (statep->i810_monitor_gain) {
(void) audio810_or_ac97(statep,
} else if (statep->i810_input_port ==
AUDIO_CD) {
(void) audio810_or_ac97(statep,
}
(void) audio810_write_ac97(statep,
}
break;
case AUDIO_LINE_IN:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (statep->i810_monitor_gain) {
if (statep->i810_input_port ==
(void) audio810_or_ac97(statep,
} else if (statep->i810_input_port ==
AUDIO_CD) {
(void) audio810_or_ac97(statep,
}
(void) audio810_write_ac97(statep,
}
break;
case AUDIO_CD:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (statep->i810_monitor_gain) {
if (statep->i810_input_port ==
(void) audio810_or_ac97(statep,
} else if (statep->i810_input_port ==
(void) audio810_or_ac97(statep,
}
(void) audio810_write_ac97(statep,
}
break;
case AUDIO_CODEC_LOOPB_IN:
/* set to the loopback input */
if (statep->i810_monitor_gain) {
(void) audio810_or_ac97(statep,
} else if (statep->i810_input_port ==
(void) audio810_or_ac97(statep,
} else if (statep->i810_input_port ==
AUDIO_CD) {
(void) audio810_or_ac97(statep,
}
}
break;
default:
return (AUDIO_FAILURE);
}
/* select the input */
(void) audio810_write_ac97(statep,
if ((port != AUDIO_NONE) &&
AC97_RECORD_GAIN_REGISTER)] & RGR_MUTE)) {
(void) audio810_and_ac97(statep,
}
}
ATRACE_32("810_set_port() returning", 0);
return (AUDIO_SUCCESS);
} /* audio810_set_port() */
/*
* audio810_set_monitor_gain()
*
* Description:
* Set the monitor gain.
*
* Arguments:
* audio810_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->i810_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
*/
(void) audio810_write_ac97(statep,
break;
case AUDIO_LINE_IN:
(void) audio810_write_ac97(statep,
break;
case AUDIO_CD:
(void) audio810_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! */
rc = AUDIO_FAILURE;
goto done;
}
if (gain == 0) {
statep->i810_monitor_gain = 0;
} else {
}
done:
return (rc);
} /* audio810_set_monitor_gain() */
/*
* audio810_codec_sync()
*
* Description:
* Serialize access to the AC97 audio mixer registers.
*
* Arguments:
* audio810_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 ((casr & 1) == 0) {
return (AUDIO_SUCCESS);
}
drv_usecwait(10);
}
return (AUDIO_FAILURE);
} /* audio810_codec_sync() */
/*
* audio810_and_ac97()
*
* Description:
* Logically AND the value with the specified ac97 codec register
*
* Arguments:
* audio810_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);
} /* audio810_and_ac97() */
/*
* audio810_or_ac97()
*
* Description:
* Logically OR the value with the specified ac97 codec register
*
* Arguments:
* audio810_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);
} /* audio810_or_ac97() */
/*
* audio810_write_ac97()
*
* Description:
* Set the specific AC97 Codec register.
*
* Arguments:
* audio810_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);
}
return (AUDIO_SUCCESS);
} /* audio810_write_ac97() */
/*
* audio810_read_ac97()
*
* Description:
* Get the specific AC97 Codec register. It also updates codec_shadow[]
* with the register value.
*
* Arguments:
* audio810_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
{
*data = 0xffff;
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio810_read_ac97() */
/*
* audio810_reset_ac97()
*
* Description:
* Reset AC97 Codec register.
*
* Arguments:
* audio810_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS Reset the codec successfully
* AUDIO_FAILURE Failed to reset the codec
*/
static int
{
if (audio810_read_ac97(statep,
return (AUDIO_FAILURE);
}
return (AUDIO_FAILURE);
}
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio810_reset_ac97() */
/*
* audio810_fill_play_buf()
*
* Description:
* This routine is called by i810_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:
* audio810_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 */
/* clr the flags getting ready for next start */
/* return the value for i810_ad_start_play() */
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 */
}
}
if (!buf->io_started) {
cr |= I810_BM_CR_IOCE;
}
/* start PCM out engine */
cr |= I810_BM_CR_RUN;
return (AUDIO_SUCCESS);
} /* audio810_fill_play_buf() */
/*
* audio810_prepare_record_buf()
*
* Description:
* This routine is called by audio810_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:
* audio810_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;
if (!buf->io_started) {
/* pause PCM in DMA engine */
/* reset PCM in DMA engine */
/* set last valid index to 0 */
/* buffer base */
}
return (AUDIO_SUCCESS);
}
/* if not an even number of samples we panic! */
if ((samples & 1) != 0) {
samples++;
}
}
}
if (!buf->io_started) {
cr |= I810_BM_CR_IOCE;
}
cr |= I810_BM_CR_RUN;
}
(I810_BM_CR_RUN | I810_BM_CR_IOCE)) {
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio810_prepare_record_buf() */
/*
* audio810_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:
* audio810_state_t *statep The device's state structure
*
* Returns:
* void
*/
static void
{
int samples;
}
break;
}
}
} /* audio810_reclaim_record_buf() */