audiohd.c revision 9ba19c87647b6b9e8c1fc1f52288772ae3cc247c
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "audiohd.h"
#define DEFINTS 175
#define DRVNAME "audiohd"
/*
* Module linkage routines for the kernel
*/
static int audiohd_quiesce(dev_info_t *);
static int audiohd_resume(audiohd_state_t *);
static int audiohd_suspend(audiohd_state_t *);
/* interrupt handler */
/*
* Local routines
*/
static void audiohd_fini_pci(audiohd_state_t *);
static int audiohd_reset_controller(audiohd_state_t *);
static int audiohd_init_controller(audiohd_state_t *);
static void audiohd_fini_controller(audiohd_state_t *);
static void audiohd_stop_dma(audiohd_state_t *);
static void audiohd_disable_intr(audiohd_state_t *);
static int audiohd_create_codec(audiohd_state_t *);
static void audiohd_build_path(audiohd_state_t *);
static void audiohd_destroy_codec(audiohd_state_t *);
static int audiohd_reinit_hda(audiohd_state_t *);
static ddi_device_acc_attr_t hda_dev_accattr = {
};
static const char *audiohd_dtypes[] = {
AUDIO_PORT_NONE, /* reserved port, don't use */
NULL,
};
enum {
CTL_VOLUME = 0,
};
static void
{
const char *name;
const char *vers;
devid <<= 16;
switch (devid) {
case 0x80862668:
name = "Intel HD Audio";
vers = "ICH6";
break;
case 0x808627d8:
name = "Intel HD Audio";
vers = "ICH7";
break;
case 0x8086284b:
name = "Intel HD Audio";
vers = "ICH8";
break;
case 0x8086293e:
name = "Intel HD Audio";
vers = "ICH9";
break;
case 0x10de0371:
name = "NVIDIA HD Audio";
vers = "MCP55";
break;
case 0x10de03f0:
name = "NVIDIA HD Audio";
vers = "MCP61A";
break;
case 0x10de026c:
name = "NVIDIA HD Audio";
vers = "6151";
break;
case 0x10de03e4:
name = "NVIDIA HD Audio";
vers = "MCP61";
break;
case 0x10de044a:
name = "NVIDIA HD Audio";
vers = "MCP65";
break;
case 0x10de055c:
name = "NVIDIA HD Audio";
vers = "MCP67";
break;
case 0x1002437b:
name = "ATI HD Audio";
vers = "SB450";
break;
case 0x10024383:
name = "ATI HD Audio";
vers = "SB600";
break;
case 0x11063288:
name = "VIA HD Audio";
vers = "HDA";
break;
}
/* set device information */
}
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (audiohd_resume(statep));
default:
return (DDI_FAILURE);
}
/* High-level interrupt isn't supported by this driver */
if (ddi_intr_hilevel(dip, 0) != 0) {
"unsupported high level interrupt");
return (DDI_FAILURE);
}
/* allocate the soft state structure */
/* interrupt cookie and initialize mutex */
"audiohd_init_state failed");
goto err_attach_exit3;
}
/* Set PCI command register to enable bus master and memeory I/O */
"couldn't init pci regs");
goto err_attach_exit4;
}
"couldn't init controller");
goto err_attach_exit5;
}
"couldn't create codec");
goto err_attach_exit6;
}
goto err_attach_exit7;
}
/* set up kernel statistics */
KSTAT_FLAG_PERSISTENT)) != NULL) {
}
/* disable interrupts and clear interrupt status */
/* set up the interrupt handler */
DDI_SUCCESS) {
"bad interrupt specification ");
goto err_attach_exit8;
}
/*
* Register audio controls.
*/
"unable to allocate controls");
goto err_attach_exit9;
}
"unable to register with framework");
goto err_attach_exit9;
}
/* enable interrupt */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (audiohd_suspend(statep));
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static struct dev_ops audiohd_dev_ops = {
DEVO_REV, /* rev */
0, /* refcnt */
NULL, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
audiohd_attach, /* attach */
audiohd_detach, /* detach */
nodev, /* reset */
NULL, /* cb_ops */
NULL, /* bus_ops */
NULL, /* power */
audiohd_quiesce, /* quiesce */
};
static struct modldrv audiohd_modldrv = {
&mod_driverops, /* drv_modops */
"AudioHD", /* linkinfo */
&audiohd_dev_ops, /* dev_ops */
};
static struct modlinkage modlinkage = {
{ &audiohd_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
/*
* Audio routines
*/
static int
audiohd_engine_format(void *arg)
{
return (AUDIO_FORMAT_S16_LE);
}
static int
audiohd_engine_channels(void *arg)
{
}
static int
audiohd_engine_rate(void *arg)
{
return (48000);
}
/*
* get the max channels the hardware supported
*/
static void
{
int i;
maxp = 2;
for (i = 0; i < AUDIOHD_MAX_ASSOC; i++) {
assoc = i;
}
}
/* for record, support stereo so far */
}
static void
{
int i;
/* enable SPDIF output */
0);
ctrl |= AUDIOHD_SPDIF_ON;
(void) audioha_codec_verb_get(
ctrl8);
}
}
/* two channels supported */
(void) audioha_codec_verb_get(
(void) audioha_codec_4bit_verb_get(
AUDIOHD_FMT_PCM << 4 |
/* multichannel supported */
} else {
switch (color) {
case AUDIOHD_PIN_BLACK:
break;
case AUDIOHD_PIN_ORANGE:
nchann = 2;
break;
case AUDIOHD_PIN_GREY:
nchann = 4;
break;
case AUDIOHD_PIN_GREEN:
nchann = 0;
break;
default:
nchann = 0;
break;
}
(void) audioha_codec_verb_get(statep,
nchann);
(void) audioha_codec_4bit_verb_get(
AUDIOHD_FMT_PCM << 4 |
}
}
static void
{
int i;
/*
* Since there is no SPDIF input device available for test,
* we will use this code in the future to support SPDIF input
*/
#if 0
0);
ctrl |= AUDIOHD_SPDIF_ON;
(void) audioha_codec_verb_get(
ctrl8);
}
#endif
}
}
}
(void) audioha_codec_verb_get(statep,
(void) audioha_codec_4bit_verb_get(statep,
}
static void
{
int i;
if (!path)
continue;
case PLAY:
break;
case RECORD:
break;
default:
break;
}
}
}
static int
{
int i;
/* stop stream */
bTmp &= ~AUDIOHD_REG_RIRBSIZE;
/* wait 40us for stream to stop as HD spec */
drv_usecwait(40);
/* reset stream */
for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
/* Empirical testing time, which works well */
drv_usecwait(50);
if (bTmp)
break;
}
if (!bTmp) {
return (AUDIO_FAILURE);
}
/* Empirical testing time, which works well */
drv_usecwait(300);
/* exit reset stream */
bTmp &= ~AUDIOHDR_SD_CTL_SRST;
for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
/* Empircal testing time */
drv_usecwait(50);
if (!bTmp)
break;
}
if (bTmp) {
"Failed to exit reset state for"
return (AUDIO_FAILURE);
}
AUDIOHD_BDLE_NUMS - 1);
/* clear status */
/* set stream tag */
return (AUDIO_SUCCESS);
}
static int
{
(void) audiohd_reset_port(port);
return (0);
}
static void
{
/* if suspended, then do nothing else */
return;
}
/* Enable interrupt and start DMA */
}
static void
{
/* if suspended, then do nothing else */
return;
}
}
static int
audiohd_engine_start(void *arg)
{
}
return (0);
}
static void
audiohd_engine_stop(void *arg)
{
}
}
static void
{
int pos;
pos &= AUDIOHD_POS_MASK;
else {
len &= AUDIOHD_POS_MASK;
}
}
static uint64_t
audiohd_engine_count(void *arg)
{
return (val);
}
static void
audiohd_engine_close(void *arg)
{
}
static void
{
}
static size_t
audiohd_engine_qlen(void *arg)
{
}
AUDIO_ENGINE_VERSION, /* version number */
};
static int
{
return (0);
}
static void
{
int i;
audiohd_widget_t *w;
if (statep->soft_volume)
return;
continue;
/* use the DACs to adjust the volume */
maxgain = w->outamp_cap &
maxgain >>= AUDIOHD_GAIN_OFF;
if (w->outamp_cap) {
(void) audioha_codec_4bit_verb_get(statep,
wid,
(void) audioha_codec_4bit_verb_get(statep,
wid,
}
}
}
static void
{
uint8_t l, r;
int gain;
if (val == 0) {
(void) audioha_codec_4bit_verb_get(
return;
}
r = (val & 0xff);
(void) audioha_codec_4bit_verb_get(statep,
tmp);
(void) audioha_codec_4bit_verb_get(statep,
tmp);
(void) audioha_codec_4bit_verb_get(
gain);
(void) audioha_codec_4bit_verb_get(
gain);
}
}
static void
{
int i, j;
switch (type) {
case DTYPE_LINEOUT:
return;
break;
case DTYPE_SPEAKER:
return;
break;
case DTYPE_HP_OUT:
return;
break;
case DTYPE_CD:
return;
break;
case DTYPE_LINE_IN:
return;
break;
case DTYPE_MIC_IN:
return;
break;
}
if (!path)
continue;
}
}
}
}
static void
{
int i, j;
uint8_t l, r;
switch (color) {
case AUDIOHD_PIN_BLACK:
return;
break;
case AUDIOHD_PIN_ORANGE:
return;
return;
val = (l << 8) | r;
break;
case AUDIOHD_PIN_GREY:
return;
break;
}
if (!path)
continue;
}
}
}
}
static int
{
int i, j;
continue;
case DTYPE_LINE_IN:
case DTYPE_MIC_IN:
case DTYPE_CD:
}
}
break;
default:
break;
}
break;
}
return (DDI_SUCCESS);
}
static void
{
int i, k;
uint8_t l, r;
r = (gain & 0xff);
(void) audioha_codec_4bit_verb_get(
(void) audioha_codec_4bit_verb_get(
(void) audioha_codec_4bit_verb_get(
(void) audioha_codec_4bit_verb_get(
}
}
}
}
static void
{
int i, j;
audiohd_widget_t *w;
return;
continue;
}
}
}
static void
{
}
static void
{
}
static void
{
(void) audiohd_set_input_pin(statep);
}
static int
{
val &= 0xff;
if (val > 100)
return (EINVAL);
return (0);
}
static int
{
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
val &= 0xff;
if (val > 100)
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
val &= 0xff;
if (val > 100)
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static int
{
uint8_t l, r;
if (val & ~0xffff)
return (EINVAL);
r = (val & 0xff);
if ((l > 100) || (r > 100))
return (EINVAL);
return (0);
}
static audiohd_ctrl_t *
{
switch (num) {
case CTL_VOLUME:
desc.acd_minvalue = 0;
break;
case CTL_FRONT:
desc.acd_minvalue = 0;
break;
case CTL_SPEAKER:
desc.acd_minvalue = 0;
break;
case CTL_HEADPHONE:
desc.acd_minvalue = 0;
break;
case CTL_REAR:
desc.acd_minvalue = 0;
break;
case CTL_CENTER:
desc.acd_minvalue = 0;
break;
case CTL_SURROUND:
desc.acd_minvalue = 0;
break;
case CTL_LFE:
desc.acd_minvalue = 0;
break;
case CTL_LINEIN:
desc.acd_minvalue = 0;
break;
case CTL_MIC:
desc.acd_minvalue = 0;
break;
case CTL_CD:
desc.acd_minvalue = 0;
fn = audiohd_set_cd;
break;
case CTL_MONGAIN:
desc.acd_minvalue = 0;
break;
case CTL_RECSRC:
for (int i = 0; audiohd_dtypes[i]; i++) {
}
break;
}
return (pc);
}
static void
{
return;
}
static void
{
int i;
for (i = 0; i < CTRL_NUM; i++) {
}
}
static int
{
int i, j;
audiohd_widget_t *widget, *w;
"Unable to allocate %s control", #ID); \
return (DDI_FAILURE); \
}
continue;
/*
* Firstly we check if all the DACs on the play paths
* have amplifiers. If any of them doesn't have, we just use
* the soft volume control to adjust the PCM volume.
*/
if (!w->outamp_cap) {
break;
}
}
/*
* if all the DACs on the play paths have the amplifiers, we use DACs'
* amplifiers to adjust volume.
*/
if (!statep->soft_volume) {
}
/* allocate other controls */
if (!path)
continue;
}
if (clr == AUDIOHD_PIN_BLACK &&
} else if (clr == AUDIOHD_PIN_ORANGE) {
} else if (clr == AUDIOHD_PIN_GREY) {
}
}
}
if (!statep->monitor_unsupported) {
ADD_CTRL(CTL_MONGAIN, 0);
}
return (DDI_SUCCESS);
}
/*
* quiesce(9E) entry point.
*
* This function is called when the system is single-threaded at high
* PIL with preemption disabled. Therefore, this function must not be
* blocked.
*
* This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
* DDI_FAILURE indicates an error condition and should almost never happen.
*/
static int
{
return (DDI_SUCCESS);
}
/*
* audiohd_init_state()
*
* Description
* This routine initailizes soft state of driver instance,
* also, it requests an interrupt cookie and initializes
* mutex for soft state.
*/
/*ARGSUSED*/
static int
{
return (AUDIO_FAILURE);
}
/* set device information */
DDI_SUCCESS) {
"cannot get iblock cookie");
goto error;
}
statep->hda_rirb_rp = 0;
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audiohd_init_state() */
/*
* audiohd_init_pci()
*
* Description
* enable driver to access PCI configure space and memory
* I/O space.
*/
static int
{
"pci config mapping failed");
goto err_init_pci_exit1;
}
"memory I/O mapping failed");
goto err_init_pci_exit2;
}
/*
* HD audio control uses memory I/O only, enable it here.
*/
switch (vid) {
case AUDIOHD_VID_INTEL:
/*
* Currently, Intel (G)MCH and ICHx chipsets support PCI
* Express QoS. It implemenets two VCs(virtual channels)
* and allows OS software to map 8 traffic classes to the
* two VCs. Some BIOSes initialize HD audio hardware to
* use TC7 (traffic class 7) and to map TC7 to VC1 as Intel
* recommended. However, solaris doesn't support PCI express
* QoS yet. As a result, this driver can not work for those
* hardware without touching PCI express control registers.
* always enabled and TC0 is always mapped to VC0) for all
* Intel HD audio controllers.
*/
break;
case AUDIOHD_VID_ATI:
/*
* Refer to ATI SB450 datesheet. We set snoop for SB450
* like hardware.
*/
break;
/*
* Refer to the datasheet, we set snoop for NVIDIA
* like hardware
*/
case AUDIOHD_VID_NVIDIA:
break;
default:
break;
}
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audiohd_init_pci() */
/*
* audiohd_fini_pci()
*
* Description
* Release mapping for PCI configure space.
*/
static void
{
}
}
} /* audiohd_fini_pci() */
/*
* audiohd_stop_dma()
*
* Description
* Stop all DMA behaviors of controllers, for command I/O
* and each audio stream.
*/
static void
{
int i;
for (i = 0; i < statep->hda_streams_nums; i++) {
}
/* wait 40us for stream DMA to stop */
drv_usecwait(40);
} /* audiohd_stop_dma() */
/*
* audiohd_reset_controller()
*
* Description:
* This routine is just used to reset controller and
* CODEC as well by HW reset bit in global control
* register of HD controller.
*/
static int
{
int i;
/* Reset Status register but preserve the first bit */
/* reset controller */
gctl &= ~AUDIOHDR_GCTL_CRST;
for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
/* Empirical testing time: 150 */
drv_usecwait(150);
if ((gctl & AUDIOHDR_GCTL_CRST) == 0)
break;
}
if ((gctl & AUDIOHDR_GCTL_CRST) != 0) {
"failed to enter reset state");
return (AUDIO_FAILURE);
}
/* Empirical testing time:300 */
drv_usecwait(300);
/* exit reset state */
for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
/* Empirical testing time: 150, which works well */
drv_usecwait(150);
if (gctl & AUDIOHDR_GCTL_CRST)
break;
}
if ((gctl & AUDIOHDR_GCTL_CRST) == 0) {
"failed to exit reset state");
return (AUDIO_FAILURE);
}
/* HD spec requires to wait 250us at least. we use 500us */
drv_usecwait(500);
/* enable unsolicited response */
return (AUDIO_SUCCESS);
} /* audiohd_reset_controller() */
/*
* audiohd_alloc_dma_mem()
*
* Description:
* This is an utility routine. It is used to allocate DMA
* memory.
*/
static int
{
"ddi_dma_alloc_handle failed");
goto error_alloc_dma_exit1;
}
"ddi_dma_mem_alloc failed");
goto error_alloc_dma_exit2;
}
"ddi_dma_addr_bind_handle failed");
goto error_alloc_dma_exit3;
}
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audiohd_alloc_dma_mem() */
/*
* audiohd_release_dma_mem()
*
* Description:
* Release DMA memory.
*/
static void
{
}
}
}
} /* audiohd_release_dma_mem() */
/*
* audiohd_reinit_hda()
*
* Description:
* This routine is used to re-initialize HD controller and codec.
*/
static int
{
/* set PCI configure space in case it's not restored OK */
/* reset controller */
return (AUDIO_FAILURE);
/* Initialize controller RIRB */
/* Initialize controller CORB */
return (AUDIO_SUCCESS);
} /* audiohd_reinit_hda */
/*
* audiohd_init_controller()
*
* Description:
* This routine is used to initialize HD controller. It
* list and cylic data buffer for both play and record
* stream.
*/
static int
{
int retval;
DMA_ATTR_V0, /* version */
0, /* addr_lo */
0xffffffffffffffffULL, /* addr_hi */
0x00000000ffffffffULL, /* count_max */
128, /* 128-byte alignment as HD spec */
0xfff, /* burstsize */
1, /* minxfer */
0xffffffff, /* maxxfer */
0xffffffff, /* seg */
1, /* sgllen */
1, /* granular */
0 /* flags */
};
/*
* If the device doesn't support 64-bit DMA, we should not
* allocate DMA memory from 4G above
*/
if ((gcap & AUDIOHDR_GCAP_64OK) == 0)
/* stop all dma before starting to reset controller */
return (AUDIO_FAILURE);
/* check codec */
if (! statep->hda_codec_mask) {
"no codec exists");
goto err_init_ctlr_exit1;
}
/* allocate DMA for CORB */
if (retval != AUDIO_SUCCESS) {
"failed to alloc DMA for CORB");
goto err_init_ctlr_exit1;
}
/* allocate DMA for RIRB */
if (retval != AUDIO_SUCCESS) {
"failed to alloc DMA for RIRB");
goto err_init_ctlr_exit2;
}
/* Initialize RIRB */
/* initialize CORB */
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* audiohd_init_controller() */
/*
* audiohd_fini_controller()
*
* Description:
* Releases DMA memory allocated in audiohd_init_controller()
*/
static void
{
} /* audiohd_fini_controller() */
/*
* audiohd_get_conns_from_entry()
*
* Description:
* Get connection list from every entry for a widget
*/
static void
{
int i, k, num;
for (i = 0; i < prop->conns_per_entry &&
break;
}
} else {
1; k <= input_wid; k++) {
break;
} else {
}
}
}
} else {
}
}
}
/*
* audiohd_get_conns()
*
* Description:
* Get all connection list for a widget. The connection list is used for
* build output path, input path, and monitor path
*/
static void
{
int i;
} else {
}
/*
* This should not happen since the ConnectionList bit of
* widget capabilities already told us that this widget
* has a connection list
*/
return;
}
return;
}
return;
}
}
}
/*
* Read PinCapabilities & default configuration
*/
static void
{
/*
* If the pin has no physical connection for port,
* we won't link it to pin linkage list ???
*/
}
/* bit 4:3 are reserved, read-modify-write is needed */
if (vrefbits & AUDIOHD_PIN_VREF_L1)
else if (vrefbits & AUDIOHD_PIN_VREF_L2)
else if (vrefbits & AUDIOHD_PIN_VREF_L3)
else
/* enable the unsolicited response of the pin */
}
/* accommodate all the pins in a link list sorted by assoc and seq */
} else {
while (p) {
break;
break;
prev = p;
p = p->next;
}
if (prev) {
} else {
}
}
} /* audiohd_get_pin_config() */
/*
* audiohd_create_widgets()
*
* Description:
* All widgets are created and stored in an array of codec
*/
static int
{
widget = (audiohd_widget_t *)
/* if there's connection list */
if (widcap & AUDIOHD_WIDCAP_CONNLIST) {
}
/* if power control, power it up to D0 state */
if (widcap & AUDIOHD_WIDCAP_PWRCTRL) {
}
/*
* if this widget has format override, we read it.
* Otherwise, it uses the format of audio function.
*/
if (widcap & AUDIOHD_WIDCAP_FMT_OVRIDE) {
widget->pcm_format =
} else {
}
/*
* Input amplifier. Has the widget input amplifier ?
*/
if (widcap & AUDIOHD_WIDCAP_INAMP) {
/*
* if overrided bit is 0, use the default
* amplifier of audio function as HD spec.
* Otherwise, we read it.
*/
if ((widcap & AUDIOHD_WIDCAP_AMP_OVRIDE) == 0)
else
} else {
}
/*
* output amplifier. Has this widget output amplifier ?
*/
if (widcap & AUDIOHD_WIDCAP_OUTAMP) {
if ((widcap & AUDIOHD_WIDCAP_AMP_OVRIDE) == 0)
else
widget->outamp_cap =
} else {
widget->outamp_cap = 0;
}
switch (type) {
case WTYPE_AUDIO_OUT:
case WTYPE_AUDIO_IN:
case WTYPE_AUDIO_MIX:
case WTYPE_AUDIO_SEL:
case WTYPE_VENDOR:
case WTYPE_POWER:
case WTYPE_VOL_KNOB:
break;
case WTYPE_PIN:
break;
case WTYPE_BEEP:
break;
default:
break;
}
}
return (DDI_SUCCESS);
} /* audiohd_create_widgets() */
/*
* audiohd_destroy_widgets()
*/
static void
{
for (int i = 0; i < AUDIOHD_MAX_WIDGET; i++) {
}
}
} /* audiohd_destroy_widgets() */
static void
{
char buf[256];
case 0x10ec0260:
break;
case 0x10ec0262:
break;
case 0x10ec0268:
break;
case 0x10ec0662:
break;
case 0x10ec861:
break;
case 0x10ec0862:
break;
case 0x10ec0880:
break;
case 0x10ec0882:
break;
case 0x10ec0883:
break;
case 0x10ec0885:
break;
case 0x10ec0888:
break;
case 0x13f69880:
break;
case 0x434d4980:
break;
case 0x11d41981:
"Analog Devices HD codec: AD1981");
break;
case 0x11d41983:
"Analog Devices HD codec: AD1983");
break;
case 0x11d41984:
"Analog Devices HD codec: AD1984");
break;
case 0x11d41986:
"Analog Devices HD codec: AD1986A");
break;
case 0x11d41988:
"Analog Devices HD codec: AD1988A");
break;
case 0x11d4198b:
"Analog Devices HD codec: AD1988B");
break;
case 0x83847690:
"Sigmatel HD codec: STAC9200");
break;
case 0x838476a0:
"Sigmatel HD codec: STAC9205");
break;
case 0x838476a1:
"Sigmatel HD codec: STAC9205D");
break;
case 0x838476a2:
"Sigmatel HD codec: STAC9204");
break;
case 0x838476a3:
"Sigmatel HD codec: STAC9204D");
break;
case 0x83847880:
"Sigmatel HD codec: STAC9220 A1");
break;
case 0x83847882:
"Sigmatel HD codec: STAC9220 A2");
break;
case 0x83847680:
"Sigmatel HD codec: STAC9221 A1");
break;
case 0x83847681:
"Sigmatel HD codec: STAC9220 D");
break;
case 0x83847682:
"Sigmatel HD codec: STAC9221");
break;
case 0x83847683:
"Sigmatel HD codec: STAC9221D");
break;
case 0x83847610:
"Sigmatel HD codec: STAC9230XN");
break;
case 0x83847611:
"Sigmatel HD codec: STAC9230DN");
break;
case 0x83847612:
"Sigmatel HD codec: STAC9230XT");
break;
case 0x83847613:
"Sigmatel HD codec: STAC9230DT");
break;
case 0x83847614:
"Sigmatel HD codec: STAC9229X");
break;
case 0x83847615:
"Sigmatel HD codec: STAC9229D");
break;
case 0x83847616:
"Sigmatel HD codec: STAC9228X");
break;
case 0x83847617:
"Sigmatel HD codec: STAC9228D");
break;
case 0x83847618:
"Sigmatel HD codec: STAC9227X");
break;
case 0x83847619:
"Sigmatel HD codec: STAC9227D");
break;
case 0x838476a4:
"Sigmatel HD codec: STAC9255");
break;
case 0x838476a5:
"Sigmatel HD codec: STAC9255D");
break;
case 0x838476a6:
"Sigmatel HD codec: STAC9254");
break;
case 0x838476a7:
"Sigmatel HD codec: STAC9254D");
break;
case 0x83847620:
"Sigmatel HD codec: STAC9274");
break;
case 0x83847621:
"Sigmatel HD codec: STAC9274D");
break;
case 0x83847622:
"Sigmatel HD codec: STAC9273X");
break;
case 0x83847623:
"Sigmatel HD codec: STAC9273D");
break;
case 0x83847624:
"Sigmatel HD codec: STAC9272X");
break;
case 0x83847625:
"Sigmatel HD codec: STAC9272D");
break;
case 0x83847626:
"Sigmatel HD codec: STAC9271X");
break;
case 0x83847627:
"Sigmatel HD codec: STAC9271D");
break;
case 0x83847628:
"Sigmatel HD codec: STAC9274X5NH");
break;
case 0x83847629:
"Sigmatel HD codec: STAC9274D5NH");
break;
case 0x83847662:
"Sigmatel HD codec: STAC9872AK");
break;
case 0x83847664:
"Sigmatel HD codec: STAC9872K");
break;
default:
"Unkown HD codec");
break;
}
}
/*
* audiohd_create_codec()
*
* Description:
* Searching for supported CODEC. If find, allocate memory
* to hold codec structure.
*/
static int
{
uint32_t i, j;
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
if ((mask & (1 << i)) == 0)
continue;
sizeof (hda_codec_t), KM_SLEEP);
continue;
}
/*
* Assume that each codec has just one audio function group
*/
if ((type & AUDIOHD_CODEC_TYPE_MASK) ==
break;
}
}
continue;
}
/* work around for Sony VAIO laptop with specific codec */
/*
* GPIO controls which are laptop specific workarounds
* and might be changed. Some laptops use GPIO,
* so we need to enable and set the GPIO correctly.
*/
}
/* power-up audio function group */
/* subsystem id is attached to funtion group */
/*
* We output the codec information to syslog
*/
(void) audiohd_create_widgets(codec);
}
return (AUDIO_SUCCESS);
} /* audiohd_create_codec() */
/*
* audiohd_destroy_codec()
*
* Description:
* destroy codec structure, and release its memory
*/
static void
{
int i;
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
/*
* free pins
*/
while (pin) {
}
}
}
} /* audiohd_destroy_codec() */
/*
* audiohd_find_dac()
* Description:
* Find a dac for a output path. Then the play data can be sent to the out
* put pin through the output path.
*
* Arguments:
* hda_codec_t *codec where the dac widget exists
* wid_t wid the no. of a widget
* int mixer whether the path need mixer or not
* int *mixernum the total of mixer in the output path
* int exclusive an exclusive path or share path
* int depth the depth of search
*
* Return:
* 1) wid of the first shared widget in the path from
* pin to DAC if exclusive is 0;
* 2) wid of DAC widget;
* 3) 0 if no path
*/
static wid_t
{
if (depth > AUDIOHD_MAX_DEPTH)
return (uint32_t)(AUDIO_FAILURE);
return (uint32_t)(AUDIO_FAILURE);
/*
* If exclusive is true, we try to find a path which doesn't
* share any widget with other paths.
*/
if (exclusive) {
return (uint32_t)(AUDIO_FAILURE);
} else {
return (wid);
}
case WTYPE_AUDIO_OUT:
/* We need mixer widget, but the the mixer num is 0, failed */
return (uint32_t)(AUDIO_FAILURE);
widget->out_weight++;
break;
case WTYPE_AUDIO_MIX:
case WTYPE_AUDIO_SEL:
(*mixernum)++;
widget->avail_conn[i],
}
widget->out_weight++;
/* return when found a path */
return (wdac);
}
}
default:
break;
}
return (wdac);
} /* audiohd_find_dac() */
/*
* audiohd_do_build_output_path()
*
* Description:
* Search an output path for each pin in the codec.
* Arguments:
* hda_codec_t *codec where the output path exists
* int mixer wheter the path needs mixer widget
* int *mnum total of mixer widget in the path
* int exclusive an exclusive path or shared path
* int depth search depth
*/
static void
{
int i;
continue;
continue;
continue;
continue;
/*
* If a dac found, the return value is the wid of the
* widget on the path, or the return value is
* AUDIO_FAILURE
*/
depth);
/*
* A dac was not found
*/
continue;
path = (audiohd_path_t *)
kmem_zalloc(sizeof (audiohd_path_t),
KM_SLEEP);
widget->out_weight++;
break;
}
}
} /* audiohd_do_build_output_path() */
/*
* audiohd_build_output_path()
*
* Description:
* Build the output path in the codec for every pin.
* First we try to search output path with mixer widget exclusively
* Then we try to search shared output path with mixer widget.
* Then we try to search output path without mixer widget exclusively.
* At last we try to search shared ouput path for the remained pins
*/
static void
{
int mnum = 0;
/* work around for hp mini 1000 laptop */
mixer_allow = 0;
/* search an exclusive mixer widget path. This is preferred */
/* search a shared mixer widget path for the remained pins */
/* search an exclusive widget path without mixer for the remained pin */
/* search a shared widget path without mixer for the remained pin */
} /* audiohd_build_output_path */
/*
* audiohd_build_output_amp
*
* Description:
* Find the gain control and mute control widget
*/
static void
{
int weight;
int i, j;
continue;
/*
* search a node which can mute this pin while
* the mute functionality doesn't effect other
* pins.
*/
while (widget) {
if (widget->outamp_cap &
break;
}
break;
}
break;
break;
}
/*
* We select the wid which has maxium gain range in
* the output path. Meanwhile, the gain controlling
* of this node doesn't effect other pins if this
* output stream has multiple pins.
*/
gain = 0;
while (widget) {
}
}
break;
break;
}
}
/*
* if this stream has multiple pins, we try to find
* a mute & gain-controlling nodes which can effect
* all output pins of this stream to be used for the
* whole stream
*/
} else {
while (w && w->out_weight != weight) {
}
/* find mute controlling node for this stream */
widget = w;
while (widget) {
if (widget->outamp_cap &
break;
}
break;
}
break;
}
/* find volume controlling node for this stream */
gain = 0;
widget = w;
while (widget) {
}
}
break;
}
}
}
} /* audiohd_build_output_amp */
/*
* audiohd_finish_output_path()
*
* Description:
* Enable the widgets on the output path
*/
static void
{
int i, j;
continue;
{
}
/* If this pin has external amplifier, enable it */
if (widget->outamp_cap) {
(void) audioha_codec_4bit_verb_get(statep,
}
while (widget) {
/*
* Set all amplifiers in this path to
* the maximum
* volume and unmute them.
*/
if (widget->outamp_cap) {
(void) audioha_codec_4bit_verb_get(
}
(void) audioha_codec_4bit_verb_get(
}
break;
/*
* Accoding to HD spec, mixer doesn't support
* "select connection"
*/
(void) audioha_codec_verb_get(statep,
wid,
}
}
}
} /* audiohd_finish_output_path() */
/*
* audiohd_find_input_pins()
*
* Description:
* execept the first one, only the first input/connection of those
* widgets will be used by our driver, that means, we ignore other
*/
static int
{
int retval = -1;
int num, i;
if (depth > AUDIOHD_MAX_DEPTH)
return (uint32_t)(AUDIO_FAILURE);
return (uint32_t)(AUDIO_FAILURE);
/* we don't share widgets */
return (uint32_t)(AUDIO_FAILURE);
case WTYPE_PIN:
if (pin->no_phys_conn)
return (uint32_t)(AUDIO_FAILURE);
/* enable the pins' input capability */
}
case DTYPE_CD:
case DTYPE_LINE_IN:
case DTYPE_MIC_IN:
case DTYPE_AUX:
return (AUDIO_SUCCESS);
}
break;
case WTYPE_AUDIO_MIX:
case WTYPE_AUDIO_SEL:
/*
* If the sum widget has only one input, we don't
* consider it as a real sum widget.
*/
widget->avail_conn[0],
if (retval != AUDIO_FAILURE) {
}
break;
}
if (allowmixer) {
/*
* This is a real sum widget, we will reject
* other real sum widget when we find more in
* the following path-searching.
*/
path);
if (retval != AUDIO_FAILURE) {
widget->path_flags |=
}
}
}
/* return SUCCESS if we found at least one input path */
} else {
/*
* We had already found a real sum before this one since
* allowmixer is 0.
*/
path);
if (retval != AUDIO_FAILURE) {
break;
}
}
}
break;
default:
break;
}
return (retval);
} /* audiohd_find_input_pins */
/*
* audiohd_build_input_path()
*
* Description:
* Find input path for the codec
*/
static void
{
int i;
int retval;
/* check if it is an ADC widget */
continue;
KM_SLEEP);
else
/*
* Is there any ADC widget which has more than one input ??
* I don't believe. Anyway, we carefully deal with this. But
* if hardware vendors embed a selector in a ADC, we just use
* the first available input, which has connection to input pin
* widget. Because selector cannot perform mixer functionality,
* and we just permit one selector or mixer in a recording path,
* if we use the selector embedded in ADC,we cannot use possible
* mixer during path searching.
*/
if (retval == AUDIO_SUCCESS) {
break;
}
}
}
if (path)
} /* audiohd_build_input_path */
/*
* audiohd_build_input_amp()
*
* Description:
* Find gain and mute control widgets on the input path
*/
static void
{
int i, j;
int weight;
continue;
/*
* Search node which has mute functionality for
* the whole input path
*/
w = wadc;
while (w) {
if (w->outamp_cap & AUDIOHDC_AMP_CAP_MUTE_CAP) {
break;
}
if ((w->inamp_cap & AUDIOHDC_AMP_CAP_MUTE_CAP) &&
break;
}
if (w->selconn == AUDIOHD_NULL_CONN)
break;
break;
}
/*
* Search a node for amplifier adjusting for the whole
* input path
*/
w = wadc;
gain = 0;
while (w) {
}
}
if (w->selconn == AUDIOHD_NULL_CONN)
break;
}
/*
* controlling is shared by the whole path and pin
*/
continue;
}
/*
* For multi-pin device, there must be a selector
* or mixer along the input path, and the sum_wid
* is the widget's node id.
*/
/* find node for mute */
} else {
while (w) {
if (w->outamp_cap &
break;
}
if (w->inamp_cap &
break;
}
if (w->selconn == AUDIOHD_NULL_CONN)
break;
}
}
/* find node for amp controlling */
while (w) {
gain = (w->outamp_cap &
}
}
if (w->selconn == AUDIOHD_NULL_CONN)
break;
}
}
}
} /* audiohd_build_input_amp() */
/*
* audiohd_finish_input_path()
*
* Description:
* Enable the widgets on the input path
*/
static void
{
audiohd_widget_t *w, *wsum;
int i, j;
continue;
w->wid_wid,
if (w->outamp_cap) {
(void) audioha_codec_4bit_verb_get(statep,
}
if (w->inamp_cap) {
(void) audioha_codec_4bit_verb_get(statep,
(w->selconn <<
}
}
/*
* After exiting from the above loop, the widget pointed
* is a pin widget, we already finish "select connection"
* operation for the whole path.
*/
continue;
/*
* deal with multi-pin input devices.
*/
continue;
if (wsum->outamp_cap) {
(void) audioha_codec_4bit_verb_get(statep,
}
(void) audioha_codec_4bit_verb_get(statep,
(path->sum_selconn[j] <<
}
path->sum_selconn[j]);
}
if ((w->type != WTYPE_AUDIO_MIX) &&
(w->nconns > 1))
(void) audioha_codec_verb_get(statep,
w->selconn);
if (w->outamp_cap) {
(void) audioha_codec_4bit_verb_get(
w->wid_wid,
}
if (w->inamp_cap) {
(void) audioha_codec_4bit_verb_get(
w->wid_wid,
(w->selconn <<
}
}
}
} /* end of istream loop */
} /* audiohd_finish_input_path */
/*
* audiohd_find_inpin_for_monitor()
*
* Description:
* Find input pin for monitor path.
*
* Arguments:
* hda_codec_t *codec where the monitor path exists
* audiohd_ostream_t *ostream output ostream
* wid_t id no. of widget being searched
* int mixer share or not
*/
static int
{
int i, find = 0;
return (uint32_t)(AUDIO_FAILURE);
if (pin->no_phys_conn)
return (uint32_t)(AUDIO_FAILURE);
case DTYPE_SPDIF_IN:
return (AUDIO_SUCCESS);
case DTYPE_CD:
return (AUDIO_SUCCESS);
case DTYPE_LINE_IN:
return (AUDIO_SUCCESS);
case DTYPE_MIC_IN:
return (AUDIO_SUCCESS);
case DTYPE_AUX:
return (AUDIO_SUCCESS);
default:
return (uint32_t)(AUDIO_FAILURE);
}
}
/* the widget has been visited and can't be directed to input pin */
return (uint32_t)(AUDIO_FAILURE);
}
/* the widget has been used by the monitor path, and we can share it */
if (mixer)
return (AUDIO_SUCCESS);
else
return (uint32_t)(AUDIO_FAILURE);
}
case WTYPE_AUDIO_MIX:
continue;
path,
find = 1;
}
}
break;
case WTYPE_AUDIO_SEL:
continue;
path,
widget->avail_conn[i],
mixer) ==
return (AUDIO_SUCCESS);
}
}
default:
break;
}
if (!find) {
return (uint32_t)(AUDIO_FAILURE);
}
else
return (AUDIO_SUCCESS);
} /* audiohd_find_inpin_for_monitor */
/*
* audiohd_build_monitor_path()
*
* Description:
* The functionality of mixer is to mix inputs, such as CD-IN, MIC,
* Line-in, etc, with DAC outputs, so as to minitor what is being
* recorded and implement "What you hear is what you get". However,
* this functionality are really hardware-dependent: the inputs
* must be directed to MIXER if they can be directed to ADC as
* recording sources.
*/
static void
{
int i, j, k, l, find;
int mixernum = 0;
continue;
l = 0;
while (widget) {
while (widget &&
break;
wid =
}
/*
* No mixer in this output path, we cannot build
* mixer path for this path, skip it,
* and continue
* for next output path.
*/
break;
}
mixernum++;
/*
* this connection must be routined
* to DAC instead of an input pin
* widget, we needn't waste time for
* it
*/
continue;
find = 0;
path,
widget->avail_conn[k], 0) ==
k;
widget->path_flags |=
find = 1;
} else if (
path,
k;
widget->path_flags |=
find = 1;
}
}
/*
* we needn't check widget->selconn here
* since this
* widget is a selector or mixer, it cannot
* be NULL connection.
*/
if (!find) {
widget->path_flags |=
}
l++;
}
}
}
if (mixernum == 0)
else
} /* audiohd_build_monitor_path */
/*
* audiohd_do_finish_monitor_path
*
* Description:
* Enable the widgets on the monitor path
*/
static void
{
audiohd_widget_t *w;
int i;
int share = 0;
return;
share = 1;
}
}
!share) {
}
return;
}
}
} /* audiohd_do_finish_monitor_path */
/*
* audiohd_finish_monitor_path
*
* Description:
* Enable the monitor path for every ostream path
*/
static void
{
int i, j, k;
continue;
if (wid == 0) {
continue;
}
}
}
}
} /* audiohd_finish_monitor_path */
/*
* audiohd_do_build_monit_amp()
*
* Description:
* Search for the gain control widget for the monitor path
*/
static void
{
audiohd_widget_t *w = widget;
int i;
if (!w ||
!w->used ||
(w->path_flags & AUDIOHD_PATH_ADC))
return;
if (!(w->path_flags & AUDIOHD_PATH_DAC)) {
if (gain) {
return;
}
if (gain) {
return;
}
}
for (i = 0; i < w->used; i++) {
}
} /* audiohd_do_build_monitor_amp() */
/*
* audiohd_build_monitor_amp()
*
* Description:
* Search gain control widget for every ostream monitor
*/
static void
{
audiohd_widget_t *widget, *w;
int i, j, k;
continue;
if (!wid)
continue;
widget);
}
}
}
}
/*
* audiohd_build_path()
*
* Description:
* Here we build the output, input, monitor path.
* And also enable the path in default.
* Search for the gain and mute control for the path
*/
static void
{
int i;
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
}
}
} /* audiohd_build_path */
/*
* audiohd_allocate_port()
*/
static int
{
int i, j;
int dir;
unsigned caps;
char *prop;
int rc;
DMA_ATTR_V0, /* version */
0, /* addr_lo */
0xffffffffffffffffULL, /* addr_hi */
0x00000000ffffffffULL, /* count_max */
128, /* 128-byte alignment as HD spec */
0xfff, /* burstsize */
1, /* minxfer */
0xffffffff, /* maxxfer */
0xffffffff, /* seg */
1, /* sgllen */
1, /* granular */
0 /* flags */
};
if ((gcap & AUDIOHDR_GCAP_64OK) == 0)
for (i = 0; i < PORT_MAX; i++) {
switch (i) {
case PORT_ADC:
prop = "record-interrupts";
break;
case PORT_DAC:
prop = "play-interrupts";
break;
default:
return (DDI_FAILURE);
}
/* make sure the values are good */
}
(AUDIOHD_FRAGFR_ALIGN - 1);
AUDIOHD_BDLE_BUF_ALIGN - 1) & ~
(AUDIOHD_BDLE_BUF_ALIGN - 1);
/* allocate dma handle */
if (rc != DDI_SUCCESS) {
rc);
goto error_alloc_dma_exit1;
}
/* allocate DMA buffer */
if (rc == DDI_FAILURE) {
goto error_alloc_dma_exit2;
}
/* bind DMA buffer */
"ddi_dma_addr_bind_handle failed: %d", rc);
goto error_alloc_dma_exit3;
}
/*
* now, from here we allocate DMA
* memory for buffer descriptor list.
* we allocate adjacent DMA memory for all DMA engines.
*/
if (rc != DDI_SUCCESS) {
"ddi_dma_alloc_handle(bdlist) failed");
goto error_alloc_dma_exit3;
}
/*
* we allocate all buffer descriptors lists in continuous
* dma memory.
*/
if (rc != DDI_SUCCESS) {
"ddi_dma_mem_alloc(bdlist) failed");
goto error_alloc_dma_exit4;
}
goto error_alloc_dma_exit5;
}
for (j = 0; j < AUDIOHD_BDLE_NUMS; j++) {
entry++;
}
goto error_alloc_dma_exit5;
}
}
return (DDI_SUCCESS);
return (AUDIO_FAILURE);
}
static void
{
int i;
return;
}
for (i = 0; i < PORT_MAX; i++) {
continue;
}
}
}
}
}
}
}
}
}
/*
* audiohd_change_widget_power_state(audiohd_state_t *statep, int off)
* Description:
* This routine is used to change the widget power betwen D0 and D2.
* D0 is fully on; D2 allows the lowest possible power consuming state
* from which it can return to the fully on state: D0.
*/
static void
{
int i;
/* Change power to D2 */
if (off) {
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
if (!codec)
continue;
wid++) {
if (widget->widget_cap &
(void) audioha_codec_verb_get(statep,
}
}
}
/* Change power to D0 */
} else {
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
if (!codec)
continue;
wid++) {
if (widget->widget_cap &
(void) audioha_codec_verb_get(statep,
}
}
}
}
}
/*
* audiohd_restore_path()
* Description:
* This routine is used to restore the path on the codec.
*/
static void
{
int i;
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
if (!codec)
continue;
}
}
/*
* restore_play_and_record()
*/
static void
{
int i;
for (i = 0; i < PORT_MAX; i++) {
continue;
(void) audiohd_reset_port(port);
} else {
}
}
}
/*
* audiohd_reset_pins_ur_cap()
* Description:
* Enable the unsolicited response of the pins which have the unsolicited
* response capability
*/
static void
{
int i;
for (i = 0; i <= AUDIOHD_CODEC_MAX; i++) {
if (!codec)
continue;
while (pin) {
/* enable the unsolicited response of the pin */
if ((widget->widget_cap &
(AUDIOHD_URCAP_MASK) &&
(AUDIOHD_UR_ENABLE_OFF - 1));
(void) audioha_codec_verb_get(statep,
}
}
}
}
static void
{
int i;
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
continue;
/* power-up audio function group */
/* work around for Sony VAIO laptop with specific codec */
/*
* GPIO controls which are laptop specific workarounds
* and might be changed. Some laptops use GPIO,
* so we need to enable and set the GPIO correctly.
*/
}
}
}
/*
* audiohd_resume()
*/
static int
{
/* Restore the hda state */
"hda reinit failed");
return (DDI_SUCCESS);
}
/* reset to enable the capability of unsolicited response for pin */
/* Enable interrupt */
/* clear the unsolicited response interrupt */
/* set widget power to D0 */
return (DDI_SUCCESS);
} /* audiohd_resume */
/*
* audiohd_suspend()
*/
static int
{
/* set widget power to D2 */
/* Disable h/w */
return (DDI_SUCCESS);
} /* audiohd_suspend */
/*
* audiohd_disable_pin()
*/
static int
{
return (AUDIO_SUCCESS);
}
/*
* audiohd_enable_pin()
*/
static int
{
return (AUDIO_SUCCESS);
}
/*
* audiohd_change_speaker_state()
*/
static void
{
int i, j;
continue;
if (on) {
(void) audiohd_enable_pin(
}
}
} else {
(void) audiohd_disable_pin(
}
}
}
}
}
/*
* audiohd_select_mic()
*
* Description:
* This function is used for the recording path which has a selector
* as the sumwidget. We select the external MIC if it is plugged into the
* MIC jack, otherwise the internal integrated MIC is selected.
*/
static void
{
int i, j;
return;
continue;
return;
if (select &&
(void) audioha_codec_verb_get(
path->sum_selconn[j]);
return;
} else if (!select &&
(void) audioha_codec_verb_get(
path->sum_selconn[j]);
return;
}
}
break;
}
}
/*
* If the input istream > 1, we should set the record stream tag
* respectively. All the input streams sharing one tag may make the
* record sound distorted.
*/
continue;
return;
if (select &&
return;
} else if (!select &&
return;
}
}
}
}
}
/*
* audiohd_pin_sense()
*
* Description
*
* When the earphone is plugged into the jack associtated with the pin
* complex, we disable the built in speaker. When the earphone is plugged
* out of the jack, we enable the built in speaker.
*/
static void
{
return;
return;
/* A MIC is plugged in, we select the MIC as input */
return;
}
/* output pin is plugged */
} else {
/*
* A MIC is unplugged, we select the built in MIC
* as input.
*/
return;
}
/* output pin is unplugged */
}
}
/*
* audiohd_intr()
*
* Description
*
*
* Arguments:
* caddr_t arg Pointer to the interrupting device's state
* structure
*
* Returns:
* DDI_INTR_CLAIMED Interrupt claimed and processed
* DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored
*/
static uint_t
{
int i, ret;
return (DDI_INTR_UNCLAIMED);
}
if (status == 0) {
return (DDI_INTR_UNCLAIMED);
}
/*
* unsolicited response from pins, maybe something plugged in or out
* of the jack.
*/
if (status & AUDIOHD_CIS_MASK) {
/* clear the unsolicited response interrupt */
/*
* We have to wait and try several times to make sure the
* unsolicited response is generated by our pins.
* we need to make it work for audiohd spec 0.9, which is
* just a draft version and requires more time to wait.
*/
for (i = 0; i < AUDIOHD_TEST_TIMES; i++) {
&respex);
if ((ret == AUDIO_SUCCESS) &&
(respex & AUDIOHD_RIRB_UR_MASK)) {
/*
* A pin may generate more than one ur rirb,
* we only need handle one of them, and clear
* the other ones
*/
break;
}
}
if ((ret == AUDIO_SUCCESS) &&
(respex & AUDIOHD_RIRB_UR_MASK)) {
}
}
/* stream intr */
for (i = 0; i < statep->hda_streams_nums; i++) {
if ((status & (1<<i)) == 0)
continue;
/* clear intrs */
if (i < statep->hda_input_streams)
else
}
/* update the kernel interrupt statistics */
((kstat_intr_t *)
}
if (do_adc)
if (do_dac)
return (DDI_INTR_CLAIMED);
} /* audiohd_intr() */
/*
* audiohd_disable_intr()
*
* Description:
* Disable all possible interrupts.
*/
static void
{
int i;
for (i = 0; i < statep->hda_streams_nums; i++) {
}
} /* audiohd_disable_intr() */
/*
* audiohd_12bit_verb_to_codec()
*
* Description:
*
*/
static int
{
wptr++;
/* overflow */
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audiohd_12bit_verb_to_codec() */
/*
* audiohd_4bit_verb_to_codec()
*
* Description:
*
*/
static int
{
wptr++;
/* overflow */
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audiohd_4bit_verb_to_codec() */
/*
* audiohd_response_from_codec()
*
* Description:
*
*/
static int
{
return (AUDIO_FAILURE);
}
rptr++;
return (AUDIO_SUCCESS);
} /* audiohd_response_from_codec() */
/*
* audioha_codec_verb_get()
*/
static uint32_t
{
int ret;
int i;
if (ret != AUDIO_SUCCESS) {
return (uint32_t)(-1);
}
/*
* Empirical testing times. 50 times is enough for audiohd spec 1.0.
* But we need to make it work for audiohd spec 0.9, which is just a
* draft version and requires more time to wait.
*/
for (i = 0; i < 500; i++) {
((respex & AUDIOHD_BDLE_RIRB_UNSOLICIT) == 0) &&
(ret == AUDIO_SUCCESS))
break;
/* Empirical testing time, which works well */
drv_usecwait(30);
}
if (ret == AUDIO_SUCCESS) {
return (resp);
}
" response from codec: wid=%d, verb=0x%04x, param=0x%04x",
return ((uint32_t)(-1));
} /* audioha_codec_verb_get() */
/*
* audioha_codec_4bit_verb_get()
*/
static uint32_t
{
int ret;
int i;
if (ret != AUDIO_SUCCESS) {
return (uint32_t)(-1);
}
for (i = 0; i < 500; i++) {
((respex & AUDIOHD_BDLE_RIRB_UNSOLICIT) == 0) &&
(ret == AUDIO_SUCCESS))
break;
/* Empirical testing time, which works well */
drv_usecwait(30);
}
if (ret == AUDIO_SUCCESS) {
return (resp);
}
" response from codec: wid=%d, verb=0x%04x, param=0x%04x",
return ((uint32_t)(-1));
} /* audioha_codec_4bit_verb_get() */