audiots.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.
*/
/*
* audiots Audio Driver
*
* This Audio Driver controls the T2 audio core in the ALI M1553
* southbridge chip. This chip supports multiple play streams, but just
* a single record stream. It also supports wave table synthesis and
* hardware MIDI and joystick ports. Unfortunately the MIDI ports are
* not available because their pins have been re-assigned to expose
* interrupts. We also aren't going to do anything with the joystick
* ports. The audio core controls an AC-97 V2.1 Codec.
*
* This driver uses the mixer Audio Personality Module to implement
* audio(7I) and mixer(7I) semantics. Although the play side of the
* audio core supports multiple streams we don't use that feature.
* The mixer needs to be fixed up first. Thus we let the mixer do it's
* thing for both directions.
*
* The DMA engine uses a single buffer which is large enough to hold
* two interrupts worth of data. When it gets to the mid point an
* interrupt is generated and data is either sent (for record) or
* requested and put in that half of the buffer (for play). When the
* second half is played we do the same, but the audio core loops the
* pointer back to the beginning. For play we bzero() the half buffer
* before we ask for more audio. That way if there isn't enough waiting
* for us we just play silence. If more arrives later we'll keep going,
* but after a slight pop.
*
* The audio core has a bug in silicon that doesn't let it read the AC-97
* Codec's register. T2 has provided an algorithm that attempts to read the
* the Codec several times. This is probably heuristic and thus isn't
* absolutely guaranteed to work. However we do have to place a limit on
* the looping, otherwise when we read a valid 0x00 we would never exit
* the loop. Unfortunately there is also a problem with writing the AC-97
* Codec's registers as well. Thus we read it back to verify the write.
*
* Every time we program the AC-97 Codec we save the value in ts_shadow[].
* Thus every time we need to get a Codec register we don't have to do
* a very long read. This also means that register state information is
* saved for power management shutdown (CPR). When the Codec is started
* back up we use this saved state to restore the Codec's state in
* audiots_chip_init().
*
* We don't save any of the audio controller registers during normal
* operation. When we need to save register state we only have to save
* the aram and eram. The rest of the controller state is never modified
* from the initial programming. Thus restoring the controller state
* can be done from audiots_chip_init() as well.
*
*
* WARNING: The SME birdsnest platform uses a PCI bridge chip between the
* CPU and the southbridge containing the audio core. There is
* a bug in silicon that causes a bogus parity error. With the mixer
* reimplementation project, Bug 4374774, the audio driver is always
* set to the best precision and number of channels. Thus when turning
* the mixer on and off the only thing that changes is the sample rate.
* This change in programming doesn't trigger the silicon error.
* Thus the supported channels must always be 2 and the precision
* must always be 16-bits. This will keep any future change in the
* mixer from exposing this bug.
*
* Due to a hardware bug, system power management is not supported by this
* driver.
*
* CAUTION: If audio controller state is changed outside of aram
* and eram then that information must be saved and restored
* during power management shutdown and bringup.
*
* NOTE: The AC-97 Codec's reset pin is set to PCI reset, so we
* can't power down the Codec all the way.
*
* modules being loaded first.
*
* NOTE: Don't OR the ap_stop register to stop a play or record. This
* will just stop all active channels because a read of ap_stop
* returns ap_start. Just set the ap_stop register with the
* channels you want to stop. The same goes for ap_start.
*
* NOTE: There is a hardware problem with P2 rev motherboards. After
* prolonged use, reading the AC97 register will always return
* busy. The AC97 register is now useless. Consequently, we are no
* longer able to program the Codec. This work around disables
* audio when this state is detected. It's not great, but its
* better than having audio blasting out at 100% all the time.
*
* NOTE: Power Management testing has also exposed this AC97 timeout
* problem. Management has decided this is too risky for customers
* and hence they want power management support removed from the
* audio subsystem. All PM support is now removed.
*/
#include <sys/audiovar.h>
/*
* Module linkage routines for the kernel
*/
static int audiots_power(dev_info_t *, int, int);
/*
* Entry point routine prototypes
*/
static int audiots_ad_setup(audiohdl_t, int, int);
static void audiots_ad_pause_play(audiohdl_t, int);
static int audiots_ad_set_config(audiohdl_t, int, int, int, int, int);
static int audiots_ad_set_format(audiohdl_t, int, int, int, int, int, int);
static int audiots_ad_start_play(audiohdl_t, int);
static void audiots_ad_stop_play(audiohdl_t, int);
static int audiots_ad_start_record(audiohdl_t, int);
static void audiots_ad_stop_record(audiohdl_t, int);
/*
* Local Routine Prototypes
*/
static void audiots_chip_init(audiots_state_t *, int);
static void audiots_power_down(audiots_state_t *);
static void audiots_power_up(audiots_state_t *);
static void audiots_save_controller(audiots_state_t *);
static int audiots_set_busy(audiots_state_t *);
static int audiots_set_gain(audiots_state_t *, int, int, int, int);
static void audiots_set_idle(audiots_state_t *);
static int audiots_set_monitor_gain(audiots_state_t *, int);
static int audiots_set_port(audiots_state_t *, int, int);
static int audiots_start_play(audiots_state_t *);
static void audiots_stop_play(audiots_state_t *);
static int audiots_start_record(audiots_state_t *);
static void audiots_stop_record(audiots_state_t *);
static void audiots_stop_everything(audiots_state_t *);
static void audiots_unmap_regs(audiots_state_t *);
/*
* Global variables, but viewable only by this file.
*/
/* anchor for soft state structures */
static void *audiots_statep;
/* driver name, so we don't have to call ddi_driver_name() or hard code strs */
static char *audiots_name = TS_NAME;
static uint_t audiots_mixer_srs[] = {
};
static uint_t audiots_compat_srs[] = {
};
static am_ad_sample_rates_t audiots_mixer_sample_rates = {
};
static am_ad_sample_rates_t audiots_compat_sample_rates = {
};
static uint_t audiots_channels[] = {
};
static am_ad_cap_comb_t audiots_combinations[] = {
{ 0 }
};
static am_ad_entry_t audiots_entry = {
audiots_ad_setup, /* ad_setup() */
NULL, /* ad_teardown() */
audiots_ad_set_config, /* ad_set_config() */
audiots_ad_set_format, /* ad_set_format() */
audiots_ad_start_play, /* ad_start_play() */
audiots_ad_pause_play, /* ad_pause_play() */
audiots_ad_stop_play, /* ad_stop_play() */
audiots_ad_start_record, /* ad_start_record() */
audiots_ad_stop_record, /* ad_stop_record() */
NULL, /* ad_ioctl() */
NULL /* ad_iocdata() */
};
/*
* STREAMS Structures
*/
/* STREAMS driver id and limit value structure */
static struct module_info audiots_modinfo = {
TS_IDNUM, /* module ID number */
TS_NAME, /* module name */
TS_MINPACKET, /* minimum packet size */
TS_MAXPACKET, /* maximum packet size */
TS_HIWATER, /* high water mark */
TS_LOWATER /* low water mark */
};
/* STREAMS queue processing procedures structures */
/* read queue */
static struct qinit audiots_rqueue = {
audio_sup_rput, /* put procedure */
audio_sup_rsvc, /* service procedure */
audio_sup_open, /* open procedure */
audio_sup_close, /* close procedure */
NULL, /* unused */
&audiots_modinfo, /* module parameters */
NULL /* module statistics */
};
/* write queue */
static struct qinit audiots_wqueue = {
audio_sup_wput, /* put procedure */
audio_sup_wsvc, /* service procedure */
NULL, /* open procedure */
NULL, /* close procedure */
NULL, /* unused */
&audiots_modinfo, /* module parameters */
NULL /* module statistics */
};
/* STREAMS entity declaration structure */
static struct streamtab audiots_str_info = {
&audiots_rqueue, /* read queue */
&audiots_wqueue, /* write queue */
NULL, /* mux lower read queue */
NULL, /* mux lower write queue */
};
/*
* DDI Structures
*/
/* Entry points structure */
static struct cb_ops audiots_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 */
&audiots_str_info, /* cb_str */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev, /* cb_arwite */
};
/* Device operations structure */
static struct dev_ops audiots_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
audiots_getinfo, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
audiots_attach, /* devo_attach */
audiots_detach, /* devo_detach */
nodev, /* devo_reset */
&audiots_cb_ops, /* devi_cb_ops */
NULL, /* devo_bus_ops */
audiots_power, /* devo_power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/* Linkage structure for loadable drivers */
static struct modldrv audiots_modldrv = {
&mod_driverops, /* drv_modops */
TS_MOD_NAME, /* drv_linkinfo */
&audiots_dev_ops /* drv_dev_ops */
};
/* Module linkage structure */
static struct modlinkage audiots_modlinkage = {
MODREV_1, /* ml_rev */
(void *)&audiots_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
/*
* NOTE: Grover OBP v4.0.166 and rev G of the ALI Southbridge chip force the
* audiots driver to use the upper 2 GB DMA address range. However to maintain
* 4 GB DMA range.
*
* Eventually, this will be set back to using the proper high 2 GB DMA range.
*/
/* Device attribute structure - full 4 gig address range */
static ddi_dma_attr_t audiots_attr = {
DMA_ATTR_VERSION, /* version */
0x0000000000000000LL, /* dlim_addr_lo */
0x00000000ffffffffLL, /* dlim_addr_hi */
0x0000000000003fffLL, /* DMA counter register - 16 bits */
0x0000000000000008LL, /* DMA address alignment, 64-bit */
0x0000007f, /* 1 through 64 byte burst sizes */
0x00000001, /* min effective DMA size */
0x0000000000003fffLL, /* maximum transfer size, 16k */
0x000000000000ffffLL, /* segment boundary, 64k */
0x00000001, /* s/g list length, no s/g */
0x00000001, /* granularity of device, don't care */
0 /* DMA flags */
};
static ddi_device_acc_attr_t ts_acc_attr = {
};
/* ******* Loadable Module Configuration Entry Points ********************* */
/*
* _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;
/* initialize the soft state */
sizeof (audiots_state_t), 1)) != 0) {
return (error);
}
}
return (error);
}
/*
* _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);
}
/* free the soft state internal structures */
return (0);
}
/*
* _info()
*
* Description:
* Module information, returns infomation 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);
}
/* ******* Driver Entry Points ******************************************** */
/*
* audiots_getinfo()
*/
/*ARGSUSED*/
static int
{
int error = DDI_FAILURE;
int instance;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
} else {
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result =
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
/*
* audiots_attach()
*
* Description:
* Attach an instance of the audiots driver. This routine does the
* device dependent attach tasks. When it is complete 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.
*
* NOTE: audiots_attach() assumes the Codec is powered down.
*
* 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
{
char *pm_comp[] = {
"NAME=audiots audio device",
"0=off",
"1=on"
};
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
/* we've already allocated the state structure so get ptr */
NULL) {
"!%s%d: attach() RESUME get soft state failed",
return (DDI_FAILURE);
}
"check port, gain, balance, and mute settings");
/* and clear the fault state flags */
}
/*
* power up the Codec, see comment
* in audiots_set_busy()
*/
TS_PWR_ON) == DDI_FAILURE) {
/* match the busy call above */
"!attach() DDI_RESUME failed");
return (DDI_FAILURE);
}
}
/* Restore the audiots chip's state */
/*
* Put the address engine interrupt enable register in a known
* state - everything off.
*/
/*
* Start playing and recording, if not needed they'll stop
* on their own. But, we don't start them if the hardware has
* failed.
*/
"!attach() audio restart failed");
}
/* we're no longer busy */
}
return (DDI_SUCCESS);
default:
"!%s%d: attach() unknown command: 0x%x", audiots_name,
return (DDI_FAILURE);
}
/* before we do anything make sure that we haven't had a h/w failure */
return (DDI_FAILURE);
}
/* we don't support high level interrupts in this driver */
if (ddi_intr_hilevel(dip, 0) != 0) {
"!%s%d: attach() unsupported high level interrupt",
return (DDI_FAILURE);
}
/* allocate the state structure */
"!%s%d: attach() soft state allocate failed",
return (DDI_FAILURE);
}
/*
* WARNING: From here on all errors require that we free memory,
* including the state structure.
*/
/* get the state structure */
"!%s%d: attach() get soft state failed",
goto error_mem;
}
/* call audiosup module registration routine */
"!%s%d: attach() audio_sup_register() failed",
goto error_mem;
}
/* initialize the audio state structures */
"!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 up the pm-components */
"!init_state() couldn't create component");
return (DDI_FAILURE);
}
/* Mark PM supported */
/* Mark as powering up at attach time */
/* make sure the power framework knows the we are powered up */
DDI_FAILURE) {
"!attach() power up failed");
goto error_destroy;
}
/* Clear the attach time powering flag */
} else {
/* Mark on for CPR */
}
/* initialize the audio controller and the AC-97 Codec */
/*
* Put the address engine interrupt enable register in a known
* state - everything off.
*/
/* save private state */
/* call the mixer attach() routine */
"!attach() am_attach() failed");
goto error_unmap;
}
/* set up kernel statistics */
}
/* set up the interrupt handler */
"!attach() bad interrupt specification");
goto error_kstat;
}
/* everything worked out, so report the device */
return (DDI_SUCCESS);
/*
* CAUTION: Make sure there's an audio_sup_log() call before jumping
* here
*/
}
/* destroy the state mutexes and cvs */
return (DDI_FAILURE);
} /* audiots_attach() */
/*
* audiots_detach()
*
* Description:
* Detach an instance of the audiots driver. After the Codec is detached
* we call am_detach() and audio_sup_unregister() 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;
/* get the state structure */
"!%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 (state->ts_busy_cnt != 0) {
}
/* we may already be powered down, so only save state if up */
/* stop playing and recording */
(void) audiots_stop_everything(state);
/*
* Save the controller state. The Codec's state is
* already in ts_shadow[]. audiots_power_down()
* saves the state and gets us ready to be powered
* back up when we resume.
*/
} else {
}
}
"!detach() audio save failed");
}
return (DDI_SUCCESS);
default:
"!detach() unknown command: 0x%x", cmd);
return (DDI_FAILURE);
}
/* Make sure play and record are stopped and disable all interrupts */
ATRACE("ts_detach() stopping all DMA engines before detaching", 0);
/*
* power down the device, no reason to waste power
* without a driver
*/
}
/* remove the interrupt handler */
/* free the kernel statistics structure */
}
/*
* Call the mixer detach routine to tear down the mixer before
* we lose the hardware.
*/
/*
* Now call the audio support module's detach routine to remove this
* driver completely from the audio driver architecture.
*/
/* unmap the registers */
/* free temporary capture buffer */
/* destroy the state mutexes and cvs */
/* free the memory for the state pointer */
return (DDI_SUCCESS);
} /* audiots_detach() */
/*
* audiots_power()
*
* Description:
* This routine is used to turn the power to the Codec and audio core
* on and off. The Codec's registers are always saved, however, when
* we power down we have to save the audio core's state. When powering
* on we restore both Codec and core state via audiots_chip_init().
*
* This routine doesn't worry about starting or stopping audio, other
* routines have that responsibility.
*
* Arguments:
* def_info_t *dip Ptr to the device's dev_info structure
* int level The power level for the component
*
* Returns:
* DDI_SUCCESS Power level changed
* DDI_FAILURE Power level didn't change
*/
/*ARGSUSED*/
static int
{
int instance;
int rc = DDI_FAILURE;
/* get the state structure */
"!%s%d: power() get soft state failed", audiots_name,
instance);
return (DDI_FAILURE);
}
/* PM should be enabled */
/* check the level change to see what we need to do */
/* don't power off if we're busy */
state->ts_busy_cnt) {
/* device is busy, so don't power off */
/* reset the timer */
ATRACE("ts_power() power off failed, busy",
state->ts_busy_cnt);
goto done;
}
/* power down and save the state */
#ifdef DEBUG
} else {
#endif
}
rc = DDI_SUCCESS;
done:
return (rc);
} /* audiots_power() */
/* ******* DDAudio Entry Point Routines ************************************ */
/*
* audiots_ad_setup()
*
* Description:
* This routine checks whether the audio hardware has failed. If so, it
* returns AUDIO_FAILURE and opens are blocked. Otherwise, it lets
* everything proceed as normal.
*
* 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 dir Direction of audio, we don't care here
*
* Returns:
* AUDIO_SUCCESS The audio hardware is working
* AUDIO_FAILURE The audio hardware has failed
*/
/*ARGSUSED*/
static int
{
int rc = AUDIO_SUCCESS;
/* get the state structure */
/* Check if the hardware has failed */
rc = AUDIO_FAILURE;
}
return (rc);
} /* audiots_ad_setup() */
/*
* audiots_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
*/
/*ARGSUSED*/
static void
{
/* get the state structure */
/* wait on suspend, power up and mark as busy */
"!pause_play() set_busy() failed");
return;
}
/* we don't do anything if we aren't already running */
goto done;
}
done:
/* we're no longer busy */
} /* audiots_ad_pause_play() */
/*
* audiots_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
*/
/*ARGSUSED*/
static int
{
int rc = AUDIO_FAILURE;
/* get the state structure */
/* Check if the hardware has failed */
return (AUDIO_FAILURE);
}
/* wait on suspend, power up and mark as busy */
"!set_config() set_busy() failed");
return (AUDIO_FAILURE);
}
/*
* CAUTION: From here on we must goto done to exit.
*/
switch (command) {
case AM_SET_GAIN:
/*
* Set the gain for a channel. The audio mixer calculates the
* impact, if any, of balance on gain.
*
* AUDIO_MIN_GAIN <= gain <= AUDIO_MAX_GAIN
*
* arg1 --> gain
* arg2 --> channel #, 0 == left, 1 == right
*/
break;
case AM_SET_PORT:
/*
* enforces exclusiveness of in ports, as well as which ports
* are modifiable. We just turn on the ports that match the
* bits.
*
* arg1 --> port bit pattern
* arg2 --> not used
*/
break;
case AM_SET_MONITOR_GAIN:
/*
* Set the loopback monitor gain.
*
* AUDIO_MIN_GAIN <= gain <= AUDIO_MAX_GAIN
*
* dir ---> N/A
* arg1 --> gain
* arg2 --> not used
*/
break;
case AM_OUTPUT_MUTE:
/*
* Mute or enable the output.
*
* dir ---> N/A
* arg1 --> ~0 == mute, 0 == enable
* arg2 --> not used
*/
if (arg1) { /* mute */
MVR_MUTE);
} else { /* not muted */
/* by setting the port we unmute only active ports */
}
rc = AUDIO_SUCCESS;
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 */
} else { /* disable */
}
rc = AUDIO_SUCCESS;
break;
case AM_SET_DIAG_MODE:
/*
* Set the loopback diagnostics mode.
*
* arg1 --> 1 == diagnostics on, 0 == diagnostics off
* arg2 --> not used
*/
if (arg1) {
} else {
}
rc = AUDIO_SUCCESS;
break;
default:
/*
* We let default catch commands we don't support, as well
* as bad commands.
*/
break;
}
done:
/* we're no longer busy */
return (rc);
} /* audiots_ad_set_config() */
/*
* audiots_ad_set_format()
*
* Description:
* This routine is used to set a new audio control data format.
* We only support 8 and 16 bit signed linear.
*
* NOTE: We don't support mono or 8-bit. See the WARNING at the
* top of the file.
*
* 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, 8 or 16
* int encoding Encoding method, u-law, A-law and 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
*/
/*ARGSUSED*/
static int
{
int ch;
int rc = AUDIO_FAILURE;
/*
* first, check the encoding method
*/
if (encoding != AUDIO_ENCODING_LINEAR) {
return (AUDIO_FAILURE);
}
/*
* get the state structure
*/
/* Check if the hardware has failed */
return (AUDIO_FAILURE);
}
/* wait on suspend, power up and mark as busy */
"!set_format() set_busy() failed");
return (AUDIO_FAILURE);
}
/*
* CAUTION: From here on we must goto done to exit.
*/
switch (sample_rate) {
case TS_SAMPR5510: break;
case TS_SAMPR6620: break;
case TS_SAMPR8000: break;
case TS_SAMPR9600: break;
case TS_SAMPR11025: break;
case TS_SAMPR16000: break;
case TS_SAMPR18900: break;
case TS_SAMPR22050: break;
case TS_SAMPR27420: break;
case TS_SAMPR32000: break;
case TS_SAMPR33075: break;
case TS_SAMPR37800: break;
case TS_SAMPR44100: break;
case TS_SAMPR48000: break;
default:
goto done;
}
/* can't fail, so update the saved format and implement */
if (dir == AUDIO_PLAY) {
/* save for later use */
/*
* first find the h/w channel with the stream
*/
/*
* convert the sample rate into 4.12 format
*/
/* for play we always use signed */
} else {
/* save for later use */
/*
* we set the only record stream
*/
/*
* convert the sample rate into 4.12 format
*/
/* for 16-bit record we use signed */
}
/* check for stereo only */
/* check for 16-bit only */
/* always linear encoding */
/* program the sample rate */
/* program the precision and number of channels */
ATRACE("ts_ad_set_format() finished programming the device", 0);
rc = AUDIO_SUCCESS;
done:
/* we're no longer busy */
return (rc);
} /* audiots_ad_set_format() */
/*
* audiots_ad_start_play()
*
* Description:
* Wrapper to call audiots_start_play().
*
* 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:
*/
/*ARGSUSED*/
static int
{
int rc;
/* get the state structure */
/* Check if the hardware has failed */
return (AUDIO_FAILURE);
}
/* wait on suspend, power up and mark as busy */
"!start_play() set_busy() failed");
return (AUDIO_FAILURE);
}
}
}
/* we're no longer busy */
return (rc);
} /* audiots_ad_start_play() */
/*
* audiots_ad_stop_play()
*
* Description:
* Wrapper to call audiots_stop_play().
*
* Arguments:
* audiohdl_t ahandle Handle to this device
* int stream Stream number
*
* Returns:
* void
*/
/*ARGSUSED*/
static void
{
/* get the state structure */
/* wait on suspend, power up and mark as busy */
"!stop_play() set_busy() failed");
return;
}
}
/* we're no longer busy */
} /* audiots_ad_stop_play() */
/*
* audiots_ad_start_record()
*
* Description:
* Wrapper to call audiots_start_record().
*
* 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
*/
/*ARGSUSED*/
static int
{
int rc;
/* get the state structure */
/* Check if the hardware has failed */
ATRACE_32("ts_ad_start_record() h/w has failed",
return (AUDIO_FAILURE);
}
/* wait on suspend, power up and mark as busy */
"!start_record() set_busy() failed");
return (AUDIO_FAILURE);
}
/* we're no longer busy */
return (rc);
} /* audiots_ad_start_record() */
/*
* audiots_ad_stop_record()
*
* Description:
* Wrapper to call audiots_stop_record().
*
* 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:
* void
*/
/*ARGSUSED*/
static void
{
/* get the state structure */
/* wait on suspend, power up and mark as busy */
"!stop_record() set_busy() failed");
return;
}
}
/* we're no longer busy */
} /* audiots_ad_stop_record() */
/* ******* Local Routines *************************************************** */
/*
* audiots_and_ac97()
*
* Description:
* Logically AND a value with the specified AC-97 Codec register.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
* uint16_t data The value to AND
*
* Returns:
* void
*/
static void
{
} /* audiots_and_ac97() */
/*
* audiots_chip_init()
*
* Description:
* Initialize the audio core and the AC-97 Codec. The AC-97 Codec is
* always programmed from ts_shadow[]. If we aren't doing a restore
* we initialize ts_shadow[], otherwise we use the current values of
* ts_shadow[]. This allows this routine to be used for both attaching
* and for power management power up.
*
* Speaker, line out and headphone out gain are not set to 0 gain.
* Thus their respective registers are never set to 0. Since the failed
* read and write over the AC-97 link results in a 0 we now know for
* sure if the write succeeded or not.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int restore If TS_INIT_RESTORE then restore
* from ts_shadow[]
*
* Returns:
* void
*/
static void
{
int i;
int str;
/* set global music and wave volume to 0dB */
/*
* Enable middle and end interrupts for all channels. Since
* we always set these we don't have to save it as well.
*/
/* for each channel, set gain and enable interrupts for middle & end */
/*
* Set volume to 0dB attenuation, 1st left and then right.
* These are never changed, so we don't have to save them.
*/
/*
* The envelope engine *MUST* remain in still mode (off).
* Otherwise bad things like gain randomly disappearing might
* happen. See bug #4332773.
*/
/* Set the eram and aram state */
}
/* program channel 31 for record */
/* do a warm reset, which powers up the Codec */
drv_usecwait(2);
/* do a warm reset via the Codec, yes, I'm being paranoid! */
/* Make sure the Codec is powered up. */
i = TS_WAIT_CNT;
PCSR_POWERD_UP) != PCSR_POWERD_UP && i--) {
drv_usecwait(1);
}
/*
* Here we're raising power at attach time, we need to
* initialize the shadow array and then mute the outputs.
* If we don't, there's a loud double pop on the headphones.
*/
}
/* set outputs muted */
}
/* preload shadow registers if not restoring */
if (restore == TS_INIT_NO_RESTORE) {
/* read the Codec & save in the shadow register array */
}
/* 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 - mute 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 */
/*
* The rest we ignore, most are reserved.
*
* CAUTION: If we add to the list we need to fix the end
* of the loop below.
*/
}
/* now program the AC-97 Codec from ts_shadow[] */
for (i = 0; i < TS_LAST_AC_REG; i += TS_REG_SIZE) {
}
} /* audiots_chip_init() */
/*
* audiots_get_ac97()
*
* Description:
* Get the value in the specified AC-97 Codec register. There is a
* bug in silicon which forces us to do multiple reads of the Codec's
* register. This algorithm was provided by T2 and is heuristic in
* nature. Unfortunately we have no guarantees that the real answer
* isn't 0x0000, which is what we get when a read fails. So we loop
* TS_LOOP_CNT times before we give up. We just have to hope this is
* sufficient to give us the correct value.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
*
* Returns:
* unsigned short The value in the specified register
*/
static uint16_t
{
int count;
int delay;
} else {
}
/* make sure the register is good */
break;
}
while (delay--) {
}
break;
}
}
/* save the value in the shadow register array */
ATRACE_16("ts_get_ac97() returning",
/*
* Arggg, if you let the next read happen too soon then it fails.
* 12 usec fails, 13 usec succeeds. So set it to 20 for safety.
*/
} /* audiots_get_ac97() */
/*
* audiots_init_state()
*
* Description:
* This routine initializes the audio driver's state structure.
* This includes reading the properties.
*
* CAUTION: This routine cannot allocate resources, unless it frees
* them before returning for an error. Also, error_destroy:
* in audiots_attach() would need to be fixed as well.
*
* NOTE: birdsnest supports CD ROM input. We check for the cdrom
* property. If there we turn it on.
*
* Arguments:
* audiots_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 ts_pints;
int ts_rints;
int cdrom = 0;
/* get the number of play and record interrupts per second */
"play-interrupts", TS_INTS);
"record-interrupts", TS_INTS);
/* make sure the values are good */
if (ts_pints < TS_MIN_INTS) {
ATRACE_32("ts_init_state() "
"play interrupt rate set too low, resetting", ts_pints);
"play interrupt rate set too low, %d, resetting to %d",
} else if (ts_pints > TS_MAX_INTS) {
ATRACE_32("ts_init_state() "
"play interrupt rate set too high, resetting", ts_pints);
"play interrupt rate set too high, %d, resetting to %d",
}
if (ts_rints < TS_MIN_INTS) {
ATRACE_32("ts_init_state() "
"record interrupt rate set too low, resetting", ts_rints);
"record interrupt rate set too low, %d, resetting to %d",
} else if (ts_rints > TS_MAX_INTS) {
ATRACE_32("ts_init_state() "
"record interrupt rate set too high, resetting", ts_rints);
"record interrupt rate set too high, %d, resetting to %d",
}
/* get the mode from the .conf file */
"mixer-mode", AM_MIXER_MODE)) {
} else {
}
/* figure out the platform */
"cdrom", 0);
/* save the device info pointer */
/* get the iblock cookie needed for interrupt context */
DDI_SUCCESS) {
"!init_state() cannot get iblock cookie");
return (AUDIO_FAILURE);
}
/* initialize the state mutexes and condition variables */
/*
* CAUTION: From here on we must destroy the mutexes if there's
* an error and we return failure.
*/
/* fill in the device default state */
/* add CD ROM for birdsnest */
if (cdrom) {
}
/* fill in the ad_info structure */
/* play capabilities */
/* record capabilities */
/* fill in device info strings */
if (!cdrom) {
} else {
}
/*
* Figure out the largest transfer size for the DMA engine. We then
* double the size because an interrupt happens in the middle and
* the end of the buffer. This size must be modulo 16 to fit two 16-bit
* stereo streams in on the interrupt boundaries, middle and end,
* with an 8-byte boundary.
*/
/* init power management state */
state->ts_busy_cnt = 0;
return (AUDIO_SUCCESS);
} /* audiots_init_state() */
/*
* audiots_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.
*
* NOTE: This device needs to make sure any PIO access required to clear
* its interrupt has made it out on the PCI bus before returning from its
* interrupt handler so that the interrupt has been deasserted. This is
* done by rereading the address engine interrupt register.
*
* Arguments:
* caddr_t T 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
{
char *bptr;
int i;
int count;
int samples;
int rc = DDI_INTR_UNCLAIMED;
ATRACE("ts_intr() servicing with pending intr from stop()", 0);
goto done;
}
if (interrupts == 0) {
/* no interrupts to process, so it's not us */
goto done;
}
/* is this in the 1st or 2nd half of the buffer? */
/* clear the interrupt, should this be after getting loc? */
/*
* Reread the interrupt reg to ensure that
* PIO write has completed.
*/
/* set up for 1st or 2nd half */
} else { /* in 1st half */
/* pcnt[0] could be different from pcnt[TS_2ND_HALF] */
}
/* always zero for silence */
/* get the next chunk of audio */
samples) == 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.
*/
/* yes, it was, so we're done */
ATRACE("ts_intr() restart after"
"shutdown", 0);
goto rec_intr;
}
/* done playing, so stop playing */
/* clr the flags getting ready for next start */
} else {
/* next time we shut down, if no sound again */
}
} else {
/* we got at least one sample, so don't shutdown yet */
}
}
/* is this in the 1st or 2nd half of the buffer? */
/* clear the interrupt, should this be after getting loc? */
/*
* Reread the interrupt reg to ensure that
* PIO write has completed.
*/
if (location & TS_INPUT_CHANNEL_MASK) {
/* 1st half just filled */
/* skip the first interrupt, it is NULL */
} else {
DDI_FAILURE) {
"!dma_sync(1) failed audio lost");
goto done;
}
samples = TS_FIFO_SIZE /
(state->ts_cprecision >>
for (i = 0; i < (TS_FIFO_SIZE/2); i++) {
}
ATRACE_32("ts_intr(c1) "
"calling am_send_audio() samples", samples);
DDI_DMA_SYNC_FORCPU) == DDI_FAILURE) {
"!dma_sync(2) failed audio lost");
goto done;
}
(state->ts_cprecision >>
for (i = 0; i < count; i++) {
}
ATRACE_32("ts_intr(c2) "
"calling am_send_audio() samples", samples);
}
} else {
/* 2nd half just filled */
DDI_FAILURE) {
"!dma_sync(2) failed audio lost");
goto done;
}
for (i = 0; i < count; i++) {
}
ATRACE_32("ts_intr(c3) "
"calling am_send_audio() samples", samples);
}
}
if (interrupts) {
/*
* handle, but don't service non play or record audiots
* interrupts and shutdown the other DMA engines if they
* somehow activated themselves - which is why we are here in
* the first place.
*/
/*
* Reread the interrupt reg to ensure that
* PIO write has completed.
*/
}
/* update the kernel interrupt statistics */
if (rc == DDI_INTR_CLAIMED) {
}
}
done:
return (rc);
} /* audiots_intr() */
/*
* audiots_map_regs()
*
* Description:
* This routine allocates the DMA handles and the memory for the
* DMA engines to use. It then binds each of the buffers to its
* respective handle, getting a DMA cookie. Finally, the registers
* are mapped in.
*
* Once the config space registers are mapped in we determine if the
* audio core may be power managed. It should, but if it doesn't,
* then trying to may cause the core to hang.
*
* NOTE: All of the ddi_dma_... routines sleep if they cannot get
* memory. This means these calls will almost always succeed.
*
* NOTE: ddi_dma_alloc_handle() attempts to use the full 4 GB DMA address
* range. This is to work around Southbridge rev E/G OBP issues.
* (See Grover OBP note above)
*
* CAUTION: Make sure all errors call audio_sup_log().
*
* Arguments:
* dev_info_t *dip Pointer to the device's devinfo
* audiots_state_t *state The device's state structure
* Returns:
* AUDIO_SUCCESS Registers successfully mapped
* AUDIO_FAILURE Registers not successfully mapped
*/
static int
{
int rc;
/* allocate one handle for play and one for record */
"!map_regs() ddi_dma_alloc_handle(P) failed: %d", rc);
goto error;
}
"!map_regs() ddi_dma_alloc_handle(C) failed: %d", rc);
goto error_ph;
}
/* allocate the two DMA buffers, one for play and one for record */
"!map_regs() ddi_dma_mem_alloc(P) failed");
goto error_ch;
}
"!map_regs() ddi_dma_mem_alloc(C) failed");
goto error_pmh;
}
/* bind each of the buffers to a DMA handle */
"!map_regs() ddi_dma_addr_bind_handle(P) failed: %d", rc);
goto error_cmh;
}
"!map_regs() ddi_dma_addr_bind_handle(C) failed: %d", rc);
goto error_pc;
}
/* map in the registers, the config and memory mapped registers */
"!map_regs() ddi_regs_map_setup() failed: %d", rc);
goto error_cc;
}
/* make sure we can power manage the audio core */
TS_CAP_PTR) {
ATRACE("Audio core is power manageable", 0);
} else {
ATRACE("Audio core is not power manageable", 0);
}
"!map_regs() ddi_regs_map_setup() failed: %d", rc);
goto error_unmap;
}
/* Read the Audio Controller's revision ID */
if (rev_id & AC_REV_ID1) {
} else if (rev_id & AC_REV_ID2) {
} else {
/* Unknown rev, who knows what else has changed... */
"map_regs() unsupported SouthBridge Chip revision: %x",
rev_id);
return (AUDIO_FAILURE);
}
/* let the state structure know about the attributes */
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audiots_map_regs() */
/*
* audiots_or_ac97()
*
* Description:
* Logically OR a value with the specified AC-97 Codec register.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
* uint16_t data The value to OR
*
* Returns:
* void
*/
static void
{
} /* audiots_or_ac97() */
/*
* audiots_power_down()
*
* Description:
* Power down the AC-97 Codec and then, if allowed, power down
* the audio core. The audio core implements PCI power management
* version 1.0. We don't support state D3 (cold) since we don't
* have an external device to drive reseting the core to get it
* back up and running. We just toggle between D0 and D3 since
* we need the device either all on or all off.
*
* Minimum transition times between the power states:
* D0 <---> D1 no time delay
* D0 <---> D2 200 microseconds
* D0 <---> D3 10 milliseconds
*
* To be safe I'm doubling that time because if you don't wait
* long enough you get a hard hang for the app opening audio.
*
* AC-97 Codec's walk from power states PR0 through PR5 plus the
* optional PR6 and EAPD. We always set PR6 EAPD since they don't
* hurt anything. Then we walk to PR2|PR3. We don't go any further
* because this requires that we do a cold reset. Since this pin
* isn't connected in the 1553 we can't do this.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
/* PM should be enabled */
/* No interrupts should be pending before we power down */
/* powering down, save the audio core's state */
/* power down the Codec, also shut down external amps */
/* shut down ADC and inputs */
/* shut down DAC */
/* shut down analog mixer and Vref */
/* MUST clear the shadow register so we can power up! */
/* power down the core, if allowed */
/* okay, drop to D3 for the best power reduction */
/*
* wait 20 milliseconds for state change,
* __lock_lint tells warlock not to flag this delay()
*/
#ifndef __lock_lint
#endif
}
} /* audiots_power_down() */
/*
* audiots_power_up()
*
* Description:
* Power up the audio core if allowed, and then the AC-97 Codec.
* The state change timings documented in audiots_power_down()
* apply to powering up as well.
*
* audiots_chip_init() does a soft reset, which powers up all of
* the sections of the Codec that were powered down in
* audiots_power_down().
*
* NOTE: We don't worry about locking since the only routines that
* may call us are audiots_attach() and audiots_power().
* Both of which should be the only threads in the driver.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
/* PM should be enabled */
/* power up the core, if allowed */
/* go from D3 to D0 */
/*
* wait 20 milliseconds for state change,
* __lock_lint tells warlock not to flag this delay()
*/
#ifndef __lock_lint
#endif
/* clear the PME# flag */
}
/* restore the state, does reset to power up the Codec */
} /* audiots_power_up() */
/*
* audiots_read_ac97()
*
* Description:
* This routine actually reads the AC-97 Codec's register. It may
* be called several times to succeed.
*
* NOTE:
* Revision M1535D B1-C of the ALI SouthBridge includes a workaround for
* the broken busy flag. Resetting the busy flag requires a software tweak
* to go with the worked around hardware. When we detect failure, we make
* 10 attempts to reset the chip before we fail. This should reset the new
* SB systems. On all SB systems, this will increse the read delay
* slightly, but shouldn't bother it otherwise.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
*
* Returns:
* unsigned short The value in the specified register
*/
static uint16_t
{
int resets = 0;
int i;
} else {
}
/* wait for ready to send read request */
for (i = 0; i < TS_READ_TRIES; i++) {
break;
}
/* don't beat on the bus */
drv_usecwait(1);
}
if (i >= TS_READ_TRIES) {
if (resets < TS_RESET_TRIES) {
/* Attempt to reset */
resets++;
goto first_read;
} else {
ATRACE("Reading the AC97 register has failed #1", 0);
"Unable to communicate with AC97 CODEC");
"The audio AC97 register has timed out.");
"Audio is now disabled.\n");
"Please reboot to restore audio.\n");
/* Don't flood the console */
}
}
return (0);
}
/* program the register to read */
(~AP_ACWR_W_SELECT_WRITE)));
/* hardware bug work around */
i = TS_WAIT_CNT;
i--;
}
resets = 0;
/* wait again for read to send read request */
for (i = 0; i < TS_READ_TRIES; i++) {
break;
}
/* don't beat on the bus */
drv_usecwait(1);
}
if (i >= TS_READ_TRIES) {
if (resets < TS_RESET_TRIES) {
/* Attempt to reset */
resets++;
goto second_read;
} else {
ATRACE("Reading the AC97 register has failed #2", 0);
"Unable to communicate with AC97 CODEC");
"The audio AC97 register has timed out.");
"Audio is now disabled.\n");
"Please reboot to restore audio.\n");
/* Don't flood the console */
}
}
return (0);
}
} /* audiots_read_ac97() */
/*
* audiots_save_controller()
*
* Description:
* Save the state of the audio controller.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
int str;
/* save the controller state, Codec state is in ts_shadow[] */
}
} /* audiots_save_controller() */
/*
* audiots_set_ac97()
*
* Description:
* Set the value in the specified AC-97 Codec register. Just like
* reading the AC-97 Codec, it is possible there is a problem writing
* it as well. So we loop.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
* uint16_t value The value to write
*
* Returns:
* void
*/
static void
{
int count;
int i;
/* Don't touch the reserved bits on the pre 35D+ SouthBridge */
} else {
}
/* wait for ready to write */
for (i = 0; i < TS_WAIT_CNT; i++) {
/* ready to write */
/* Write the data */
break;
}
}
if (i >= TS_WAIT_CNT) {
/* try again */
continue;
}
/* wait for write to complete */
for (i = 0; i < TS_WAIT_CNT; i++) {
/* done writing */
break;
}
}
/* verify the value written and also update ts_shadow[] */
/* successfully loaded, so we can return */
return;
}
}
} /* audiots_set_ac97() */
/*
* audiots_set_busy()
*
* Description:
* This routine is called whenever a routine needs to guarantee
* that it will not be suspended or the power removed by the power
* manager. 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:
* audiots_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS Set busy and powered up
* AUDIO_FAILURE Couldn't power up, so not busy
*/
static int
{
/* get the lock so we are safe */
/* Don't proceed if PM is not enabled */
return (AUDIO_SUCCESS);
}
/* block if we are going to be suspended */
}
/*
* Okay, we aren't going to be suspended yet, so mark as busy.
* This will keep us from being suspended when we release the lock.
*/
state->ts_busy_cnt++;
/* now can release the lock before we raise the power */
/*
* Mark as busy before we ask for power to be raised. This removes
* the race condtion between the call to audiots_power() and our call
* to raise power. After we raise power we immediately mark as idle
* so the count is still good.
*/
DDI_FAILURE) {
/* match the busy call above */
"!set_busy() power up failed");
if (state->ts_busy_cnt == 0) {
/* let DDI_SUSPEND continue */
}
return (AUDIO_FAILURE);
}
/* power is up and we are marked as busy, so we are done */
return (AUDIO_SUCCESS);
} /* audiots_set_busy() */
/*
* audiots_set_gain()
*
* Description:
*
* Arguments:
* audiots_state_t *state The device's state structure
* int stream Stream number for multi-stream Codecs,
* which is not how we program the device
* for now.
* 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
*/
/*ARGSUSED*/
static int
int channel)
{
}
/*
* For play we use PCM so all volumes change with just
* one write. This way we get line out, headphone and
* internal speaker in one shot.
*
* The AC-97 Codec goes from -34.5 dB (11111) to 0 dB
* (01000) to +12.0 dB (00000). We turn gain into atten.
*/
if (channel == 0) { /* left channel */
TS_GAIN_SHIFT3) << TS_BYTE_SHIFT) &
} else { /* right channel */
}
} else {
/*
* For record we use the master record gain with all
* of the inputs set to 0dB.
*
* The AC-97 Codec goes from 0 dB (0000) to +22.5 dB
* (1111), so gain remains gain. We chop off the bottom
* 4 bits and use the top for the gain.
*/
if (channel == 0) { /* left channel */
tmp_short &= ~RGR_LEFT_MASK;
} else { /* right channel */
tmp_short &= ~RGR_RIGHT_MASK;
}
}
return (AUDIO_SUCCESS);
} /* audiots_set_gain() */
/*
* audiots_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.
* It ends by resetting the power management timer.
*
* We don't do anything with power because the routine that is no longer
* busy either doesn't need the hardware, or we are playing or recording
* so the power won't come down anyway.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
/* get the lock so we are safe */
/* Don't proceed if PM is not enabled */
return;
}
/* decrement the busy count */
state->ts_busy_cnt--;
/* if no longer busy, then we wake up a waiting SUSPEND */
if (state->ts_busy_cnt == 0) {
}
/* we're done, so unlock */
/* reset the timer */
} /* audiots_set_idle() */
/*
* audiots_set_monitor_gain()
*
* Description:
* Set the monitor gain.
*
* Arguments:
* audiots_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
*/
/*ARGSUSED*/
static int
{
int rc = AUDIO_SUCCESS;
}
if (gain == 0) {
/* disable loopbacks when gain == 0 */
} else {
/* Adjust the value of gain to meet the requirement of AC'97 */
}
switch (state->ts_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
*/
break;
case AUDIO_LINE_IN:
break;
case AUDIO_CD:
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("ts_ad_set_config() monitor gain bad device",
NULL);
rc = AUDIO_FAILURE;
goto done;
}
if (gain == 0) {
state->ts_monitor_gain = 0;
} else {
}
done:
return (rc);
} /* audiots_set_monitor_gain() */
/*
* audiots_set_port()
*
* Description:
*
* We also use this routine to unmute the play ports. By passing
* TS_PORT_UNMUTE as the port we know to use the stored play port.
* This has to be done inside the lock, otherwise there is the
* possibility that the port passed in when this routine is called
* could be wrong by the time we execute the unmute.
*
* Arguments:
* audiots_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
*
* Returns:
* AUDIO_SUCCESS The Codec parameter has been set
* AUDIO_FAILURE The port could not been set
*/
static int
{
int rc = AUDIO_SUCCESS;
/* figure out which output port(s) to turn on */
tmp_word = 0;
/*
* CAUTION: We must get the saved output port after the
* lock so we know it is good.
*/
if (port == TS_PORT_UNMUTE) {
}
if (port & AUDIO_SPEAKER) {
} else {
}
if (port & AUDIO_HEADPHONE) {
} else {
}
if (port & AUDIO_LINE_OUT) {
} else {
MVR_MUTE);
}
rc = AUDIO_FAILURE;
goto done;
}
} else {
/* figure out which input port to set */
switch (port) {
case AUDIO_NONE:
/* set to an unused input */
/* mute the master record input */
RGR_MUTE);
/* see if we need to update monitor loopback */
if (state->ts_monitor_gain) {
} else if (state->ts_input_port ==
} else if (state->ts_input_port ==
AUDIO_CD) {
}
}
break;
case AUDIO_MICROPHONE:
/* set to the mic input */
/* see if we need to update monitor loopback */
if (state->ts_monitor_gain) {
}
}
break;
case AUDIO_LINE_IN:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (state->ts_monitor_gain) {
}
}
break;
case AUDIO_CD:
/* set to the line in input */
/* see if we need to update monitor loopback */
if (state->ts_monitor_gain) {
} else if (state->ts_input_port ==
}
}
break;
case AUDIO_CODEC_LOOPB_IN:
/* set to the loopback input */
/* see if we need to update monitor loopback */
if (state->ts_monitor_gain) {
} else if (state->ts_input_port ==
}
}
break;
default:
/* unknown or inclusive input ports */
rc = AUDIO_FAILURE;
goto done;
}
/* select the input */
/* if an input port then make sure we aren't muted */
if (port != AUDIO_NONE &&
RGR_MUTE)) {
}
}
done:
return (rc);
} /* audiots_set_port() */
/*
* audiots_start_play()
*
* Description:
* The audio core uses a single DMA buffer which is divided into two
* halves. An interrupt is generated when the middle of the buffer has
* been reached and at the end. The audio core resets the pointer back
* to the beginning automatically. After the interrupt the driver clears
* the buffer and asks the mixer for more audio samples. If there aren't
* enough then silence is played out.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
*/
static int
{
char *bptr;
int rc = AUDIO_SUCCESS;
int rs;
int samples;
int str = TS_OUTPUT_STREAM;
/* see if we are already playing */
goto done;
}
/* see if we are just paused */
/* make sure it starts playing */
ATRACE_32("ts_start_play() DMA engine restarted",
goto done;
}
/*
* Okay, we are starting from scratch, so get the first chunks of audio.
* We ask for half of the buffer twice. That way we don't mess up the
* mixer's paradigm of using two buffers. But before we get the audio
* we zero the buffer, giving it silence, just in case there isn't
* enough audio.
*/
/* if not an even number of samples we panic! */
/* if stereo & sr = 11025 & ints = 50 then 441 samples, bad! - so fix */
if ((samples % AUDIO_CHANNELS_STEREO) != 0) {
/* need to adjust */
samples++;
}
samples);
if (rs == 0) {
/* there's nothing to play */
rc = AUDIO_FAILURE;
goto done;
}
samples);
if (rs > 0) {
} else {
}
/* sync the DMA buffer */
DDI_DMA_SYNC_FORDEV) == DDI_FAILURE) {
"!start_play() ddi_dma_sync() failed, audio lost");
rc = AUDIO_FAILURE;
goto done;
}
/* set ALPHA and FMS to 0 */
0x0);
/* set CSO to 0 */
/* set LBA */
/* set ESO */
} else {
samples--;
}
/* enable interrupts */
/* make sure it starts playing */
done:
return (rc);
} /* audiots_start_play() */
/*
* audiots_stop_play()
*
* Description:
* This routine stops the play DMA engine.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
} /* audiots_stop_play() */
/*
* audiots_start_record()
*
* Description:
* The record DMA engine works the same way as the play DMA engine.
* Record cannot be multi-stream and must be on T2 channel 31.
*
* There isn't a pause for record, only start and stop. So this code
* is a touch simpler than starting up play.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* AUDIO_SUCCESS Recording successfully started
* AUDIO_FAILURE Record not started
*/
/*ARGSUSED*/
static int
{
int rc = AUDIO_SUCCESS;
int samples;
int str = TS_INPUT_STREAM;
/* see if we are already recording */
TS_INPUT_CHANNEL)) {
ATRACE("ts_start_record() DMA engine already running",
state);
return (rc);
}
/* okay, we start from scratch; start by figuring out sample sizes */
/* if not an even number of samples we panic! */
if ((samples % AUDIO_CHANNELS_STEREO) != 0) {
/* need to adjust */
samples++;
}
/* set ALPHA and FMS to 0 */
0x0);
/* set CSO to 0 */
/* set LBA */
/* set ESO */
} else {
samples--;
}
/* enable interrupts */
/* make sure it starts recording */
done:
}
return (rc);
} /* audiots_start_record() */
/*
* audiots_stop_record()
*
* Description:
* This routine stops the record DMA engine. Any data in the buffer
* is just thrown away. After all, we're done recording so there aren't
* any apps that need audio.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
/*ARGSUSED*/
static void
{
/* stop recording */
} /* audiots_stop_record() */
/*
* audiots_stop_everything()
*
* Description:
* This routine disables the address engine interrupt for all 32 DMA
* engines. Just to be sure, it then explicitly issues a stop command to
* the address engine and envelope engines for all 32 channels.
*
* NOTE:
* There is a hardware bug that generates a spurious interrupt when the DMA
* engines are stopped. It's not consistent - it happens every 1 out of 6
* stops or so. It will show up as a record interrupt. The problem is that
* once the driver is detached or if the system goes into low power mode,
* nobody will service that interrupt. The system will eventually become
* unusable.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
if (intr != 0) {
intr);
ATRACE("ts_stop_everything() clearing the bogus interrupt",
intr);
/*
* Flag this interrupt for the audiots_intr() routine to claim,
* but this interrupt doesn't need to be processed.
*/
}
} /* audiots_stop_everything() */
/*
* audiots_unmap_regs()
*
* Description:
* This routine unbinds the DMA cookies, frees the DMA buffers,
* deallocates the DMA handles, and finally unmaps the Codec's and
* DMA engine's registers.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* None
*/
static void
{
} /* audiots_unmap_regs() */