/*
* 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
*/
/*
*/
#include "audiohd.h"
/*
* 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 *);
/*
* 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 void audiohd_finish_output_path(hda_codec_t *);
static int audiohd_reinit_hda(audiohd_state_t *);
static int audiohd_response_from_codec(audiohd_state_t *,
static void audiohd_restore_codec_gpio(audiohd_state_t *);
static void audiohd_change_speaker_state(audiohd_state_t *, int);
static int audiohd_allocate_port(audiohd_state_t *);
static void audiohd_free_port(audiohd_state_t *);
static void audiohd_restore_path(audiohd_state_t *);
static void audiohd_create_controls(audiohd_state_t *);
static void audiohd_get_channels(audiohd_state_t *);
static void audiohd_init_path(audiohd_state_t *);
static void audiohd_del_controls(audiohd_state_t *);
static void audiohd_destroy(audiohd_state_t *);
static void audiohd_beep_on(void *);
static void audiohd_beep_off(void *);
static void audiohd_beep_freq(void *, int);
static void audiohd_build_beep_path(hda_codec_t *);
static void audiohd_build_beep_amp(hda_codec_t *);
static void audiohd_finish_beep_path(hda_codec_t *);
static void audiohd_do_set_beep_volume(audiohd_state_t *,
audiohd_path_t *, uint64_t);
static void audiohd_set_beep_volume(audiohd_state_t *);
static int audiohd_set_beep(void *, uint64_t);
static int audiohd_beep;
static int audiohd_beep_divider;
/* Warlock annotation */
};
static const char *audiohd_dtypes[] = {
AUDIO_PORT_NONE, /* reserved port, don't use */
NULL,
};
{0x1002aa01, "ATI R600 HDMI", 0x0},
{0x10134206, "Cirrus CS4206", 0x0},
{0x10de0002, "nVidia MCP78 HDMI", 0x0},
{0x10de0003, "nVidia MCP78 HDMI", 0x0},
{0x10de0006, "nVidia MCP78 HDMI", 0x0},
{0x10de0007, "nVidia MCP7A HDMI", 0x0},
{0x10ec0268, "Realtek ALC268", 0x0},
{0x10ec0272, "Realtek ALC272", 0x0},
{0x10ec0662, "Realtek ALC662", 0x0},
{0x10ec0663, "Realtek ALC663", 0x0},
{0x10ec0861, "Realtek ALC861", 0x0},
{0x10ec0862, "Realtek ALC862", 0x0},
{0x10ec0880, "Realtek ALC880", 0x0},
{0x10ec0882, "Realtek ALC882", 0x0},
{0x10ec0883, "Realtek ALC883", 0x0},
{0x10ec0885, "Realtek ALC885", 0x0},
{0x11d4194a, "Analog Devices AD1984A", 0x0},
{0x11d41983, "Analog Devices AD1983", 0x0},
{0x11d41984, "Analog Devices AD1984", 0x0},
{0x11d41986, "Analog Devices AD1986A", 0x0},
{0x11d41988, "Analog Devices AD1988A", 0x0},
{0x11d4198b, "Analog Devices AD1988B", 0x0},
{0x13f69880, "CMedia CMI19880", 0x0},
{0x14f15051, "Conexant CX20561", 0x0},
{0x434d4980, "CMedia CMI19880", 0x0},
{0x80862802, "Intel HDMI", 0x0},
{0x83847610, "Sigmatel STAC9230XN", 0x0},
{0x83847611, "Sigmatel STAC9230DN", 0x0},
{0x83847612, "Sigmatel STAC9230XT", 0x0},
{0x83847613, "Sigmatel STAC9230DT", 0x0},
{0x83847614, "Sigmatel STAC9229X", 0x0},
{0x83847615, "Sigmatel STAC9229D", 0x0},
{0x83847616, "Sigmatel STAC9228X", 0x0},
{0x83847617, "Sigmatel STAC9228D", 0x0},
{0x83847618, "Sigmatel STAC9227X", 0x0},
{0x83847619, "Sigmatel STAC9227D", 0x0},
{0x83847620, "Sigmatel STAC9274", 0x0},
{0x83847621, "Sigmatel STAC9274D", 0x0},
{0x83847622, "Sigmatel STAC9273X", 0x0},
{0x83847623, "Sigmatel STAC9273D", 0x0},
{0x83847624, "Sigmatel STAC9272X", 0x0},
{0x83847625, "Sigmatel STAC9272D", 0x0},
{0x83847626, "Sigmatel STAC9271X", 0x0},
{0x83847627, "Sigmatel STAC9271D", 0x0},
{0x83847628, "Sigmatel STAC9274X5NH", 0x0},
{0x83847629, "Sigmatel STAC9274D5NH", 0x0},
{0x83847662, "Sigmatel STAC9872AK", 0x0},
{0x83847664, "Sigmatel STAC9872K", 0x0},
{0x83847680, "Sigmatel STAC9221A1", 0x0},
{0x83847680, "Sigmatel STAC9221A1", 0x0},
{0x83847681, "Sigmatel STAC9220D", 0x0},
{0x83847682, "Sigmatel STAC9221", 0x0},
{0x83847683, "Sigmatel STAC9221D", 0x0},
{0x83847690, "Sigmatel STAC9200", 0x0},
{0x838476a0, "Sigmatel STAC9205", 0x0},
{0x838476a1, "Sigmatel STAC9205D", 0x0},
{0x838476a2, "Sigmatel STAC9204", 0x0},
{0x838476a3, "Sigmatel STAC9204D", 0x0},
{0x838476a4, "Sigmatel STAC9255", 0x0},
{0x838476a5, "Sigmatel STAC9255D", 0x0},
{0x838476a6, "Sigmatel STAC9254", 0x0},
{0x838476a7, "Sigmatel STAC9254D", 0x0},
{0x83847880, "Sigmatel STAC9220A1", 0x0},
{0x83847882, "Sigmatel STAC9220A2", 0x0},
{0x0, "Unknown 0x00000000", 0x0},
};
static void
{
const char *name;
const char *vers;
devid <<= 16;
switch (devid) {
case 0x1002437b:
name = "ATI HD Audio";
vers = "SB450";
break;
case 0x10024383:
name = "ATI HD Audio";
vers = "SB600";
break;
case 0x10029442:
name = "ATI HD Audio";
vers = "Radeon HD 4850";
break;
case 0x1002aa30:
name = "ATI HD Audio";
vers = "HD 48x0";
break;
case 0x1002aa38:
name = "ATI HD Audio";
vers = "Radeon HD 4670";
break;
case 0x10de026c:
name = "NVIDIA HD Audio";
vers = "MCP51";
break;
case 0x10de0371:
name = "NVIDIA HD Audio";
vers = "MCP55";
break;
case 0x10de03e4:
name = "NVIDIA HD Audio";
vers = "MCP61";
break;
case 0x10de03f0:
name = "NVIDIA HD Audio";
vers = "MCP61A";
break;
case 0x10de044a:
name = "NVIDIA HD Audio";
vers = "MCP65";
break;
case 0x10de055c:
name = "NVIDIA HD Audio";
vers = "MCP67";
break;
case 0x10de0774:
name = "NVIDIA HD Audio";
vers = "MCP78S";
break;
case 0x10de0ac0:
name = "NVIDIA HD Audio";
vers = "MCP79";
break;
case 0x11063288:
name = "VIA HD Audio";
vers = "HDA";
break;
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 0x80863a3e:
name = "Intel HD Audio";
vers = "ICH10";
break;
case 0x80863b56:
name = "Intel HD Audio";
vers = "PCH";
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);
}
/* allocate the soft state structure */
/* interrupt cookie and initialize mutex */
goto error;
}
/* Set PCI command register to enable bus master and memeory I/O */
"couldn't init pci regs");
goto error;
}
"couldn't init controller");
goto error;
}
"couldn't create codec");
goto error;
}
goto error;
}
/* set up kernel statistics */
KSTAT_FLAG_PERSISTENT)) != NULL) {
}
/* disable interrupts and clear interrupt status */
/*
* Register audio controls.
*/
"unable to register with framework");
goto error;
}
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);
if (audiohd_beep)
(void) beep_fini();
return (DDI_SUCCESS);
}
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 */
};
&mod_driverops, /* drv_modops */
"AudioHD", /* linkinfo */
&audiohd_dev_ops, /* dev_ops */
};
{ &audiohd_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
/*
* Audio routines
*/
static int
{
switch (statep->sample_bit_depth) {
case AUDIOHD_BIT_DEPTH24:
return (AUDIO_FORMAT_S32_LE);
case AUDIOHD_BIT_DEPTH16:
default:
return (AUDIO_FORMAT_S16_LE);
}
}
static int
{
}
static int
{
return (statep->sample_rate);
}
static void
{
int i;
}
}
}
static void
{
}
/*
* 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);
/*
* We find that on intel ICH10 chipset with codec
* ALC888, audio is scratchy if we set the tag on the
* SPDIF path. So we just return here without setting
* the tag for the path as a workaround.
*/
return;
}
}
/* two channels supported */
(void) audioha_codec_verb_get(
(void) audioha_codec_4bit_verb_get(
/* 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(
}
}
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 (EIO);
}
/* 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 (EIO);
}
AUDIOHD_BDLE_NUMS - 1);
/* clear status */
/* set stream tag */
return (0);
}
static int
{
return (0);
}
static int
{
int rv;
return (rv);
}
/* Start DMA */
return (0);
}
static void
{
}
static void
{
int i, ret;
/* Convert the position into a frame count */
} else {
}
/*
* Check unsolicited response from pins, maybe something plugged in or
* out of the jack.
*/
if (status == 0) {
/* No pending interrupt we should take care */
return;
}
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 == DDI_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;
}
}
}
}
static uint64_t
{
return (val);
}
static void
{
}
static void
{
}
AUDIO_ENGINE_VERSION, /* version number */
NULL,
NULL,
};
static int
{
return (0);
}
static void
{
uint8_t l, r;
int gain;
(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_SPEAKER:
return;
break;
case DTYPE_HP_OUT:
return;
break;
case DTYPE_LINEOUT:
return;
break;
case DTYPE_CD:
return;
break;
case DTYPE_LINE_IN:
return;
break;
case DTYPE_MIC_IN:
return;
break;
}
continue;
}
}
}
}
static void
{
int i, j;
uint8_t l, r;
switch (color) {
case AUDIOHD_PIN_GREEN:
return;
break;
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;
switch (set_val) {
case DTYPE_LINE_IN:
case DTYPE_MIC_IN:
case DTYPE_CD:
}
}
break;
default:
break;
}
}
if (pin_wid == 0)
return (DDI_SUCCESS);
/*
* If there is a real selector in this input path,
* we select the right one input for the selector.
*/
if (w->type == WTYPE_AUDIO_SEL) {
(void) audioha_codec_verb_get(
path->sum_selconn[i]);
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(
widget->monitor_path_next[i]<<
(void) audioha_codec_4bit_verb_get(
widget->monitor_path_next[i]<<
}
}
}
}
static void
{
int i, j;
audiohd_widget_t *w;
continue;
}
}
}
static void
{
int i;
continue;
switch (vid) {
case AUDIOHD_VID_SIGMATEL:
/*
* Sigmatel HD codec specific operation.
* There is a workaround,
* Due to Sigmatel HD codec hardware problem,
* which it can't mute beep when volume is 0.
* So add global value audiohd_beep_vol,
* Set freq to 0 when volume is 0.
*/
if (tmp == 0) {
audiohd_beep_vol = 0;
} else {
(void) audioha_codec_verb_get(
tmp);
}
break;
default:
/* Common operation based on audiohd spec */
break;
}
}
}
static void
{
uint8_t l, r;
int gain;
if (val == 0) {
(void) audioha_codec_4bit_verb_get(
return;
}
r = (val & 0xff);
l = r;
(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
{
}
static void
{
(void) audiohd_set_input_pin(statep);
}
static int
{
return (EINVAL);
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
int i, j;
continue;
if (val == 1) {
/* Turn on loopback recording */
(void) audioha_codec_verb_get(statep,
(void) audioha_codec_verb_get(statep,
}
} else {
/* Turn off loopback recording */
(void) audioha_codec_verb_get(statep,
}
}
}
}
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static void
{
int i;
for (i = 0; i < CTL_MAX; i++) {
}
}
}
static void
{
desc.acd_minvalue = 0;
}
static void
{
desc.acd_minvalue = 0;
}
static void
{
desc.acd_minvalue = 0;
}
static void
{
for (int i = 0; audiohd_dtypes[i]; i++) {
}
}
static void
{
int i, j;
/*
* We always use soft volume control to adjust PCM volume.
*/
/* Allocate other controls */
continue;
if (color == AUDIOHD_PIN_GREEN) {
} else if (color == AUDIOHD_PIN_BLACK &&
} else if (color == AUDIOHD_PIN_ORANGE) {
} else if (color == AUDIOHD_PIN_GREY) {
}
}
}
continue;
}
}
}
if (statep->monitor_supported) {
}
if (statep->loopback_supported) {
0, audiohd_set_loopback);
}
}
/*
* 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);
}
static void
{
}
static void
{
}
static void
{
int divider;
if (freq == 0) {
divider = 0;
} else {
if (freq > AUDIOHDC_MAX_BEEP_GEN)
else if (freq < AUDIOHDC_MIX_BEEP_GEN)
switch (vid) {
case AUDIOHD_VID_SIGMATEL:
/*
* Sigmatel HD codec specification:
* frequency = 48000 * (257 - Divider) / 1024
*/
break;
default:
break;
}
}
if (audiohd_beep_vol == 0)
divider = 0;
}
/*
* 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
{
statep->hda_rirb_rp = 0;
return (DDI_FAILURE);
}
/* set device information */
return (DDI_SUCCESS);
} /* 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");
return (DDI_FAILURE);
}
"memory I/O mapping failed");
return (DDI_FAILURE);
}
/*
* 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;
case AUDIOHD_VID_NVIDIA:
/*
* Refer to the datasheet, we set snoop for NVIDIA
* like hardware
*/
break;
default:
break;
}
return (DDI_SUCCESS);
} /* 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 (DDI_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 (DDI_FAILURE);
}
/* HD spec requires to wait 250us at least. we use 500us */
drv_usecwait(500);
/* enable unsolicited response */
return (DDI_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");
return (DDI_FAILURE);
}
"ddi_dma_mem_alloc failed");
return (DDI_FAILURE);
}
"ddi_dma_addr_bind_handle failed");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
} /* 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 (DDI_FAILURE);
/* Initialize controller RIRB */
/* Initialize controller CORB */
return (DDI_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 (DDI_FAILURE);
/* check codec */
if (!statep->hda_codec_mask) {
"no codec exists");
return (DDI_FAILURE);
}
/* allocate DMA for CORB */
if (retval != DDI_SUCCESS) {
"failed to alloc DMA for CORB");
return (DDI_FAILURE);
}
/* allocate DMA for RIRB */
if (retval != DDI_SUCCESS) {
"failed to alloc DMA for RIRB");
return (DDI_FAILURE);
}
/* Initialize RIRB */
/* initialize CORB */
return (DDI_SUCCESS);
} /* 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
*/
"node %d has 0 connections", wid);
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:
/*
* Some codec(like ALC262) don't provide beep widget,
* it only has input Pin to connect an external beep
* (maybe in motherboard or elsewhere). So we open
* all PINs here in order to enable external beep
* source.
*/
(void) audioha_codec_4bit_verb_get(statep,
}
break;
case WTYPE_BEEP:
/*
* Get the audiohd_beep_switch value from audiohd.conf,
*/
if (audiohd_beep) {
(void) beep_fini();
}
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() */
/*
* audiohd_create_codec()
*
* Description:
* Searching for supported CODEC. If find, allocate memory
* to hold codec structure.
*/
static int
{
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
if ((mask & (1 << i)) == 0)
continue;
sizeof (hda_codec_t), KM_SLEEP);
continue;
}
continue;
}
/*
* Assume that each codec has just one audio function group
*/
if ((type & AUDIOHD_CODEC_TYPE_MASK) ==
break;
}
}
continue;
}
for (j = 0; j < len-1; j++) {
break;
}
}
} else {
}
/* 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 */
if (rate == 192000 &&
} else if (rate == 96000 &&
} else {
}
if (bits == 24 &&
} else {
}
/*
* We output the codec information to syslog
*/
(void) audiohd_create_widgets(codec);
}
return (DDI_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)(DDI_FAILURE);
return (uint32_t)(DDI_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)(DDI_FAILURE);
} else {
return (wid);
}
case WTYPE_AUDIO_OUT:
/* We need mixer widget, but the the mixer num is 0, failed */
return (uint32_t)(DDI_FAILURE);
widget->out_weight++;
break;
case WTYPE_AUDIO_MIX:
(*mixernum)++;
/* FALLTHRU */
case WTYPE_AUDIO_SEL:
widget->avail_conn[i],
if (widget->output_path_next ==
widget->output_path_next = 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 whether 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
* DDI_FAILURE
*/
depth);
/*
* A dac was not found
*/
continue;
path = (audiohd_path_t *)
kmem_zalloc(sizeof (audiohd_path_t),
KM_SLEEP);
widget->out_weight++;
widget->output_path_next = i;
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 laptops which have IDT or AD audio chipset, such as
* HP mini 1000 laptop, Dell Lattitude 6400, Lenovo T60, Lenove R61e.
* We don't allow mixer widget on such path, which leads to speaker
* loud hiss noise.
*/
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;
}
if (next == AUDIOHD_NULL_CONN)
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) {
}
}
if (next == AUDIOHD_NULL_CONN)
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;
}
if (next == AUDIOHD_NULL_CONN)
break;
}
/* find volume controlling node for this stream */
gain = 0;
widget = w;
while (widget) {
}
}
if (next == AUDIOHD_NULL_CONN)
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(
(widget->output_path_next <<
}
if (next == AUDIOHD_NULL_CONN)
break;
/*
* Accoding to HD spec, mixer doesn't support
* "select connection"
*/
(void) audioha_codec_verb_get(statep,
}
}
}
} /* 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 num, i;
if (depth > AUDIOHD_MAX_DEPTH)
return (uint32_t)(DDI_FAILURE);
return (uint32_t)(DDI_FAILURE);
/* we don't share widgets */
return (uint32_t)(DDI_FAILURE);
case WTYPE_PIN:
if (pin->no_phys_conn)
return (uint32_t)(DDI_FAILURE);
/* enable the pins' input capability */
}
case DTYPE_CD:
case DTYPE_LINE_IN:
case DTYPE_MIC_IN:
case DTYPE_AUX:
return (DDI_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->input_path_next = 0;
widget->avail_conn[0],
if (retval == DDI_SUCCESS) {
}
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 == DDI_SUCCESS) {
widget->input_path_next = i;
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 == DDI_SUCCESS) {
widget->input_path_next = i;
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 == DDI_SUCCESS) {
widget->input_path_next = i;
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;
}
next = w->input_path_next;
if (next == AUDIOHD_NULL_CONN)
break;
break;
}
/*
* Search a node for amplifier adjusting for the whole
* input path
*/
w = wadc;
gain = 0;
while (w) {
}
}
next = w->input_path_next;
if (next == 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;
}
next = w->input_path_next;
if (next == AUDIOHD_NULL_CONN)
break;
}
}
/* find node for amp controlling */
while (w) {
gain = (w->outamp_cap &
}
}
next = w->input_path_next;
if (next == AUDIOHD_NULL_CONN)
break;
}
}
}
} /* audiohd_build_input_amp() */
/*
* audiohd_finish_input_path()
*
* Description:
* Enable the widgets on the input path
*/
static void
{
int i, j;
continue;
w->input_path_next);
if (w->outamp_cap) {
(void) audioha_codec_4bit_verb_get(statep,
}
if (w->inamp_cap) {
(void) audioha_codec_4bit_verb_get(statep,
(w->input_path_next <<
}
}
/*
* 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->input_path_next);
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->input_path_next <<
}
}
}
} /* 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
* wid_t id no. of widget being searched
* int mixer share or not
*/
static int
{
int i, find = 0;
return (uint32_t)(DDI_FAILURE);
if (pin->no_phys_conn)
return (uint32_t)(DDI_FAILURE);
case DTYPE_SPDIF_IN:
case DTYPE_CD:
case DTYPE_LINE_IN:
case DTYPE_MIC_IN:
case DTYPE_AUX:
return (DDI_SUCCESS);
default:
return (uint32_t)(DDI_FAILURE);
}
}
/* the widget has been visited and can't be directed to input pin */
return (uint32_t)(DDI_FAILURE);
}
/* the widget has been used by the monitor path, and we can share it */
if (mixer)
return (DDI_SUCCESS);
else
return (uint32_t)(DDI_FAILURE);
}
case WTYPE_AUDIO_MIX:
if (widget->output_path_next == i)
continue;
DDI_SUCCESS) {
w = widget;
w->monitor_path_next[w->used++] = i;
w->path_flags |= AUDIOHD_PATH_MON;
find = 1;
}
}
break;
case WTYPE_AUDIO_SEL:
if (widget->output_path_next == i)
continue;
DDI_SUCCESS) {
widget->monitor_path_next[0] = i;
find = 1;
break;
}
}
break;
default:
break;
}
if (!find) {
return (uint32_t)(DDI_FAILURE);
}
else
return (DDI_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 &&
if (next == AUDIOHD_NULL_CONN)
break;
}
/*
* 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
*/
if (widget->output_path_next == k)
continue;
find = 0;
widget->avail_conn[k], 0) ==
DDI_SUCCESS) {
w = widget;
w->monitor_path_next[w->used++]
= k;
w->path_flags |=
find = 1;
} else if (
DDI_SUCCESS) {
w = widget;
w->monitor_path_next[w->used++]
= k;
w->path_flags |=
find = 1;
}
}
/*
* we needn't check widget->output_path_next
* 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;
(widget->monitor_path_next[i]
}
}
!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
{
int i, j, k;
continue;
if (!wid)
continue;
widget);
}
}
}
}
/*
* audiohd_find_beep()
* Description:
* Find a beep for a beep path. Then the play data can be sent to the out
* put pin through the beep path.
*
* Arguments:
* hda_codec_t *codec where the beep widget exists
* wid_t wid the no. of a widget
* int depth the depth of search
*
* Return:
* 1) wid of Beep widget;
* 2) 0 if no path
*/
static wid_t
{
if (depth > AUDIOHD_MAX_DEPTH)
return (uint32_t)(DDI_FAILURE);
return (uint32_t)(DDI_FAILURE);
case WTYPE_BEEP:
break;
case WTYPE_AUDIO_MIX:
case WTYPE_AUDIO_SEL:
if (retval == DDI_SUCCESS) {
if (widget->output_path_next !=
continue;
widget->beep_path_next = i;
return (wbeep);
}
}
break;
default:
break;
}
return (wbeep);
} /* audiohd_find_beep() */
/*
* audiohd_build_beep_path()
*
* Description:
* Search an beep path for each pin in the codec.
* Arguments:
* hda_codec_t *codec where the beep path exists
*/
static void
{
int i;
continue;
continue;
continue;
/*
* If a beep found, the return value is the wid of the
* widget on the path, or the return value is
* DDI_FAILURE
*/
widget->avail_conn[i], 0);
/*
* A beep was not found
*/
continue;
continue;
path = (audiohd_path_t *)
kmem_zalloc(sizeof (audiohd_path_t),
KM_SLEEP);
beeppath = 1;
widget->beep_path_next = i;
break;
}
}
if (!beeppath) {
for (int i = 0; i < AUDIOHD_CODEC_MAX; i++) {
continue;
wid++) {
path = (audiohd_path_t *)
kmem_zalloc(sizeof (audiohd_path_t),
KM_SLEEP);
beeppath = 1;
break;
}
}
}
}
} /* audiohd_build_beep_path() */
/*
* audiohd_build_beep_amp
*
* Description:
* Find the gain control and mute control widget
*/
static void
{
int i, j;
continue;
if (gain) {
}
break;
}
while (widget) {
if (widget->out_weight == 0 &&
widget->outamp_cap &
break;
}
if (next == AUDIOHD_NULL_CONN)
break;
}
gain = 0;
while (widget) {
if (widget->out_weight == 0 &&
widget->outamp_cap &
}
}
if (next == AUDIOHD_NULL_CONN)
break;
}
}
}
} /* audiohd_build_beep_amp */
/*
* audiohd_finish_beep_path()
*
* Description:
* Enable the widgets on the beep path
*/
static void
{
int i, j;
continue;
if (widget->outamp_cap) {
(void) audioha_codec_4bit_verb_get(
}
(void) audioha_codec_4bit_verb_get(
(widget->beep_path_next <<
}
continue;
}
while (widget) {
/*
* Set all amplifiers in this path to
* the maximum volume and unmute them.
*/
if (widget->out_weight != 0)
continue;
if (widget->outamp_cap) {
(void) audioha_codec_4bit_verb_get(
}
(void) audioha_codec_4bit_verb_get(
(widget->beep_path_next <<
}
if (next == AUDIOHD_NULL_CONN)
break;
/*
* Accoding to HD spec, mixer doesn't support
* "select connection"
*/
(void) audioha_codec_verb_get(statep,
}
}
}
} /* audiohd_finish_beep_path */
static int
{
if (depth > AUDIOHD_MAX_DEPTH)
return (retval);
return (retval);
case WTYPE_PIN:
if (pin->no_phys_conn)
return (DDI_FAILURE);
case DTYPE_LINE_IN:
/* Connection between line-in and output pins */
break;
case DTYPE_LINEOUT:
case DTYPE_HP_OUT:
case DTYPE_SPDIF_OUT:
retval = (DDI_SUCCESS);
break;
default:
break;
}
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->loopback_path_next = 0;
if (retval == (DDI_SUCCESS)) {
}
break;
}
if (retval == (DDI_SUCCESS)) {
widget->loopback_path_next = i;
break;
}
}
break;
default:
break;
}
return (retval);
}
static void
{
int i, retval;
/* check if it is an ADC widget */
continue;
else
if (retval == (DDI_SUCCESS)) {
widget->loopback_path_next = i;
break;
}
}
}
if (path)
} /* audiohd_build_loopback_path() */
/*
* 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;
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:
break;
case PORT_DAC:
break;
default:
return (DDI_FAILURE);
}
switch (statep->sample_rate) {
case 192000:
break;
case 96000:
break;
case 48000:
default: /* 48kHz is default */
break;
}
switch (statep->sample_bit_depth) {
case AUDIOHD_BIT_DEPTH24:
break;
case AUDIOHD_BIT_DEPTH16:
default: /* 16 bits is default */
break;
}
/* allocate dma handle */
if (rc != DDI_SUCCESS) {
rc);
return (DDI_FAILURE);
}
/*
* Warning: please be noted that allocating the dma memory
* with the flag IOMEM_DATA_UNCACHED is a hack due
* to an incorrect cache synchronization on NVidia MCP79
* chipset which causes the audio distortion problem,
* and that it should be fixed later. There should be
* no reason you have to allocate UNCACHED memory. In
* complex architectures with nested IO caches,
* reliance on this flag might lead to failure.
*/
if (rc == DDI_FAILURE) {
"ddi_dma_mem_alloc failed");
return (DDI_FAILURE);
}
}
/* bind DMA buffer */
"ddi_dma_addr_bind_handle failed: %d", rc);
return (DDI_FAILURE);
}
/*
* 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");
return (DDI_FAILURE);
}
/*
* we allocate all buffer descriptors lists in continuous
* dma memory.
*/
if (rc != DDI_SUCCESS) {
"ddi_dma_mem_alloc(bdlist) failed");
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
for (j = 0; j < AUDIOHD_BDLE_NUMS; j++) {
entry++;
}
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static void
{
int i;
for (i = 0; i < PORT_MAX; i++) {
continue;
}
}
}
}
}
}
}
}
}
/*
* audiohd_change_widget_power_state(audiohd_state_t *statep, int state)
* 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;
for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
continue;
wid++) {
if (widget->widget_cap &
(void) audioha_codec_verb_get(statep,
state);
}
}
}
}
/*
* 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++) {
continue;
}
}
/*
* 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++) {
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_FAILURE);
}
/* reset to enable the capability of unsolicited response for pin */
/* 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 void
{
if (tmp == AUDIOHD_CODEC_FAILURE)
return;
}
/*
* audiohd_enable_pin()
*/
static void
{
if (tmp == AUDIOHD_CODEC_FAILURE)
return;
}
/*
* audiohd_change_speaker_state()
*/
static void
{
int i, j;
continue;
if (on) {
}
}
} else {
}
}
}
}
}
/*
* 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;
continue;
/* Have a selector to choose input pin */
(void) audioha_codec_verb_get(
path->sum_selconn[j]);
return;
(void) audioha_codec_verb_get(
path->sum_selconn[j]);
return;
}
} else {
/*
* No selector widget in the path,
* mute unselected input pin
*/
/* Open all input pin, and then mute others */
if (select == 1) {
/* Select external mic, mute internal */
(void)
wid,
}
} else {
/* Select internal mic, mute external */
(void)
wid,
}
}
}
}
}
/*
* 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;
if (rs & AUDIOHD_PIN_PRES_MASK) {
/* 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_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 (DDI_FAILURE);
}
return (DDI_SUCCESS);
} /* audiohd_12bit_verb_to_codec() */
/*
* audiohd_4bit_verb_to_codec()
*
* Description:
*
*/
static int
{
wptr++;
/* overflow */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
} /* audiohd_4bit_verb_to_codec() */
/*
* audiohd_response_from_codec()
*
* Description:
*
*/
static int
{
return (DDI_FAILURE);
}
rptr++;
return (DDI_SUCCESS);
} /* audiohd_response_from_codec() */
/*
* audioha_codec_verb_get()
*/
static uint32_t
{
int ret;
int i;
if (ret != DDI_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 == DDI_SUCCESS))
break;
/* Empirical testing time, which works well */
drv_usecwait(30);
}
if (ret == DDI_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 != DDI_SUCCESS) {
return (uint32_t)(-1);
}
for (i = 0; i < 500; i++) {
((respex & AUDIOHD_BDLE_RIRB_UNSOLICIT) == 0) &&
(ret == DDI_SUCCESS))
break;
/* Empirical testing time, which works well */
drv_usecwait(30);
}
if (ret == DDI_SUCCESS) {
return (resp);
}
"response from codec: wid=%d, verb=0x%04x, param=0x%04x",
return ((uint32_t)(-1));
} /* audioha_codec_4bit_verb_get() */