--- sys/conf/files.orig Sun Oct 1 22:27:23 2006 +++ sys/conf/files Sun Oct 1 22:28:51 2006 @@ -874,6 +874,7 @@ dev/sound/pci/via8233.c optional snd_via8233 pci dev/sound/pci/via82c686.c optional snd_via82c686 pci dev/sound/pci/vibes.c optional snd_vibes pci +dev/sound/pci/hda/hdac.c optional snd_hda pci dev/sound/pcm/ac97.c optional sound dev/sound/pcm/ac97_if.m optional sound dev/sound/pcm/ac97_patch.c optional sound --- sys/dev/sound/driver.c.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/driver.c Sun Oct 1 22:28:51 2006 @@ -67,6 +67,7 @@ MODULE_DEPEND(snd_driver, snd_ess, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_fm801, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_gusc, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_hda, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_ich, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_maestro, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_maestro3, 1, 1, 1); --- sys/dev/sound/pci/hda/hda_reg.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hda_reg.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,1226 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.1 2006/10/01 11:12:59 ariff Exp $ + */ + +#ifndef _HDA_REG_H_ +#define _HDA_REG_H_ + +/**************************************************************************** + * HDA Device Verbs + ****************************************************************************/ + +/* HDA Command */ +#define HDA_CMD_VERB_MASK 0x000fffff +#define HDA_CMD_VERB_SHIFT 0 +#define HDA_CMD_NID_MASK 0x0ff00000 +#define HDA_CMD_NID_SHIFT 20 +#define HDA_CMD_CAD_MASK 0xf0000000 +#define HDA_CMD_CAD_SHIFT 28 + +#define HDA_CMD_VERB_4BIT_SHIFT 16 +#define HDA_CMD_VERB_12BIT_SHIFT 8 + +#define HDA_CMD_VERB_4BIT(verb, payload) \ + (((verb) << HDA_CMD_VERB_4BIT_SHIFT) | (payload)) +#define HDA_CMD_4BIT(cad, nid, verb, payload) \ + (((cad) << HDA_CMD_CAD_SHIFT) | \ + ((nid) << HDA_CMD_NID_SHIFT) | \ + (HDA_CMD_VERB_4BIT((verb), (payload)))) + +#define HDA_CMD_VERB_12BIT(verb, payload) \ + (((verb) << HDA_CMD_VERB_12BIT_SHIFT) | (payload)) +#define HDA_CMD_12BIT(cad, nid, verb, payload) \ + (((cad) << HDA_CMD_CAD_SHIFT) | \ + ((nid) << HDA_CMD_NID_SHIFT) | \ + (HDA_CMD_VERB_12BIT((verb), (payload)))) + +/* Get Parameter */ +#define HDA_CMD_VERB_GET_PARAMETER 0xf00 + +#define HDA_CMD_GET_PARAMETER(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PARAMETER, (payload))) + +/* Connection Select Control */ +#define HDA_CMD_VERB_GET_CONN_SELECT_CONTROL 0xf01 +#define HDA_CMD_VERB_SET_CONN_SELECT_CONTROL 0x701 + +#define HDA_CMD_GET_CONN_SELECT_CONTROL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONN_SELECT_CONTROL, 0x0)) +#define HDA_CMD_SET_CONNECTION_SELECT_CONTROL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONN_SELECT_CONTROL, (payload))) + +/* Connection List Entry */ +#define HDA_CMD_VERB_GET_CONN_LIST_ENTRY 0xf02 + +#define HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONN_LIST_ENTRY, (payload))) + +#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_SHORT 1 +#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_LONG 2 + +/* Processing State */ +#define HDA_CMD_VERB_GET_PROCESSING_STATE 0xf03 +#define HDA_CMD_VERB_SET_PROCESSING_STATE 0x703 + +#define HDA_CMD_GET_PROCESSING_STATE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PROCESSING_STATE, 0x0)) +#define HDA_CMD_SET_PROCESSING_STATE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PROCESSING_STATE, (payload))) + +#define HDA_CMD_GET_PROCESSING_STATE_STATE_OFF 0x00 +#define HDA_CMD_GET_PROCESSING_STATE_STATE_ON 0x01 +#define HDA_CMD_GET_PROCESSING_STATE_STATE_BENIGN 0x02 + +/* Coefficient Index */ +#define HDA_CMD_VERB_GET_COEFF_INDEX 0xd +#define HDA_CMD_VERB_SET_COEFF_INDEX 0x5 + +#define HDA_CMD_GET_COEFF_INDEX(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_COEFF_INDEX, 0x0)) +#define HDA_CMD_SET_COEFF_INDEX(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_COEFF_INDEX, (payload))) + +/* Processing Coefficient */ +#define HDA_CMD_VERB_GET_PROCESSING_COEFF 0xc +#define HDA_CMD_VERB_SET_PROCESSING_COEFF 0x4 + +#define HDA_CMD_GET_PROCESSING_COEFF(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PROCESSING_COEFF, 0x0)) +#define HDA_CMD_SET_PROCESSING_COEFF(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PROCESSING_COEFF, (payload))) + +/* Amplifier Gain/Mute */ +#define HDA_CMD_VERB_GET_AMP_GAIN_MUTE 0xb +#define HDA_CMD_VERB_SET_AMP_GAIN_MUTE 0x3 + +#define HDA_CMD_GET_AMP_GAIN_MUTE(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_AMP_GAIN_MUTE, (payload))) +#define HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_AMP_GAIN_MUTE, (payload))) + +#define HDA_CMD_GET_AMP_GAIN_MUTE_INPUT 0x0000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_OUTPUT 0x8000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_RIGHT 0x0000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_LEFT 0x2000 + +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK 0x00000008 +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT 7 +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK 0x00000007 +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT 0 + +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE(rsp) \ + (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK) >> \ + HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT) +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN(rsp) \ + (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK) >> \ + HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT) + +#define HDA_CMD_SET_AMP_GAIN_MUTE_OUTPUT 0x8000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INPUT 0x4000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_LEFT 0x2000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT 0x1000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK 0x0f00 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT 8 +#define HDA_CMD_SET_AMP_GAIN_MUTE_MUTE 0x0080 +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK 0x0007 +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT 0 + +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX(index) \ + (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT) & \ + HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK) +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN(index) \ + (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT) & \ + HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK) + +/* Converter format */ +#define HDA_CMD_VERB_GET_CONV_FMT 0xa +#define HDA_CMD_VERB_SET_CONV_FMT 0x2 + +#define HDA_CMD_GET_CONV_FMT(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONV_FMT, 0x0)) +#define HDA_CMD_SET_CONV_FMT(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONV_FMT, (payload))) + +/* Digital Converter Control */ +#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT 0xf0d +#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1 0x70d +#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2 0x70e + +#define HDA_CMD_GET_DIGITAL_CONV_FMT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_DIGITAL_CONV_FMTT, 0x0)) +#define HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1, (payload))) +#define HDA_CMD_SET_DIGITAL_CONV_FMT2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2, (payload))) + +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK 0x7f00 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT 8 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK 0x0080 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT 7 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK 0x0040 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT 6 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK 0x0020 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT 5 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK 0x0010 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT 4 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK 0x0008 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT 3 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK 0x0004 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT 2 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK 0x0002 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT 1 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK 0x0001 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT 0 + +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT) + +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_L 0x80 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRO 0x40 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO 0x20 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_COPY 0x10 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRE 0x08 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_VCFG 0x04 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_V 0x02 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN 0x01 + +/* Power State */ +#define HDA_CMD_VERB_GET_POWER_STATE 0xf05 +#define HDA_CMD_VERB_SET_POWER_STATE 0x705 + +#define HDA_CMD_GET_POWER_STATE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_POWER_STATE, 0x0)) +#define HDA_CMD_SET_POWER_STATE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_POWER_STATE, (payload))) + +#define HDA_CMD_POWER_STATE_D0 0x00 +#define HDA_CMD_POWER_STATE_D1 0x01 +#define HDA_CMD_POWER_STATE_D2 0x02 +#define HDA_CMD_POWER_STATE_D3 0x03 + +#define HDA_CMD_POWER_STATE_ACT_MASK 0x000000f0 +#define HDA_CMD_POWER_STATE_ACT_SHIFT 4 +#define HDA_CMD_POWER_STATE_SET_MASK 0x0000000f +#define HDA_CMD_POWER_STATE_SET_SHIFT 0 + +#define HDA_CMD_GET_POWER_STATE_ACT(rsp) \ + (((rsp) & HDA_CMD_POWER_STATE_ACT_MASK) >> \ + HDA_CMD_POWER_STATE_ACT_SHIFT) +#define HDA_CMD_GET_POWER_STATE_SET(rsp) \ + (((rsp) & HDA_CMD_POWER_STATE_SET_MASK) >> \ + HDA_CMD_POWER_STATE_SET_SHIFT) + +#define HDA_CMD_SET_POWER_STATE_ACT(ps) \ + (((ps) << HDA_CMD_POWER_STATE_ACT_SHIFT) & \ + HDA_CMD_POWER_STATE_ACT_MASK) +#define HDA_CMD_SET_POWER_STATE_SET(ps) \ + (((ps) << HDA_CMD_POWER_STATE_SET_SHIFT) & \ + HDA_CMD_POWER_STATE_ACT_MASK) + +/* Converter Stream, Channel */ +#define HDA_CMD_VERB_GET_CONV_STREAM_CHAN 0xf06 +#define HDA_CMD_VERB_SET_CONV_STREAM_CHAN 0x706 + +#define HDA_CMD_GET_CONV_STREAM_CHAN(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONV_STREAM_CHAN, 0x0)) +#define HDA_CMD_SET_CONV_STREAM_CHAN(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONV_STREAM_CHAN, (payload))) + +#define HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK 0x000000f0 +#define HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT 4 +#define HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK 0x0000000f +#define HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT 0 + +#define HDA_CMD_GET_CONV_STREAM_CHAN_STREAM(rsp) \ + (((rsp) & HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK) >> \ + HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT) +#define HDA_CMD_GET_CONV_STREAM_CHAN_CHAN(rsp) \ + (((rsp) & HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK) >> \ + HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT) + +#define HDA_CMD_SET_CONV_STREAM_CHAN_STREAM(param) \ + (((param) << HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT) & \ + HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK) +#define HDA_CMD_SET_CONV_STREAM_CHAN_CHAN(param) \ + (((param) << HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT) & \ + HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK) + +/* Input Converter SDI Select */ +#define HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT 0xf04 +#define HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT 0x704 + +#define HDA_CMD_GET_INPUT_CONVERTER_SDI_SELECT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT, 0x0)) +#define HDA_CMD_SET_INPUT_CONVERTER_SDI_SELECT(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT, (payload))) + +/* Pin Widget Control */ +#define HDA_CMD_VERB_GET_PIN_WIDGET_CTRL 0xf07 +#define HDA_CMD_VERB_SET_PIN_WIDGET_CTRL 0x707 + +#define HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PIN_WIDGET_CTRL, 0x0)) +#define HDA_CMD_SET_PIN_WIDGET_CTRL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PIN_WIDGET_CTRL, (payload))) + +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK 0x00000080 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT 7 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK 0x00000040 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT 6 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK 0x00000020 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT 5 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x00000007 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0 + +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK) >> \ + HDA_GET_CMD_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT) + +#define HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE 0x80 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE 0x40 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE 0x20 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x07 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0 + +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(param) \ + (((param) << HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT) & \ + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) + +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_HIZ 0 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50 1 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_GROUND 2 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80 4 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100 5 + +/* Unsolicited Response */ +#define HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE 0xf08 +#define HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE 0x708 + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE, 0x0)) +#define HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE, (payload))) + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK 0x00000080 +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT 7 +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK 0x0000001f +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK) >> \ + HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT) +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG(rsp) \ + (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK) >> \ + HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT) + +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE 0x80 +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK 0x1f +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 + +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG(param) \ + (((param) << HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT) & \ + HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK) + +/* Pin Sense */ +#define HDA_CMD_VERB_GET_PIN_SENSE 0xf09 +#define HDA_CMD_VERB_SET_PIN_SENSE 0x709 + +#define HDA_CMD_GET_PIN_SENSE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PIN_SENSE, 0x0)) +#define HDA_CMD_SET_PIN_SENSE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PIN_SENSE, (payload))) + +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_MASK 0x80000000 +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_SHIFT 31 +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK 0x7fffffff +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT 0 + +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_MASK) >> \ + HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_SHIFT) +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK) >> \ + HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT) + +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_INVALID 0x7fffffff + +#define HDA_CMD_SET_PIN_SENSE_LEFT_CHANNEL 0x00 +#define HDA_CMD_SET_PIN_SENSE_RIGHT_CHANNEL 0x01 + +/* EAPD/BTL Enable */ +#define HDA_CMD_VERB_GET_EAPD_BTL_ENABLE 0xf0c +#define HDA_CMD_VERB_SET_EAPD_BTL_ENABLE 0x70c + +#define HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_EAPD_BTL_ENABLE, 0x0)) +#define HDA_CMD_SET_EAPD_BTL_ENABLE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_EAPD_BTL_ENABLE, (payload))) + +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK 0x00000004 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT 2 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK 0x00000002 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT 1 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK 0x00000001 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT 0 + +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT) +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT) +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT) + +#define HDA_CMD_SET_EAPD_BTL_ENABLE_LR_SWAP 0x04 +#define HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD 0x02 +#define HDA_CMD_SET_EAPD_BTL_ENABLE_BTL 0x01 + +/* GPI Data */ +#define HDA_CMD_VERB_GET_GPI_DATA 0xf10 +#define HDA_CMD_VERB_SET_GPI_DATA 0x710 + +#define HDA_CMD_GET_GPI_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_DATA, 0x0)) +#define HDA_CMD_SET_GPI_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_DATA, (payload))) + +/* GPI Wake Enable Mask */ +#define HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK 0xf11 +#define HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK 0x711 + +#define HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPI_WAKE_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK, (payload))) + +/* GPI Unsolicited Enable Mask */ +#define HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK 0xf12 +#define HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK 0x712 + +#define HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK, (payload))) + +/* GPI Sticky Mask */ +#define HDA_CMD_VERB_GET_GPI_STICKY_MASK 0xf13 +#define HDA_CMD_VERB_SET_GPI_STICKY_MASK 0x713 + +#define HDA_CMD_GET_GPI_STICKY_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_STICKY_MASK, 0x0)) +#define HDA_CMD_SET_GPI_STICKY_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_STICKY_MASK, (payload))) + +/* GPO Data */ +#define HDA_CMD_VERB_GET_GPO_DATA 0xf14 +#define HDA_CMD_VERB_SET_GPO_DATA 0x714 + +#define HDA_CMD_GET_GPO_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPO_DATA, 0x0)) +#define HDA_CMD_SET_GPO_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPO_DATA, (payload))) + +/* GPIO Data */ +#define HDA_CMD_VERB_GET_GPIO_DATA 0xf15 +#define HDA_CMD_VERB_SET_GPIO_DATA 0x715 + +#define HDA_CMD_GET_GPIO_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_DATA, 0x0)) +#define HDA_CMD_SET_GPIO_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_DATA, (payload))) + +/* GPIO Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_ENABLE_MASK 0xf16 +#define HDA_CMD_VERB_SET_GPIO_ENABLE_MASK 0x716 + +#define HDA_CMD_GET_GPIO_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_ENABLE_MASK, (payload))) + +/* GPIO Direction */ +#define HDA_CMD_VERB_GET_GPIO_DIRECTION 0xf17 +#define HDA_CMD_VERB_SET_GPIO_DIRECTION 0x717 + +#define HDA_CMD_GET_GPIO_DIRECTION(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_DIRECTION, 0x0)) +#define HDA_CMD_SET_GPIO_DIRECTION(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_DIRECTION, (payload))) + +/* GPIO Wake Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK 0xf18 +#define HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK 0x718 + +#define HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_WAKE_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK, (payload))) + +/* GPIO Unsolicited Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK 0xf19 +#define HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK 0x719 + +#define HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK, (payload))) + +/* GPIO_STICKY_MASK */ +#define HDA_CMD_VERB_GET_GPIO_STICKY_MASK 0xf1a +#define HDA_CMD_VERB_SET_GPIO_STICKY_MASK 0x71a + +#define HDA_CMD_GET_GPIO_STICKY_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_STICKY_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_STICKY_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_STICKY_MASK, (payload))) + +/* Beep Generation */ +#define HDA_CMD_VERB_GET_BEEP_GENERATION 0xf0a +#define HDA_CMD_VERB_SET_BEEP_GENERATION 0x70a + +#define HDA_CMD_GET_BEEP_GENERATION(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_BEEP_GENERATION, 0x0)) +#define HDA_CMD_SET_BEEP_GENERATION(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_BEEP_GENERATION, (payload))) + +/* Volume Knob */ +#define HDA_CMD_VERB_GET_VOLUME_KNOB 0xf0f +#define HDA_CMD_VERB_SET_VOLUME_KNOB 0x70f + +#define HDA_CMD_GET_VOLUME_KNOB(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_VOLUME_KNOB, 0x0)) +#define HDA_CMD_SET_VOLUME_KNOB(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_VOLUME_KNOB, (payload))) + +/* Subsystem ID */ +#define HDA_CMD_VERB_GET_SUBSYSTEM_ID 0xf20 +#define HDA_CMD_VERB_SET_SUSBYSTEM_ID1 0x720 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID2 0x721 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID3 0x722 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID4 0x723 + +#define HDA_CMD_GET_SUBSYSTEM_ID(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_SUBSYSTEM_ID, 0x0)) +#define HDA_CMD_SET_SUBSYSTEM_ID1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID1, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID2, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID3(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID3, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID4(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID4, (payload))) + +/* Configuration Default */ +#define HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT 0xf1c +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1 0x71c +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2 0x71d +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3 0x71e +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4 0x71f + +#define HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT, 0x0)) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT3(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT4(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4, (payload))) + +/* Stripe Control */ +#define HDA_CMD_VERB_GET_STRIPE_CONTROL 0xf24 +#define HDA_CMD_VERB_SET_STRIPE_CONTROL 0x724 + +#define HDA_CMD_SET_STRIPE_CONTROL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_STRIPE_CONTROL, 0x0)) +#define HDA_CMD_GET_STRIPE_CONTROL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_STRIPE_CONTROL, (payload))) + +/* Function Reset */ +#define HDA_CMD_VERB_FUNCTION_RESET 0x7ff + +#define HDA_CMD_FUNCTION_RESET(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_FUNCTION_RESET, 0x0)) + + +/**************************************************************************** + * HDA Device Parameters + ****************************************************************************/ + +/* Vendor ID */ +#define HDA_PARAM_VENDOR_ID 0x00 + +#define HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK 0xffff0000 +#define HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT 16 +#define HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK 0x0000ffff +#define HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT 0 + +#define HDA_PARAM_VENDOR_ID_VENDOR_ID(param) \ + (((param) & HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK) >> \ + HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT) +#define HDA_PARAM_VENDOR_ID_DEVICE_ID(param) \ + (((param) & HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK) >> \ + HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT) + +/* Revision ID */ +#define HDA_PARAM_REVISION_ID 0x02 + +#define HDA_PARAM_REVISION_ID_MAJREV_MASK 0x00f00000 +#define HDA_PARAM_REVISION_ID_MAJREV_SHIFT 20 +#define HDA_PARAM_REVISION_ID_MINREV_MASK 0x000f0000 +#define HDA_PARAM_REVISION_ID_MINREV_SHIFT 16 +#define HDA_PARAM_REVISION_ID_REVISION_ID_MASK 0x0000ff00 +#define HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT 8 +#define HDA_PARAM_REVISION_ID_STEPPING_ID_MASK 0x000000ff +#define HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT 0 + +#define HDA_PARAM_REVISION_ID_MAJREV(param) \ + (((param) & HDA_PARAM_REVISION_ID_MAJREV_MASK) >> \ + HDA_PARAM_REVISION_ID_MAJREV_SHIFT) +#define HDA_PARAM_REVISION_ID_MINREV(param) \ + (((param) & HDA_PARAM_REVISION_ID_MINREV_MASK) >> \ + HDA_PARAM_REVISION_ID_MINREV_SHIFT) +#define HDA_PARAM_REVISION_ID_REVISION_ID(param) \ + (((param) & HDA_PARAM_REVISION_ID_REVISION_ID_MASK) >> \ + HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT) +#define HDA_PARAM_REVISION_ID_STEPPING_ID(param) \ + (((param) & HDA_PARAM_REVISION_ID_STEPPING_ID_MASK) >> \ + HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT) + +/* Subordinate Node Cound */ +#define HDA_PARAM_SUB_NODE_COUNT 0x04 + +#define HDA_PARAM_SUB_NODE_COUNT_START_MASK 0x00ff0000 +#define HDA_PARAM_SUB_NODE_COUNT_START_SHIFT 16 +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK 0x000000ff +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT 0 + +#define HDA_PARAM_SUB_NODE_COUNT_START(param) \ + (((param) & HDA_PARAM_SUB_NODE_COUNT_START_MASK) >> \ + HDA_PARAM_SUB_NODE_COUNT_START_SHIFT) +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL(param) \ + (((param) & HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK) >> \ + HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT) + +/* Function Group Type */ +#define HDA_PARAM_FCT_GRP_TYPE 0x05 + +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK 0x00000100 +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_SHIFT 8 +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK 0x000000ff +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT 0 + +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL(param) \ + (((param) & HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK) >> \ + HDA_PARAM_FCT_GROUP_TYPE_UNSOL_SHIFT) +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(param) \ + (((param) & HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK) >> \ + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT) + +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO 0x01 +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM 0x02 + +/* Audio Function Group Capabilities */ +#define HDA_PARAM_AUDIO_FCT_GRP_CAP 0x08 + +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK 0x00010000 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT 16 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK 0x00000f00 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT 8 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK 0x0000000f +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT 0 + +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT) +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT) +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT) + +/* Audio Widget Capabilities */ +#define HDA_PARAM_AUDIO_WIDGET_CAP 0x09 + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK 0x00f00000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT 20 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK 0x000f0000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT 16 +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK 0x00000800 +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT 11 +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK 0x00000400 +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT 10 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK 0x00000200 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT 9 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK 0x00000100 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT 8 +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK 0x00000080 +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT 7 +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK 0x00000040 +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT 6 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK 0x00000020 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT 5 +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK 0x00000010 +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT 4 +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK 0x00000008 +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT 3 +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK 0x00000004 +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT 2 +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK 0x00000002 +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT 1 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK 0x00000001 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT 0 + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT) + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT 0x0 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT 0x1 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER 0x2 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR 0x3 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX 0x4 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET 0x5 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET 0x6 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET 0x7 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET 0xf + +/* Supported PCM Size, Rates */ + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE 0x0a + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK 0x00100000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT 20 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK 0x00080000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT 19 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK 0x00040000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT 18 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK 0x00020000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT 17 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK 0x00010000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT 16 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000800 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 11 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000400 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 10 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000200 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 9 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000100 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 8 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000080 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 7 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000040 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 6 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000020 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 5 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000010 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 4 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000008 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 3 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x000000004 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 2 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x000000002 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 1 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x000000001 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 0 + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT) + +/* Supported Stream Formats */ +#define HDA_PARAM_SUPP_STREAM_FORMATS 0x0b + +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK 0x00000004 +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT 2 +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK 0x00000002 +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT 1 +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK 0x00000001 +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT 0 + +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT) +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT) +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT) + +/* Pin Capabilities */ +#define HDA_PARAM_PIN_CAP 0x0c + +#define HDA_PARAM_PIN_CAP_EAPD_CAP_MASK 0x00010000 +#define HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT 16 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_MASK 0x0000ff00 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT 8 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK 0x00002000 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT 13 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK 0x00001000 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT 12 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK 0x00000400 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT 10 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK 0x00000200 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT 9 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK 0x00000100 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT 8 +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK 0x00000040 +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT 6 +#define HDA_PARAM_PIN_CAP_INPUT_CAP_MASK 0x00000020 +#define HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT 5 +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK 0x00000010 +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT 4 +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK 0x00000008 +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT 3 +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK 0x00000004 +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT 2 +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK 0x00000002 +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT 1 +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK 0x00000001 +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT 0 + +#define HDA_PARAM_PIN_CAP_EAPD_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_EAPD_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT) +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(param) \ + (((param) & HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK) >> \ + HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT) +#define HDA_PARAM_PIN_CAP_INPUT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_INPUT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD(param) \ + (((param) & HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK) >> \ + HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT) +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT) + +/* Input Amplifier Capabilities */ +#define HDA_PARAM_INPUT_AMP_CAP 0x0d + +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000 +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT 31 +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000 +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT 16 +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00 +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT 8 +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK 0x0000007f +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT 0 + +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT) + +/* Output Amplifier Capabilities */ +#define HDA_PARAM_OUTPUT_AMP_CAP 0x12 + +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000 +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT 31 +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000 +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT 16 +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00 +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT 8 +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK 0x0000007f +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT 0 + +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT) + +/* Connection List Length */ +#define HDA_PARAM_CONN_LIST_LENGTH 0x0e + +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK 0x00000080 +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT 7 +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK 0x0000007f +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT 0 + +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(param) \ + (((param) & HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK) >> \ + HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT) +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(param) \ + (((param) & HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK) >> \ + HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT) + +/* Supported Power States */ +#define HDA_PARAM_SUPP_POWER_STATES 0x0f + +#define HDA_PARAM_SUPP_POWER_STATES_D3_MASK 0x00000008 +#define HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT 3 +#define HDA_PARAM_SUPP_POWER_STATES_D2_MASK 0x00000004 +#define HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT 2 +#define HDA_PARAM_SUPP_POWER_STATES_D1_MASK 0x00000002 +#define HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT 1 +#define HDA_PARAM_SUPP_POWER_STATES_D0_MASK 0x00000001 +#define HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT 0 + +#define HDA_PARAM_SUPP_POWER_STATES_D3(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D3_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D2(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D2_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D1(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D1_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D0(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D0_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT) + +/* Processing Capabilities */ +#define HDA_PARAM_PROCESSING_CAP 0x10 + +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK 0x0000ff00 +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT 8 +#define HDA_PARAM_PROCESSING_CAP_BENIGN_MASK 0x00000001 +#define HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT 0 + +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF(param) \ + (((param) & HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK) >> \ + HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT) +#define HDA_PARAM_PROCESSING_CAP_BENIGN(param) \ + (((param) & HDA_PARAM_PROCESSING_CAP_BENIGN_MASK) >> \ + HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT) + +/* GPIO Count */ +#define HDA_PARAM_GPIO_COUNT 0x11 + +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK 0x80000000 +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT 31 +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK 0x40000000 +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT 30 +#define HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK 0x00ff0000 +#define HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT 16 +#define HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK 0x0000ff00 +#define HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT 8 +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK 0x000000ff +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT 0 + +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK) >> \ + HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT) +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK) >> \ + HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPI(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPO(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT) + +/* Volume Knob Capabilities */ +#define HDA_PARAM_VOLUME_KNOB_CAP 0x13 + +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK 0x00000080 +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT 7 +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK 0x0000007f +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT 0 + +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA(param) \ + (((param) & HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK) >> \ + HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT) +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS(param) \ + (((param) & HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK) >> \ + HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT) + + +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK 0x00000000f +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK 0x0000000f0 +#define HDA_CONFIG_DEFAULTCONF_MISC_MASK 0x000000f00 +#define HDA_CONFIG_DEFAULTCONF_COLOR_MASK 0x00000f000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK 0x000f00000 +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MASK 0x000f00000 +#define HDA_CONFIG_DEFAULTCONF_LOCATION_MASK 0x03f000000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK 0x0c0000000 + +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK (0<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE (1<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED (2<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH (3<<30) + +#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT (0<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER (1<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT (2<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_CD (3<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT (4<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT (5<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE (6<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET (7<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN (8<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_AUX (9<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN (10<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY (11<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN (12<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN (13<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER (15<<20) + +#endif --- sys/dev/sound/pci/hda/hdac.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac.c Sun Oct 1 22:29:26 2006 @@ -0,0 +1,4894 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * Copyright (c) 2006 Ariff Abdullah + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Intel High Definition Audio (Controller) driver for FreeBSD. Be advised + * that this driver still in its early stage, and possible of rewrite are + * pretty much guaranteed. There are supposedly several distinct parent/child + * busses to make this "perfect", but as for now and for the sake of + * simplicity, everything is gobble up within single source. + * + * List of subsys: + * 1) HDA Controller support + * 2) HDA Codecs support, which may include + * - HDA + * - Modem + * - HDMI + * 3) Widget parser - the real magic of why this driver works on so + * many hardwares with minimal vendor specific quirk. The original + * parser was written using Ruby and can be found at + * http://people.freebsd.org/~ariff/HDA/parser.rb . This crude + * ruby parser take the verbose dmesg dump as its input. Refer to + * http://www.microsoft.com/whdc/device/audio/default.mspx for various + * interesting documents, especiall UAA (Universal Audio Architecture). + * 4) Possible vendor specific support. + * (snd_hda_intel, snd_hda_ati, etc..) + * + * Thanks to Ahmad Ubaidah Omar @ Defenxis Sdn. Bhd. for the + * Compaq V3000 with Conexant HDA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * + * * This driver is a collaborative effort made by: * + * * * + * * Stephane E. Potvin * + * * Andrea Bittau * + * * Wesley Morgan * + * * Daniel Eischen * + * * Maxime Guillaud * + * * Ariff Abdullah * + * * * + * * ....and various people from freebsd-multimedia@FreeBSD.org * + * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "mixer_if.h" + +#define HDA_DRV_TEST_REV "20061017_0033" +#define HDA_WIDGET_PARSER_REV 1 + +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.8 2006/10/16 14:43:22 ariff Exp $"); + +#undef HDA_DEBUG_ENABLED +#define HDA_DEBUG_ENABLED 1 + +#ifdef HDA_DEBUG_ENABLED +#define HDA_DEBUG(stmt) do { \ + stmt \ +} while(0) +#else +#define HDA_DEBUG(stmt) +#endif + +#define HDA_BOOTVERBOSE(stmt) do { \ + if (bootverbose) { \ + stmt \ + } \ +} while(0) + +#if 1 +#undef HDAC_INTR_EXTRA +#define HDAC_INTR_EXTRA 1 +#endif + +#define hdac_lock(sc) snd_mtxlock((sc)->lock) +#define hdac_unlock(sc) snd_mtxunlock((sc)->lock) +#define hdac_lockassert(sc) snd_mtxassert((sc)->lock) +#define hdac_lockowned(sc) mtx_owned((sc)->lock) + +#define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) +#define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ + (fl) == 0xffffffff || \ + (((fl) & 0xffff0000) == 0xffff0000 && \ + ((fl) & 0x0000ffff) == ((v) & 0x0000ffff)) || \ + (((fl) & 0x0000ffff) == 0x0000ffff && \ + ((fl) & 0xffff0000) == ((v) & 0xffff0000))) +#define HDA_MATCH_ALL 0xffffffff +#define HDAC_INVALID 0xffffffff + +#define HDA_MODEL_CONSTRUCT(vendor, model) \ + (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) + +/* Controller models */ + +/* Intel */ +#define INTEL_VENDORID 0x8086 +#define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) +#define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) +#define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) +#define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) +#define HDA_INTEL_ALL HDA_MODEL_CONSTRUCT(INTEL, 0xffff) + +/* Nvidia */ +#define NVIDIA_VENDORID 0x10de +#define HDA_NVIDIA_MCP51 HDA_MODEL_CONSTRUCT(NVIDIA, 0x026c) +#define HDA_NVIDIA_MCP55 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0371) +#define HDA_NVIDIA_MCP61A HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) +#define HDA_NVIDIA_MCP61B HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) +#define HDA_NVIDIA_MCP65A HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) +#define HDA_NVIDIA_MCP65B HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) +#define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) + +/* ATI */ +#define ATI_VENDORID 0x1002 +#define HDA_ATI_SB450 HDA_MODEL_CONSTRUCT(ATI, 0x437b) +#define HDA_ATI_SB600 HDA_MODEL_CONSTRUCT(ATI, 0x4383) +#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) + +/* VIA */ +#define VIA_VENDORID 0x1106 +#define HDA_VIA_VT82XX HDA_MODEL_CONSTRUCT(VIA, 0x3288) +#define HDA_VIA_ALL HDA_MODEL_CONSTRUCT(VIA, 0xffff) + +/* SiS */ +#define SIS_VENDORID 0x1039 +#define HDA_SIS_966 HDA_MODEL_CONSTRUCT(SIS, 0x7502) +#define HDA_SIS_ALL HDA_MODEL_CONSTRUCT(SIS, 0xffff) + +/* OEM/subvendors */ + +/* HP/Compaq */ +#define HP_VENDORID 0x103c +#define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) +#define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) +#define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) +#define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) + +/* Dell */ +#define DELL_VENDORID 0x1028 +#define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) +#define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) +#define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) + +/* Clevo */ +#define CLEVO_VENDORID 0x1558 +#define CLEVO_D900T_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0x0900) +#define CLEVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0xffff) + +/* Acer */ +#define ACER_VENDORID 0x1025 +#define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) + +/* Asus */ +#define ASUS_VENDORID 0x1043 +#define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) +#define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_A8JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) +#define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) + +/* IBM / Lenovo */ +#define IBM_VENDORID 0x1014 +#define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) +#define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) + + +/* Misc constants.. */ +#define HDA_AMP_MUTE_DEFAULT (0xffffffff) +#define HDA_AMP_MUTE_NONE (0) +#define HDA_AMP_MUTE_LEFT (1 << 0) +#define HDA_AMP_MUTE_RIGHT (1 << 1) +#define HDA_AMP_MUTE_ALL (HDA_AMP_MUTE_LEFT | HDA_AMP_MUTE_RIGHT) + +#define HDA_AMP_LEFT_MUTED(v) ((v) & (HDA_AMP_MUTE_LEFT)) +#define HDA_AMP_RIGHT_MUTED(v) (((v) & HDA_AMP_MUTE_RIGHT) >> 1) + +#define HDA_DAC_PATH (1 << 0) +#define HDA_ADC_PATH (1 << 1) +#define HDA_ADC_RECSEL (1 << 2) + +#define HDA_CTL_OUT (1 << 0) +#define HDA_CTL_IN (1 << 1) +#define HDA_CTL_BOTH (HDA_CTL_IN | HDA_CTL_OUT) + +#define HDA_GPIO_MAX 15 +/* 0 - 14 = GPIO */ +#define HDA_QUIRK_GPIO0 (1 << 0) +#define HDA_QUIRK_GPIO1 (1 << 1) +#define HDA_QUIRK_GPIO2 (1 << 2) +#define HDA_QUIRK_SOFTPCMVOL (1 << 15) +#define HDA_QUIRK_FIXEDRATE (1 << 16) +#define HDA_QUIRK_FORCESTEREO (1 << 17) +#define HDA_QUIRK_EAPDINV (1 << 18) + +static const struct { + char *key; + uint32_t value; +} hdac_quirks_tab[] = { + { "gpio0", HDA_QUIRK_GPIO0 }, + { "gpio1", HDA_QUIRK_GPIO1 }, + { "gpio2", HDA_QUIRK_GPIO2 }, + { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, + { "fixedrate", HDA_QUIRK_FIXEDRATE }, + { "forcestereo", HDA_QUIRK_FORCESTEREO }, + { "eapdinv", HDA_QUIRK_EAPDINV }, +}; +#define HDAC_QUIRKS_TAB_LEN \ + (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) + +#define HDA_BDL_MIN 2 +#define HDA_BDL_MAX 256 +#define HDA_BDL_DEFAULT HDA_BDL_MIN + +#define HDA_BUFSZ_MIN 4096 +#define HDA_BUFSZ_MAX 65536 +#define HDA_BUFSZ_DEFAULT 16384 + +#define HDA_PARSE_MAXDEPTH 10 + +#define HDAC_UNSOLTAG_EVENT_HP 0x00 + +static MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); + +enum { + HDA_PARSE_MIXER, + HDA_PARSE_DIRECT +}; + +/* Default */ +static uint32_t hdac_fmt[] = { + AFMT_STEREO | AFMT_S16_LE, + 0 +}; + +static struct pcmchan_caps hdac_caps = {48000, 48000, hdac_fmt, 0}; + +static const struct { + uint32_t model; + char *desc; +} hdac_devices[] = { + { HDA_INTEL_82801F, "Intel 82801F" }, + { HDA_INTEL_82801G, "Intel 82801G" }, + { HDA_INTEL_82801H, "Intel 82801H" }, + { HDA_INTEL_63XXESB, "Intel 631x/632xESB" }, + { HDA_NVIDIA_MCP51, "NVidia MCP51" }, + { HDA_NVIDIA_MCP55, "NVidia MCP55" }, + { HDA_NVIDIA_MCP61A, "NVidia MCP61A" }, + { HDA_NVIDIA_MCP61B, "NVidia MCP61B" }, + { HDA_NVIDIA_MCP65A, "NVidia MCP65A" }, + { HDA_NVIDIA_MCP65B, "NVidia MCP65B" }, + { HDA_ATI_SB450, "ATI SB450" }, + { HDA_ATI_SB600, "ATI SB600" }, + { HDA_VIA_VT82XX, "VIA VT8251/8237A" }, + { HDA_SIS_966, "SiS 966" }, + /* Unknown */ + { HDA_INTEL_ALL, "Intel (Unknown)" }, + { HDA_NVIDIA_ALL, "NVidia (Unknown)" }, + { HDA_ATI_ALL, "ATI (Unknown)" }, + { HDA_VIA_ALL, "VIA (Unknown)" }, + { HDA_SIS_ALL, "SiS (Unknown)" }, +}; +#define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) + +static const struct { + uint32_t rate; + int valid; + uint16_t base; + uint16_t mul; + uint16_t div; +} hda_rate_tab[] = { + { 8000, 1, 0x0000, 0x0000, 0x0500 }, /* (48000 * 1) / 6 */ + { 9600, 0, 0x0000, 0x0000, 0x0400 }, /* (48000 * 1) / 5 */ + { 12000, 0, 0x0000, 0x0000, 0x0300 }, /* (48000 * 1) / 4 */ + { 16000, 1, 0x0000, 0x0000, 0x0200 }, /* (48000 * 1) / 3 */ + { 18000, 0, 0x0000, 0x1000, 0x0700 }, /* (48000 * 3) / 8 */ + { 19200, 0, 0x0000, 0x0800, 0x0400 }, /* (48000 * 2) / 5 */ + { 24000, 0, 0x0000, 0x0000, 0x0100 }, /* (48000 * 1) / 2 */ + { 28800, 0, 0x0000, 0x1000, 0x0400 }, /* (48000 * 3) / 5 */ + { 32000, 1, 0x0000, 0x0800, 0x0200 }, /* (48000 * 2) / 3 */ + { 36000, 0, 0x0000, 0x1000, 0x0300 }, /* (48000 * 3) / 4 */ + { 38400, 0, 0x0000, 0x1800, 0x0400 }, /* (48000 * 4) / 5 */ + { 48000, 1, 0x0000, 0x0000, 0x0000 }, /* (48000 * 1) / 1 */ + { 64000, 0, 0x0000, 0x1800, 0x0200 }, /* (48000 * 4) / 3 */ + { 72000, 0, 0x0000, 0x1000, 0x0100 }, /* (48000 * 3) / 2 */ + { 96000, 1, 0x0000, 0x0800, 0x0000 }, /* (48000 * 2) / 1 */ + { 144000, 0, 0x0000, 0x1000, 0x0000 }, /* (48000 * 3) / 1 */ + { 192000, 1, 0x0000, 0x1800, 0x0000 }, /* (48000 * 4) / 1 */ + { 8820, 0, 0x4000, 0x0000, 0x0400 }, /* (44100 * 1) / 5 */ + { 11025, 1, 0x4000, 0x0000, 0x0300 }, /* (44100 * 1) / 4 */ + { 12600, 0, 0x4000, 0x0800, 0x0600 }, /* (44100 * 2) / 7 */ + { 14700, 0, 0x4000, 0x0000, 0x0200 }, /* (44100 * 1) / 3 */ + { 17640, 0, 0x4000, 0x0800, 0x0400 }, /* (44100 * 2) / 5 */ + { 18900, 0, 0x4000, 0x1000, 0x0600 }, /* (44100 * 3) / 7 */ + { 22050, 1, 0x4000, 0x0000, 0x0100 }, /* (44100 * 1) / 2 */ + { 25200, 0, 0x4000, 0x1800, 0x0600 }, /* (44100 * 4) / 7 */ + { 26460, 0, 0x4000, 0x1000, 0x0400 }, /* (44100 * 3) / 5 */ + { 29400, 0, 0x4000, 0x0800, 0x0200 }, /* (44100 * 2) / 3 */ + { 33075, 0, 0x4000, 0x1000, 0x0300 }, /* (44100 * 3) / 4 */ + { 35280, 0, 0x4000, 0x1800, 0x0400 }, /* (44100 * 4) / 5 */ + { 44100, 1, 0x4000, 0x0000, 0x0000 }, /* (44100 * 1) / 1 */ + { 58800, 0, 0x4000, 0x1800, 0x0200 }, /* (44100 * 4) / 3 */ + { 66150, 0, 0x4000, 0x1000, 0x0100 }, /* (44100 * 3) / 2 */ + { 88200, 1, 0x4000, 0x0800, 0x0000 }, /* (44100 * 2) / 1 */ + { 132300, 0, 0x4000, 0x1000, 0x0000 }, /* (44100 * 3) / 1 */ + { 176400, 1, 0x4000, 0x1800, 0x0000 }, /* (44100 * 4) / 1 */ +}; +#define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) + +/* All codecs you can eat... */ +#define HDA_CODEC_CONSTRUCT(vendor, id) \ + (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) + +/* Realtek */ +#define REALTEK_VENDORID 0x10ec +#define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) +#define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) +#define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) +#define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) +#define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) +#define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) + +/* Analog Device */ +#define ANALOGDEVICE_VENDORID 0x11d4 +#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1981) +#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1983) +#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1986) +#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0xffff) + +/* CMedia */ +#define CMEDIA_VENDORID 0x434d +#define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) +#define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) + +/* Sigmatel */ +#define SIGMATEL_VENDORID 0x8384 +#define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) +#define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) +#define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) +#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) +#define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) + +/* + * Conexant + * + * Ok, the truth is, I don't have any idea at all whether + * it is "Venice" or "Waikiki" or other unnamed CXyadayada. The only + * place that tell me it is "Venice" is from its Windows driver INF. + * + * Venice - CX????? + * Waikiki - CX20551-22 + */ +#define CONEXANT_VENDORID 0x14f1 +#define HDA_CODEC_CXVENICE HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) +#define HDA_CODEC_CXWAIKIKI HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) +#define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) + + +/* Codecs */ +static const struct { + uint32_t id; + char *name; +} hdac_codecs[] = { + { HDA_CODEC_ALC260, "Realtek ALC260" }, + { HDA_CODEC_ALC861, "Realtek ALC861" }, + { HDA_CODEC_ALC880, "Realtek ALC880" }, + { HDA_CODEC_ALC882, "Realtek ALC882" }, + { HDA_CODEC_ALC883, "Realtek ALC883" }, + { HDA_CODEC_AD1981HD, "Analog Device AD1981HD" }, + { HDA_CODEC_AD1983, "Analog Device AD1983" }, + { HDA_CODEC_AD1986A, "Analog Device AD1986A" }, + { HDA_CODEC_CMI9880, "CMedia CMI9880" }, + { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, + { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, + { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, + { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, + { HDA_CODEC_CXVENICE, "Conexant Venice" }, + { HDA_CODEC_CXWAIKIKI, "Conexant Waikiki" }, + /* Unknown codec */ + { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, + { HDA_CODEC_ADXXXX, "Analog Device (Unknown)" }, + { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, + { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, + { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, +}; +#define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) + +enum { + HDAC_HP_SWITCH_CTL, + HDAC_HP_SWITCH_CTRL +}; + +static const struct { + uint32_t model; + uint32_t id; + int type; + nid_t hpnid; + nid_t spkrnid[8]; + nid_t eapdnid; +} hdac_hp_switch[] = { + /* Specific OEM models */ + { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 17, { 16, -1 }, 16 }, + { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 6, { 5, -1 }, 5 }, + { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 6, { 5, -1 }, 5 }, + { DELL_D820_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 13, { 14, -1 }, -1 }, + { DELL_I1300_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 13, { 14, -1 }, -1 }, + /* + * All models that at least come from the same vendor with + * simmilar codec. + */ + { HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 17, { 16, -1 }, 16 }, + { HP_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 6, { 5, -1 }, 5 }, + { DELL_ALL_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 13, { 14, -1 }, -1 }, +}; +#define HDAC_HP_SWITCH_LEN \ + (sizeof(hdac_hp_switch) / sizeof(hdac_hp_switch[0])) + +static const struct { + uint32_t model; + uint32_t id; + nid_t eapdnid; + int hp_switch; +} hdac_eapd_switch[] = { + { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, 16, 1 }, + { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, + { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, +}; +#define HDAC_EAPD_SWITCH_LEN \ + (sizeof(hdac_eapd_switch) / sizeof(hdac_eapd_switch[0])) + +/**************************************************************************** + * Function prototypes + ****************************************************************************/ +static void hdac_intr_handler(void *); +static int hdac_reset(struct hdac_softc *); +static int hdac_get_capabilities(struct hdac_softc *); +static void hdac_dma_cb(void *, bus_dma_segment_t *, int, int); +static int hdac_dma_alloc(struct hdac_softc *, + struct hdac_dma *, bus_size_t); +static void hdac_dma_free(struct hdac_dma *); +static int hdac_mem_alloc(struct hdac_softc *); +static void hdac_mem_free(struct hdac_softc *); +static int hdac_irq_alloc(struct hdac_softc *); +static void hdac_irq_free(struct hdac_softc *); +static void hdac_corb_init(struct hdac_softc *); +static void hdac_rirb_init(struct hdac_softc *); +static void hdac_corb_start(struct hdac_softc *); +static void hdac_rirb_start(struct hdac_softc *); +static void hdac_scan_codecs(struct hdac_softc *); +static int hdac_probe_codec(struct hdac_codec *); +static struct hdac_devinfo *hdac_probe_function(struct hdac_codec *, nid_t); +static void hdac_add_child(struct hdac_softc *, struct hdac_devinfo *); + +static void hdac_attach2(void *); + +static uint32_t hdac_command_sendone_internal(struct hdac_softc *, + uint32_t, int); +static void hdac_command_send_internal(struct hdac_softc *, + struct hdac_command_list *, int); + +static int hdac_probe(device_t); +static int hdac_attach(device_t); +static int hdac_detach(device_t); +static void hdac_widget_connection_select(struct hdac_widget *, uint8_t); +static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *, + uint32_t, int, int); +static struct hdac_audio_ctl *hdac_audio_ctl_amp_get(struct hdac_devinfo *, + nid_t, int, int); +static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, + nid_t, nid_t, int, int, int, int, int, int); +static int hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *); +static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); + +#define hdac_command(a1, a2, a3) \ + hdac_command_sendone_internal(a1, a2, a3) + +#define hdac_codec_id(d) \ + ((uint32_t)((d == NULL) ? 0x00000000 : \ + ((((uint32_t)(d)->vendor_id & 0x0000ffff) << 16) | \ + ((uint32_t)(d)->device_id & 0x0000ffff)))) + +static char * +hdac_codec_name(struct hdac_devinfo *devinfo) +{ + uint32_t id; + int i; + + id = hdac_codec_id(devinfo); + + for (i = 0; i < HDAC_CODECS_LEN; i++) { + if (HDA_DEV_MATCH(hdac_codecs[i].id, id)) + return (hdac_codecs[i].name); + } + + return ((id == 0x00000000) ? "NULL Codec" : "Unknown Codec"); +} + +static char * +hdac_audio_ctl_ossmixer_mask2name(uint32_t devmask) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + static char *unknown = "???"; + int i; + + for (i = SOUND_MIXER_NRDEVICES - 1; i >= 0; i--) { + if (devmask & (1 << i)) + return (ossname[i]); + } + return (unknown); +} + +static void +hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + int i, first = 1; + + bzero(buf, len); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (mask & (1 << i)) { + if (first == 0) + strlcat(buf, ", ", len); + strlcat(buf, ossname[i], len); + first = 0; + } + } +} + +static struct hdac_audio_ctl * +hdac_audio_ctl_each(struct hdac_devinfo *devinfo, int *index) +{ + if (devinfo == NULL || + devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO || + index == NULL || devinfo->function.audio.ctl == NULL || + devinfo->function.audio.ctlcnt < 1 || + *index < 0 || *index >= devinfo->function.audio.ctlcnt) + return (NULL); + return (&devinfo->function.audio.ctl[(*index)++]); +} + +static struct hdac_audio_ctl * +hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, + int index, int cnt) +{ + struct hdac_audio_ctl *ctl, *retctl = NULL; + int i, at, atindex, found = 0; + + if (devinfo == NULL || devinfo->function.audio.ctl == NULL) + return (NULL); + + at = cnt; + if (at == 0) + at = 1; + else if (at < 0) + at = -1; + atindex = index; + if (atindex < 0) + atindex = -1; + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (!(ctl->widget->nid == nid && (atindex == -1 || + ctl->index == atindex))) + continue; + found++; + if (found == cnt) + return (ctl); + retctl = ctl; + } + + return ((at == -1) ? retctl : NULL); +} + +static void +hdac_hp_switch_handler(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t id, res; + int i = 0, j, forcemute; + nid_t cad; + + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return; + + sc = devinfo->codec->sc; + cad = devinfo->codec->cad; + id = hdac_codec_id(devinfo); + for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { + if (HDA_DEV_MATCH(hdac_hp_switch[i].model, + sc->pci_subvendor) && + hdac_hp_switch[i].id == id) + break; + } + + if (i >= HDAC_HP_SWITCH_LEN) + return; + + forcemute = 0; + if (hdac_hp_switch[i].eapdnid != -1) { + w = hdac_widget_get(devinfo, hdac_hp_switch[i].eapdnid); + if (w != NULL && w->param.eapdbtl != HDAC_INVALID) + forcemute = (w->param.eapdbtl & + HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ? 0 : 1; + } + + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", + hdac_hp_switch[i].hpnid, res); + ); + res >>= 31; + + switch (hdac_hp_switch[i].type) { + case HDAC_HP_SWITCH_CTL: + ctl = hdac_audio_ctl_amp_get(devinfo, + hdac_hp_switch[i].hpnid, 0, 1); + if (ctl != NULL) { + ctl->muted = (res != 0 && forcemute == 0) ? + HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL; + hdac_audio_ctl_amp_set(ctl, + HDA_AMP_MUTE_DEFAULT, ctl->left, + ctl->right); + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + ctl = hdac_audio_ctl_amp_get(devinfo, + hdac_hp_switch[i].spkrnid[j], 0, 1); + if (ctl != NULL) { + ctl->muted = (res != 0 || forcemute == 1) ? + HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE; + hdac_audio_ctl_amp_set(ctl, + HDA_AMP_MUTE_DEFAULT, ctl->left, + ctl->right); + } + } + break; + case HDAC_HP_SWITCH_CTRL: + if (res != 0) { + /* HP in */ + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (forcemute == 0) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, + w->wclass.pin.ctrl), cad); + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, + w->wclass.pin.ctrl), cad); + } + } + } else { + /* HP out */ + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, + w->wclass.pin.ctrl), cad); + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (forcemute == 0) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, + w->wclass.pin.ctrl), cad); + } + } + } + break; + default: + break; + } +} + +static void +hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo = NULL; + device_t *devlist = NULL; + int devcount, i; + + if (codec == NULL || codec->sc == NULL) + return; + + sc = codec->sc; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Unsol Tag: 0x%08x\n", tag); + ); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo != NULL && devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && + devinfo->codec != NULL && + devinfo->codec->cad == codec->cad) { + break; + } else + devinfo = NULL; + } + if (devlist != NULL) + free(devlist, M_TEMP); + + if (devinfo == NULL) + return; + + switch (tag) { + case HDAC_UNSOLTAG_EVENT_HP: + hdac_hp_switch_handler(devinfo); + break; + default: + break; + } +} + +static void +hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) +{ + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + uint32_t res; +#endif + + if (ch->blkcnt == 0) + return; + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + res = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDSTS); +#endif + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + HDA_BOOTVERBOSE( + if (res & (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE)) + device_printf(sc->dev, + "PCMDIR_%s intr triggered beyond stream boundary:" + "%08x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", res); + ); +#endif + + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDSTS, + HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + if (res & HDAC_SDSTS_BCIS) { +#endif + ch->prevptr = ch->ptr; + ch->ptr += sndbuf_getblksz(ch->b); + ch->ptr %= sndbuf_getsize(ch->b); + hdac_unlock(sc); + chn_intr(ch->c); + hdac_lock(sc); + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + } +#endif +} + +/**************************************************************************** + * void hdac_intr_handler(void *) + * + * Interrupt handler. Processes interrupts received from the hdac. + ****************************************************************************/ +static void +hdac_intr_handler(void *context) +{ + struct hdac_softc *sc; + uint32_t intsts; + uint8_t rirbsts; + uint8_t rirbwp; + struct hdac_rirb *rirb_base, *rirb; + nid_t ucad; + uint32_t utag; + + sc = (struct hdac_softc *)context; + + hdac_lock(sc); + /* Do we have anything to do? */ + intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); + if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { + hdac_unlock(sc); + return; + } + + /* Was this a controller interrupt? */ + if (HDA_FLAG_MATCH(intsts, HDAC_INTSTS_CIS)) { + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); + /* Get as many responses that we can */ + while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { + HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); + rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_POSTREAD); + while (sc->rirb_rp != rirbwp) { + sc->rirb_rp++; + sc->rirb_rp %= sc->rirb_size; + rirb = &rirb_base[sc->rirb_rp]; + if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { + ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); + utag = rirb->response >> 26; + if (ucad > -1 && ucad < HDAC_CODEC_MAX && + sc->codecs[ucad] != NULL) { + sc->unsolq[sc->unsolq_wp++] = + (ucad << 16) | + (utag & 0xffff); + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } + } + } + rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); + } + /* XXX to be removed */ + /* Clear interrupt and exit */ +#ifdef HDAC_INTR_EXTRA + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS); +#endif + } + if (intsts & HDAC_INTSTS_SIS_MASK) { + if (intsts & (1 << sc->num_iss)) + hdac_stream_intr(sc, &sc->play); + if (intsts & (1 << 0)) + hdac_stream_intr(sc, &sc->rec); + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK); +#endif + } + + if (sc->unsolq_st == HDAC_UNSOLQ_READY) { + sc->unsolq_st = HDAC_UNSOLQ_BUSY; + while (sc->unsolq_rp != sc->unsolq_wp) { + ucad = sc->unsolq[sc->unsolq_rp] >> 16; + utag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + hdac_unsolicited_handler(sc->codecs[ucad], utag); + } + sc->unsolq_st = HDAC_UNSOLQ_READY; + } + + hdac_unlock(sc); +} + +/**************************************************************************** + * int hdac_reset(hdac_softc *) + * + * Reset the hdac to a quiescent and known state. + ****************************************************************************/ +static int +hdac_reset(struct hdac_softc *sc) +{ + uint32_t gctl; + int count, i; + + /* + * Stop all Streams DMA engine + */ + for (i = 0; i < sc->num_iss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_ISDCTL(sc, i), 0x0); + for (i = 0; i < sc->num_oss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_OSDCTL(sc, i), 0x0); + for (i = 0; i < sc->num_bss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_BSDCTL(sc, i), 0x0); + + /* + * Stop Control DMA engines + */ + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, 0x0); + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, 0x0); + + /* + * Reset the controller. The reset must remain asserted for + * a minimum of 100us. + */ + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl & ~HDAC_GCTL_CRST); + count = 10000; + do { + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + if (!(gctl & HDAC_GCTL_CRST)) + break; + DELAY(10); + } while (--count); + if (gctl & HDAC_GCTL_CRST) { + device_printf(sc->dev, "Unable to put hdac in reset\n"); + return (ENXIO); + } + DELAY(100); + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); + count = 10000; + do { + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + if (gctl & HDAC_GCTL_CRST) + break; + DELAY(10); + } while (--count); + if (!(gctl & HDAC_GCTL_CRST)) { + device_printf(sc->dev, "Device stuck in reset\n"); + return (ENXIO); + } + + /* + * Wait for codecs to finish their own reset sequence. The delay here + * should be of 250us but for some reasons, on it's not enough on my + * computer. Let's use twice as much as necessary to make sure that + * it's reset properly. + */ + DELAY(1000); + + return (0); +} + + +/**************************************************************************** + * int hdac_get_capabilities(struct hdac_softc *); + * + * Retreive the general capabilities of the hdac; + * Number of Input Streams + * Number of Output Streams + * Number of bidirectional Streams + * 64bit ready + * CORB and RIRB sizes + ****************************************************************************/ +static int +hdac_get_capabilities(struct hdac_softc *sc) +{ + uint16_t gcap; + uint8_t corbsize, rirbsize; + + gcap = HDAC_READ_2(&sc->mem, HDAC_GCAP); + sc->num_iss = HDAC_GCAP_ISS(gcap); + sc->num_oss = HDAC_GCAP_OSS(gcap); + sc->num_bss = HDAC_GCAP_BSS(gcap); + + sc->support_64bit = HDA_FLAG_MATCH(gcap, HDAC_GCAP_64OK); + + corbsize = HDAC_READ_1(&sc->mem, HDAC_CORBSIZE); + if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_256) == + HDAC_CORBSIZE_CORBSZCAP_256) + sc->corb_size = 256; + else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_16) == + HDAC_CORBSIZE_CORBSZCAP_16) + sc->corb_size = 16; + else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_2) == + HDAC_CORBSIZE_CORBSZCAP_2) + sc->corb_size = 2; + else { + device_printf(sc->dev, "%s: Invalid corb size (%x)\n", + __func__, corbsize); + return (ENXIO); + } + + rirbsize = HDAC_READ_1(&sc->mem, HDAC_RIRBSIZE); + if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_256) == + HDAC_RIRBSIZE_RIRBSZCAP_256) + sc->rirb_size = 256; + else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_16) == + HDAC_RIRBSIZE_RIRBSZCAP_16) + sc->rirb_size = 16; + else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_2) == + HDAC_RIRBSIZE_RIRBSZCAP_2) + sc->rirb_size = 2; + else { + device_printf(sc->dev, "%s: Invalid rirb size (%x)\n", + __func__, rirbsize); + return (ENXIO); + } + + return (0); +} + + +/**************************************************************************** + * void hdac_dma_cb + * + * This function is called by bus_dmamap_load when the mapping has been + * established. We just record the physical address of the mapping into + * the struct hdac_dma passed in. + ****************************************************************************/ +static void +hdac_dma_cb(void *callback_arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct hdac_dma *dma; + + if (error == 0) { + dma = (struct hdac_dma *)callback_arg; + dma->dma_paddr = segs[0].ds_addr; + } +} + +static void +hdac_dma_nocache(void *ptr) +{ +#if defined(__i386__) || defined(__amd64__) + pt_entry_t *pte; + vm_offset_t va; + + va = (vm_offset_t)ptr; + pte = vtopte(va); + if (pte) { + *pte |= PG_N; + invltlb(); + } +#endif +} + +/**************************************************************************** + * int hdac_dma_alloc + * + * This function allocate and setup a dma region (struct hdac_dma). + * It must be freed by a corresponding hdac_dma_free. + ****************************************************************************/ +static int +hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) +{ + int result; + int lowaddr; + + lowaddr = (sc->support_64bit) ? BUS_SPACE_MAXADDR : + BUS_SPACE_MAXADDR_32BIT; + bzero(dma, sizeof(*dma)); + + /* + * Create a DMA tag + */ + result = bus_dma_tag_create(NULL, /* parent */ + HDAC_DMA_ALIGNMENT, /* alignment */ + 0, /* boundary */ + lowaddr, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, /* filtfunc */ + NULL, /* fistfuncarg */ + size, /* maxsize */ + 1, /* nsegments */ + size, /* maxsegsz */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &dma->dma_tag); /* dmat */ + if (result != 0) { + device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", + __func__, result); + goto fail; + } + + /* + * Allocate DMA memory + */ + result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map); + if (result != 0) { + device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n", + __func__, result); + goto fail; + } + + /* + * Map the memory + */ + result = bus_dmamap_load(dma->dma_tag, dma->dma_map, + (void *)dma->dma_vaddr, size, hdac_dma_cb, (void *)dma, + BUS_DMA_NOWAIT); + if (result != 0 || dma->dma_paddr == 0) { + device_printf(sc->dev, "%s: bus_dmamem_load failed (%x)\n", + __func__, result); + goto fail; + } + bzero((void *)dma->dma_vaddr, size); + hdac_dma_nocache(dma->dma_vaddr); + + return (0); +fail: + if (dma->dma_map != NULL) + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + if (dma->dma_tag != NULL) + bus_dma_tag_destroy(dma->dma_tag); + return (result); +} + + +/**************************************************************************** + * void hdac_dma_free(struct hdac_dma *) + * + * Free a struct dhac_dma that has been previously allocated via the + * hdac_dma_alloc function. + ****************************************************************************/ +static void +hdac_dma_free(struct hdac_dma *dma) +{ + if (dma->dma_tag != NULL) { + /* Flush caches */ + bus_dmamap_sync(dma->dma_tag, dma->dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + bus_dma_tag_destroy(dma->dma_tag); + } +} + +/**************************************************************************** + * int hdac_mem_alloc(struct hdac_softc *) + * + * Allocate all the bus resources necessary to speak with the physical + * controller. + ****************************************************************************/ +static int +hdac_mem_alloc(struct hdac_softc *sc) +{ + struct hdac_mem *mem; + + mem = &sc->mem; + mem->mem_rid = PCIR_BAR(0); + mem->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, + &mem->mem_rid, RF_ACTIVE); + if (mem->mem_res == NULL) { + device_printf(sc->dev, + "%s: Unable to allocate memory resource\n", __func__); + return (ENOMEM); + } + mem->mem_tag = rman_get_bustag(mem->mem_res); + mem->mem_handle = rman_get_bushandle(mem->mem_res); + + return (0); +} + +/**************************************************************************** + * void hdac_mem_free(struct hdac_softc *) + * + * Free up resources previously allocated by hdac_mem_alloc. + ****************************************************************************/ +static void +hdac_mem_free(struct hdac_softc *sc) +{ + struct hdac_mem *mem; + + mem = &sc->mem; + if (mem->mem_res != NULL) + bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid, + mem->mem_res); +} + +/**************************************************************************** + * int hdac_irq_alloc(struct hdac_softc *) + * + * Allocate and setup the resources necessary for interrupt handling. + ****************************************************************************/ +static int +hdac_irq_alloc(struct hdac_softc *sc) +{ + struct hdac_irq *irq; + int result; + + irq = &sc->irq; + irq->irq_rid = 0x0; + irq->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, + &irq->irq_rid, RF_SHAREABLE | RF_ACTIVE); + if (irq->irq_res == NULL) { + device_printf(sc->dev, "%s: Unable to allocate irq\n", + __func__); + goto fail; + } + result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE, + hdac_intr_handler, sc, &irq->irq_handle); + if (result != 0) { + device_printf(sc->dev, + "%s: Unable to setup interrupt handler (%x)\n", + __func__, result); + goto fail; + } + + return (0); + +fail: + if (irq->irq_res != NULL) + bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, + irq->irq_res); + return (ENXIO); +} + +/**************************************************************************** + * void hdac_irq_free(struct hdac_softc *) + * + * Free up resources previously allocated by hdac_irq_alloc. + ****************************************************************************/ +static void +hdac_irq_free(struct hdac_softc *sc) +{ + struct hdac_irq *irq; + + irq = &sc->irq; + if (irq->irq_handle != NULL) + bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle); + if (irq->irq_res != NULL) + bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, + irq->irq_res); +} + +/**************************************************************************** + * void hdac_corb_init(struct hdac_softc *) + * + * Initialize the corb registers for operations but do not start it up yet. + * The CORB engine must not be running when this function is called. + ****************************************************************************/ +static void +hdac_corb_init(struct hdac_softc *sc) +{ + uint8_t corbsize; + uint64_t corbpaddr; + + /* Setup the CORB size. */ + switch (sc->corb_size) { + case 256: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_256); + break; + case 16: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_16); + break; + case 2: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_2); + break; + default: + panic("%s: Invalid CORB size (%x)\n", __func__, sc->corb_size); + } + HDAC_WRITE_1(&sc->mem, HDAC_CORBSIZE, corbsize); + + /* Setup the CORB Address in the hdac */ + corbpaddr = (uint64_t)sc->corb_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_CORBLBASE, (uint32_t)corbpaddr); + HDAC_WRITE_4(&sc->mem, HDAC_CORBUBASE, (uint32_t)(corbpaddr >> 32)); + + /* Set the WP and RP */ + sc->corb_wp = 0; + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); + HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, HDAC_CORBRP_CORBRPRST); + /* + * The HDA specification indicates that the CORBRPRST bit will always + * read as zero. Unfortunately, it seems that at least the 82801G + * doesn't reset the bit to zero, which stalls the corb engine. + * manually reset the bit to zero before continuing. + */ + HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, 0x0); + + /* Enable CORB error reporting */ +#if 0 + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, HDAC_CORBCTL_CMEIE); +#endif +} + +/**************************************************************************** + * void hdac_rirb_init(struct hdac_softc *) + * + * Initialize the rirb registers for operations but do not start it up yet. + * The RIRB engine must not be running when this function is called. + ****************************************************************************/ +static void +hdac_rirb_init(struct hdac_softc *sc) +{ + uint8_t rirbsize; + uint64_t rirbpaddr; + + /* Setup the RIRB size. */ + switch (sc->rirb_size) { + case 256: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_256); + break; + case 16: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_16); + break; + case 2: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_2); + break; + default: + panic("%s: Invalid RIRB size (%x)\n", __func__, sc->rirb_size); + } + HDAC_WRITE_1(&sc->mem, HDAC_RIRBSIZE, rirbsize); + + /* Setup the RIRB Address in the hdac */ + rirbpaddr = (uint64_t)sc->rirb_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_RIRBLBASE, (uint32_t)rirbpaddr); + HDAC_WRITE_4(&sc->mem, HDAC_RIRBUBASE, (uint32_t)(rirbpaddr >> 32)); + + /* Setup the WP and RP */ + sc->rirb_rp = 0; + HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST); + + /* Setup the interrupt threshold */ + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); + + /* Enable Overrun and response received reporting */ +#if 0 + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, + HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); +#else + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); +#endif + + /* + * Make sure that the Host CPU cache doesn't contain any dirty + * cache lines that falls in the rirb. If I understood correctly, it + * should be sufficient to do this only once as the rirb is purely + * read-only from now on. + */ + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_PREREAD); +} + +/**************************************************************************** + * void hdac_corb_start(hdac_softc *) + * + * Startup the corb DMA engine + ****************************************************************************/ +static void +hdac_corb_start(struct hdac_softc *sc) +{ + uint32_t corbctl; + + corbctl = HDAC_READ_1(&sc->mem, HDAC_CORBCTL); + corbctl |= HDAC_CORBCTL_CORBRUN; + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, corbctl); +} + +/**************************************************************************** + * void hdac_rirb_start(hdac_softc *) + * + * Startup the rirb DMA engine + ****************************************************************************/ +static void +hdac_rirb_start(struct hdac_softc *sc) +{ + uint32_t rirbctl; + + rirbctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); + rirbctl |= HDAC_RIRBCTL_RIRBDMAEN; + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, rirbctl); +} + + +/**************************************************************************** + * void hdac_scan_codecs(struct hdac_softc *) + * + * Scan the bus for available codecs. + ****************************************************************************/ +static void +hdac_scan_codecs(struct hdac_softc *sc) +{ + struct hdac_codec *codec; + int i; + uint16_t statests; + + statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (HDAC_STATESTS_SDIWAKE(statests, i)) { + /* We have found a codec. */ + hdac_unlock(sc); + codec = (struct hdac_codec *)malloc(sizeof(*codec), + M_HDAC, M_ZERO | M_NOWAIT); + hdac_lock(sc); + if (codec == NULL) { + device_printf(sc->dev, + "Unable to allocate memory for codec\n"); + continue; + } + codec->verbs_sent = 0; + codec->sc = sc; + codec->cad = i; + sc->codecs[i] = codec; + if (hdac_probe_codec(codec) != 0) + break; + } + } + /* All codecs have been probed, now try to attach drivers to them */ + /* bus_generic_attach(sc->dev); */ +} + +/**************************************************************************** + * void hdac_probe_codec(struct hdac_softc *, int) + * + * Probe a the given codec_id for available function groups. + ****************************************************************************/ +static int +hdac_probe_codec(struct hdac_codec *codec) +{ + struct hdac_softc *sc = codec->sc; + struct hdac_devinfo *devinfo; + uint32_t vendorid, revisionid, subnode; + int startnode; + int endnode; + int i; + nid_t cad = codec->cad; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Probing codec: %d\n", cad); + ); + vendorid = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_VENDOR_ID), + cad); + revisionid = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_REVISION_ID), + cad); + subnode = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_SUB_NODE_COUNT), + cad); + startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); + endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: \tstartnode=%d endnode=%d\n", + startnode, endnode); + ); + for (i = startnode; i < endnode; i++) { + devinfo = hdac_probe_function(codec, i); + if (devinfo != NULL) { + /* XXX Ignore other FG. */ + devinfo->vendor_id = + HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); + devinfo->device_id = + HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); + devinfo->revision_id = + HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); + devinfo->stepping_id = + HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: \tFound AFG nid=%d " + "[startnode=%d endnode=%d]\n", + devinfo->nid, startnode, endnode); + ); + return (1); + } + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: \tAFG not found\n"); + ); + return (0); +} + +static struct hdac_devinfo * +hdac_probe_function(struct hdac_codec *codec, nid_t nid) +{ + struct hdac_softc *sc = codec->sc; + struct hdac_devinfo *devinfo; + uint32_t fctgrptype; + nid_t cad = codec->cad; + + fctgrptype = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_FCT_GRP_TYPE), cad)); + + /* XXX For now, ignore other FG. */ + if (fctgrptype != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) + return (NULL); + + hdac_unlock(sc); + devinfo = (struct hdac_devinfo *)malloc(sizeof(*devinfo), M_HDAC, + M_NOWAIT | M_ZERO); + hdac_lock(sc); + if (devinfo == NULL) { + device_printf(sc->dev, "%s: Unable to allocate ivar\n", + __func__); + return (NULL); + } + + devinfo->nid = nid; + devinfo->node_type = fctgrptype; + devinfo->codec = codec; + + hdac_add_child(sc, devinfo); + + return (devinfo); +} + +static void +hdac_add_child(struct hdac_softc *sc, struct hdac_devinfo *devinfo) +{ + devinfo->dev = device_add_child(sc->dev, NULL, -1); + device_set_ivars(devinfo->dev, (void *)devinfo); + /* XXX - Print more information when booting verbose??? */ +} + +static void +hdac_widget_connection_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t res; + int i, j, max, found, entnum, cnid; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); + + w->nconns = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); + + if (w->nconns < 1) + return; + + entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; + res = 0; + i = 0; + found = 0; + max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; + + while (i < w->nconns) { + res = hdac_command(sc, + HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); + for (j = 0; j < entnum; j++) { + cnid = res; + cnid >>= (32 / entnum) * j; + cnid &= (1 << (32 / entnum)) - 1; + if (cnid == 0) + continue; + if (found > max) { + device_printf(sc->dev, + "node %d: Adding %d: " + "Max connection reached!\n", + nid, cnid); + continue; + } + w->conns[found++] = cnid; + } + i += entnum; + } + + HDA_BOOTVERBOSE( + if (w->nconns != found) { + device_printf(sc->dev, + "HDA_DEBUG: nid=%d WARNING!!! Connection " + "length=%d != found=%d\n", + nid, w->nconns, found); + } + ); +} + +static uint32_t +hdac_widget_pin_getconfig(struct hdac_widget *w) +{ + struct hdac_softc *sc; + uint32_t config, id; + nid_t cad, nid; + + sc = w->devinfo->codec->sc; + cad = w->devinfo->codec->cad; + nid = w->nid; + id = hdac_codec_id(w->devinfo); + + config = hdac_command(sc, + HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), + cad); + /* + * XXX REWRITE!!!! Don't argue! + */ + if (id == HDA_CODEC_ALC880 && + (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || + sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { + /* + * Super broken BIOS + */ + switch (nid) { + case 20: + break; + case 21: + break; + case 22: + break; + case 23: + break; + case 24: /* MIC1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 25: /* XXX MIC2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 26: /* LINE1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: /* XXX LINE2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 28: /* CD */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; + break; + case 30: + break; + case 31: + break; + default: + break; + } + } + + return (config); +} + +static void +hdac_widget_pin_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t config, pincap; + char *devstr, *connstr; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + config = hdac_widget_pin_getconfig(w); + w->wclass.pin.config = config; + + pincap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); + w->wclass.pin.cap = pincap; + + w->wclass.pin.ctrl = hdac_command(sc, + HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) & + ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE); + + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) { + w->param.eapdbtl = hdac_command(sc, + HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid), cad); + w->param.eapdbtl &= 0x7; + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + } else + w->param.eapdbtl = HDAC_INVALID; + + switch (config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: + devstr = "line out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + devstr = "speaker"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: + devstr = "headphones out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + devstr = "CD"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: + devstr = "SPDIF out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: + devstr = "digital (other) out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE: + devstr = "modem, line side"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET: + devstr = "modem, handset side"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + devstr = "line in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: + devstr = "AUX"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + devstr = "Mic in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY: + devstr = "telephony"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: + devstr = "SPDIF in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: + devstr = "digital (other) in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER: + devstr = "other"; + break; + default: + devstr = "unknown"; + break; + } + + switch (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) { + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK: + connstr = "jack"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE: + connstr = "none"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED: + connstr = "fixed"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH: + connstr = "jack / fixed"; + break; + default: + connstr = "unknown"; + break; + } + + strlcat(w->name, ": ", sizeof(w->name)); + strlcat(w->name, devstr, sizeof(w->name)); + strlcat(w->name, " (", sizeof(w->name)); + strlcat(w->name, connstr, sizeof(w->name)); + strlcat(w->name, ")", sizeof(w->name)); +} + +static void +hdac_widget_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t wcap, cap; + char *typestr; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + wcap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), + cad); + w->param.widget_cap = wcap; + w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + typestr = "audio output"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + typestr = "audio input"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + typestr = "audio mixer"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + typestr = "audio selector"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + typestr = "pin"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET: + typestr = "power widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET: + typestr = "volume widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: + typestr = "beep widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET: + typestr = "vendor widget"; + break; + default: + typestr = "unknown type"; + break; + } + + strlcpy(w->name, typestr, sizeof(w->name)); + + if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(wcap)) { + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), + cad); + DELAY(1000); + } + + hdac_widget_connection_parse(w); + + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.outamp_cap = + hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_OUTPUT_AMP_CAP), cad); + else + w->param.outamp_cap = + w->devinfo->function.audio.outamp_cap; + } else + w->param.outamp_cap = 0; + + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.inamp_cap = + hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_INPUT_AMP_CAP), cad); + else + w->param.inamp_cap = + w->devinfo->function.audio.inamp_cap; + } else + w->param.inamp_cap = 0; + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) { + cap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_SUPP_STREAM_FORMATS), cad); + w->param.supp_stream_formats = (cap != 0) ? cap : + w->devinfo->function.audio.supp_stream_formats; + cap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); + w->param.supp_pcm_size_rate = (cap != 0) ? cap : + w->devinfo->function.audio.supp_pcm_size_rate; + } else { + w->param.supp_stream_formats = + w->devinfo->function.audio.supp_stream_formats; + w->param.supp_pcm_size_rate = + w->devinfo->function.audio.supp_pcm_size_rate; + } + } else { + w->param.supp_stream_formats = 0; + w->param.supp_pcm_size_rate = 0; + } + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_widget_pin_parse(w); +} + +static struct hdac_widget * +hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid) +{ + if (devinfo == NULL || devinfo->widget == NULL || + nid < devinfo->startnode || nid >= devinfo->endnode) + return (NULL); + return (&devinfo->widget[nid - devinfo->startnode]); +} + +static void +hdac_stream_stop(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN); + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~(1 << (ch->off >> 5)); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); +} + +static void +hdac_stream_start(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= 1 << (ch->off >> 5); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); +} + +static void +hdac_stream_reset(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int timeout = 1000; + int to = timeout; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + do { + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + if (ctl & HDAC_SDCTL_SRST) + break; + DELAY(10); + } while (--to); + if (!(ctl & HDAC_SDCTL_SRST)) { + device_printf(sc->dev, "timeout in reset\n"); + } + ctl &= ~HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + to = timeout; + do { + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + if (!(ctl & HDAC_SDCTL_SRST)) + break; + DELAY(10); + } while (--to); + if (ctl & HDAC_SDCTL_SRST) + device_printf(sc->dev, "can't reset!\n"); +} + +static void +hdac_stream_setid(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL2); + ctl &= ~HDAC_SDCTL2_STRM_MASK; + ctl |= ch->sid << HDAC_SDCTL2_STRM_SHIFT; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL2, ctl); +} + +static void +hdac_bdl_setup(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint64_t addr; + int blks, size, blocksize; + struct hdac_bdle *bdle; + int i; + + addr = (uint64_t)sndbuf_getbufaddr(ch->b); + size = sndbuf_getsize(ch->b); + blocksize = sndbuf_getblksz(ch->b); + blks = size / blocksize; + bdle = (struct hdac_bdle*)ch->bdl_dma.dma_vaddr; + + for (i = 0; i < blks; i++, bdle++) { + bdle->addrl = (uint32_t)addr; + bdle->addrh = (uint32_t)(addr >> 32); + bdle->len = blocksize; + bdle->ioc = 1; + + addr += blocksize; + } + + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, size); + HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blks - 1); + addr = ch->bdl_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr); + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); +} + +static int +hdac_bdl_alloc(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int rc; + + rc = hdac_dma_alloc(sc, &ch->bdl_dma, + sizeof(struct hdac_bdle) * HDA_BDL_MAX); + if (rc) { + device_printf(sc->dev, "can't alloc bdl\n"); + return (rc); + } + hdac_dma_nocache(ch->bdl_dma.dma_vaddr); + + return (0); +} + +static void +hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid, + int index, int lmute, int rmute, + int left, int right, int dir) +{ + uint16_t v = 0; + + if (sc == NULL) + return; + + if (left != right || lmute != rmute) { + v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | + (lmute << 7) | left; + hdac_command(sc, + HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); + v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | + (rmute << 7) | right; + } else + v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | + (lmute << 7) | left; + + hdac_command(sc, + HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); +} + +static void +hdac_audio_ctl_amp_set(struct hdac_audio_ctl *ctl, uint32_t mute, + int left, int right) +{ + struct hdac_softc *sc; + nid_t nid, cad; + int lmute, rmute; + + if (ctl == NULL || ctl->widget == NULL || + ctl->widget->devinfo == NULL || + ctl->widget->devinfo->codec == NULL || + ctl->widget->devinfo->codec->sc == NULL) + return; + + sc = ctl->widget->devinfo->codec->sc; + cad = ctl->widget->devinfo->codec->cad; + nid = ctl->widget->nid; + + if (mute == HDA_AMP_MUTE_DEFAULT) { + lmute = HDA_AMP_LEFT_MUTED(ctl->muted); + rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); + } else { + lmute = HDA_AMP_LEFT_MUTED(mute); + rmute = HDA_AMP_RIGHT_MUTED(mute); + } + + if (ctl->dir & HDA_CTL_OUT) + hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, + lmute, rmute, left, right, 0); + if (ctl->dir & HDA_CTL_IN) + hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, + lmute, rmute, left, right, 1); + ctl->left = left; + ctl->right = right; +} + +static void +hdac_widget_connection_select(struct hdac_widget *w, uint8_t index) +{ + if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) + return; + hdac_command(w->devinfo->codec->sc, + HDA_CMD_SET_CONNECTION_SELECT_CONTROL(w->devinfo->codec->cad, + w->nid, index), w->devinfo->codec->cad); + w->selconn = index; +} + + +/**************************************************************************** + * uint32_t hdac_command_sendone_internal + * + * Wrapper function that sends only one command to a given codec + ****************************************************************************/ +static uint32_t +hdac_command_sendone_internal(struct hdac_softc *sc, uint32_t verb, nid_t cad) +{ + struct hdac_command_list cl; + uint32_t response = HDAC_INVALID; + + if (!hdac_lockowned(sc)) + device_printf(sc->dev, "WARNING!!!! mtx not owned!!!!\n"); + cl.num_commands = 1; + cl.verbs = &verb; + cl.responses = &response; + + hdac_command_send_internal(sc, &cl, cad); + + return (response); +} + +/**************************************************************************** + * hdac_command_send_internal + * + * Send a command list to the codec via the corb. We queue as much verbs as + * we can and msleep on the codec. When the interrupt get the responses + * back from the rirb, it will wake us up so we can queue the remaining verbs + * if any. + ****************************************************************************/ +static void +hdac_command_send_internal(struct hdac_softc *sc, + struct hdac_command_list *commands, nid_t cad) +{ + struct hdac_codec *codec; + int corbrp; + uint32_t *corb; + uint8_t rirbwp; + int timeout; + int retry = 10; + struct hdac_rirb *rirb_base, *rirb; + nid_t ucad; + uint32_t utag; + + if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL) + return; + + codec = sc->codecs[cad]; + codec->commands = commands; + codec->responses_received = 0; + codec->verbs_sent = 0; + corb = (uint32_t *)sc->corb_dma.dma_vaddr; + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + + do { + if (codec->verbs_sent != commands->num_commands) { + /* Queue as many verbs as possible */ + corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP); + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); + while (codec->verbs_sent != commands->num_commands && + ((sc->corb_wp + 1) % sc->corb_size) != corbrp) { + sc->corb_wp++; + sc->corb_wp %= sc->corb_size; + corb[sc->corb_wp] = + commands->verbs[codec->verbs_sent++]; + } + + /* Send the verbs to the codecs */ + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); + } + + timeout = 1000; + do { + rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_POSTREAD); + if (sc->rirb_rp != rirbwp) { + do { + sc->rirb_rp++; + sc->rirb_rp %= sc->rirb_size; + rirb = &rirb_base[sc->rirb_rp]; + if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { + ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); + utag = rirb->response >> 26; + if (ucad > -1 && ucad < HDAC_CODEC_MAX && + sc->codecs[ucad] != NULL) { + sc->unsolq[sc->unsolq_wp++] = + (ucad << 16) | + (utag & 0xffff); + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } + } else if (codec->responses_received < commands->num_commands) + codec->commands->responses[codec->responses_received++] = + rirb->response; + } while (sc->rirb_rp != rirbwp); + break; + } + DELAY(10); + } while (--timeout); + } while ((codec->verbs_sent != commands->num_commands || + codec->responses_received != commands->num_commands) && + --retry); + + if (retry == 0) + device_printf(sc->dev, + "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", + __func__, commands->num_commands, + codec->verbs_sent, codec->responses_received); + + codec->verbs_sent = 0; + + if (sc->unsolq_st == HDAC_UNSOLQ_READY) { + sc->unsolq_st = HDAC_UNSOLQ_BUSY; + while (sc->unsolq_rp != sc->unsolq_wp) { + ucad = sc->unsolq[sc->unsolq_rp] >> 16; + utag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + hdac_unsolicited_handler(sc->codecs[ucad], utag); + } + sc->unsolq_st = HDAC_UNSOLQ_READY; + } +} + + +/**************************************************************************** + * Device Methods + ****************************************************************************/ + +/**************************************************************************** + * int hdac_probe(device_t) + * + * Probe for the presence of an hdac. If none is found, check for a generic + * match using the subclass of the device. + ****************************************************************************/ +static int +hdac_probe(device_t dev) +{ + int i, result; + uint32_t model; + uint16_t class, subclass; + char desc[64]; + + model = (uint32_t)pci_get_device(dev) << 16; + model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; + class = pci_get_class(dev); + subclass = pci_get_subclass(dev); + + bzero(desc, sizeof(desc)); + result = ENXIO; + for (i = 0; i < HDAC_DEVICES_LEN; i++) { + if (hdac_devices[i].model == model) { + strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); + result = BUS_PROBE_DEFAULT; + break; + } + if (HDA_DEV_MATCH(hdac_devices[i].model, model) && + class == PCIC_MULTIMEDIA && + subclass == PCIS_MULTIMEDIA_HDA) { + strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); + result = BUS_PROBE_GENERIC; + break; + } + } + if (result == ENXIO && class == PCIC_MULTIMEDIA && + subclass == PCIS_MULTIMEDIA_HDA) { + strlcpy(desc, "Generic", sizeof(desc)); + result = BUS_PROBE_GENERIC; + } + if (result != ENXIO) { + strlcat(desc, " High Definition Audio Controller", + sizeof(desc)); + device_set_desc_copy(dev, desc); + } + + return (result); +} + +static void * +hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct hdac_devinfo *devinfo = data; + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_chan *ch; + + hdac_lock(sc); + if (dir == PCMDIR_PLAY) { + ch = &sc->play; + ch->off = (sc->num_iss + devinfo->function.audio.playcnt) << 5; + ch->dir = PCMDIR_PLAY; + ch->sid = ++sc->streamcnt; + devinfo->function.audio.playcnt++; + } else { + ch = &sc->rec; + ch->off = devinfo->function.audio.reccnt << 5; + ch->dir = PCMDIR_REC; + ch->sid = ++sc->streamcnt; + devinfo->function.audio.reccnt++; + } + if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { + ch->caps.minspeed = ch->caps.maxspeed = 48000; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + } + ch->b = b; + ch->c = c; + ch->devinfo = devinfo; + ch->blksz = sc->chan_size / sc->chan_blkcnt; + ch->blkcnt = sc->chan_blkcnt; + hdac_unlock(sc); + + if (hdac_bdl_alloc(ch) != 0) { + ch->blkcnt = 0; + return (NULL); + } + + if (sndbuf_alloc(ch->b, sc->chan_dmat, sc->chan_size) != 0) + return (NULL); + + hdac_dma_nocache(ch->b->buf); + + return (ch); +} + +static int +hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct hdac_chan *ch = data; + int i; + + for (i = 0; ch->caps.fmtlist[i] != 0; i++) { + if (format == ch->caps.fmtlist[i]) { + ch->fmt = format; + return (0); + } + } + + return (EINVAL); +} + +static int +hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + struct hdac_chan *ch = data; + uint32_t spd = 0; + int i; + + for (i = 0; ch->pcmrates[i] != 0; i++) { + spd = ch->pcmrates[i]; + if (spd >= speed) + break; + } + + if (spd == 0) + ch->spd = 48000; + else + ch->spd = spd; + + return (ch->spd); +} + +static void +hdac_stream_setup(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int i; + nid_t cad = ch->devinfo->codec->cad; + uint16_t fmt; + + fmt = 0; + if (ch->fmt & AFMT_S16_LE) + fmt |= ch->bit16 << 4; + else if (ch->fmt & AFMT_S32_LE) + fmt |= ch->bit32 << 4; + else + fmt |= 1 << 4; + + for (i = 0; i < HDA_RATE_TAB_LEN; i++) { + if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) { + fmt |= hda_rate_tab[i].base; + fmt |= hda_rate_tab[i].mul; + fmt |= hda_rate_tab[i].div; + break; + } + } + + if (ch->fmt & AFMT_STEREO) + fmt |= 1; + + HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); + + for (i = 0; ch->io[i] != -1; i++) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: PCMDIR_%s: Stream setup nid=%d " + "fmt=0x%08x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->io[i], fmt); + ); + hdac_command(sc, + HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); + hdac_command(sc, + HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], + ch->sid << 4), cad); + } +} + +static int +hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blocksize) +{ + struct hdac_chan *ch = data; + + sndbuf_resize(ch->b, ch->blkcnt, ch->blksz); + + return (ch->blksz); +} + +static void +hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) +{ + struct hdac_devinfo *devinfo = ch->devinfo; + nid_t cad = devinfo->codec->cad; + int i; + + hdac_stream_stop(ch); + + for (i = 0; ch->io[i] != -1; i++) { + hdac_command(sc, + HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], + 0), cad); + } +} + +static void +hdac_channel_start(struct hdac_softc *sc, struct hdac_chan *ch) +{ + ch->ptr = 0; + ch->prevptr = 0; + hdac_stream_stop(ch); + hdac_stream_reset(ch); + hdac_bdl_setup(ch); + hdac_stream_setid(ch); + hdac_stream_setup(ch); + hdac_stream_start(ch); +} + +static int +hdac_channel_trigger(kobj_t obj, void *data, int go) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + + hdac_lock(sc); + switch (go) { + case PCMTRIG_START: + hdac_channel_start(sc, ch); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + hdac_channel_stop(sc, ch); + break; + } + hdac_unlock(sc); + + return (0); +} + +static int +hdac_channel_getptr(kobj_t obj, void *data) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + int sz, delta; + uint32_t ptr; + + hdac_lock(sc); + ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); + hdac_unlock(sc); + + sz = sndbuf_getsize(ch->b); + ptr %= sz; + + if (ch->dir == PCMDIR_REC) { + delta = ptr % sndbuf_getblksz(ch->b); + if (delta != 0) { + ptr -= delta; + if (ptr < delta) + ptr = sz - delta; + else + ptr -= delta; + } + } + + return (ptr); +} + +static struct pcmchan_caps * +hdac_channel_getcaps(kobj_t obj, void *data) +{ + return (&((struct hdac_chan *)data)->caps); +} + +static kobj_method_t hdac_channel_methods[] = { + KOBJMETHOD(channel_init, hdac_channel_init), + KOBJMETHOD(channel_setformat, hdac_channel_setformat), + KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), + KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), + KOBJMETHOD(channel_trigger, hdac_channel_trigger), + KOBJMETHOD(channel_getptr, hdac_channel_getptr), + KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(hdac_channel); + +static int +hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + uint32_t mask, recmask, id; + int i, j, softpcmvol; + nid_t cad; + + hdac_lock(sc); + + mask = 0; + recmask = 0; + + id = hdac_codec_id(devinfo); + cad = devinfo->codec->cad; + for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_hp_switch[i].model, + sc->pci_subvendor) && + hdac_hp_switch[i].id == id)) + continue; + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->enable != 0 + && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { + hdac_command(sc, + HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, + w->nid, + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE| + HDAC_UNSOLTAG_EVENT_HP), cad); + hdac_hp_switch_handler(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Enabling headphone/speaker " + "audio routing switching:\n"); + device_printf(sc->dev, + "HDA_DEBUG: \tindex=%d nid=%d " + "pci_subvendor=0x%08x " + "codec=0x%08x\n", + i, w->nid, sc->pci_subvendor, id); + ); + } + break; + } + for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_eapd_switch[i].model, + sc->pci_subvendor) && + hdac_eapd_switch[i].id == id)) + continue; + w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); + if (w == NULL || w->enable == 0) + break; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID) + break; + mask |= SOUND_MASK_OGAIN; + break; + } + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + mask |= w->ctlflags; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + recmask |= cw->ctlflags; + } + } + + if (!(mask & SOUND_MASK_PCM)) { + softpcmvol = 1; + mask |= SOUND_MASK_PCM; + } else + softpcmvol = (devinfo->function.audio.quirks & + HDA_QUIRK_SOFTPCMVOL) ? 1 : 0; + + i = 0; + ctl = NULL; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (!(ctl->ossmask & SOUND_MASK_PCM)) + continue; + if (ctl->step > 0) + break; + } + + if (softpcmvol == 1 || ctl == NULL) { + struct snddev_info *d = NULL; + d = device_get_softc(sc->dev); + if (d != NULL) { + d->flags |= SD_F_SOFTPCMVOL; + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: %s Soft PCM volume\n", + (softpcmvol == 1) ? + "Forcing" : "Enabling"); + ); + } + i = 0; + /* + * XXX Temporary quirk for STAC9220, until the parser + * become smarter. + */ + if (id == HDA_CODEC_STAC9220) { + mask |= SOUND_MASK_VOLUME; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (ctl->widget->nid == 11 && ctl->index == 0) { + ctl->ossmask = SOUND_MASK_VOLUME; + ctl->ossval = 100 | (100 << 8); + } else + ctl->ossmask &= ~SOUND_MASK_VOLUME; + } + } else { + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + if (!(mask & SOUND_MASK_VOLUME)) + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (!HDA_FLAG_MATCH(ctl->ossmask, + SOUND_MASK_VOLUME | SOUND_MASK_PCM)) + continue; + if (!(ctl->mute == 1 && ctl->step == 0)) + ctl->enable = 0; + } + } + } + + recmask &= ~(SOUND_MASK_PCM | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER); + + mix_setrecdevs(m, recmask); + mix_setdevs(m, mask); + + hdac_unlock(sc); + + return (0); +} + +static int +hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t id, mute; + int lvol, rvol, mlvol, mrvol; + int i = 0; + + hdac_lock(sc); + if (dev == SOUND_MIXER_OGAIN) { + uint32_t orig; + /*if (left != right || !(left == 0 || left == 1)) { + hdac_unlock(sc); + return (-1); + }*/ + id = hdac_codec_id(devinfo); + for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { + if (HDA_DEV_MATCH(hdac_eapd_switch[i].model, + sc->pci_subvendor) && + hdac_eapd_switch[i].id == id) + break; + } + if (i >= HDAC_EAPD_SWITCH_LEN) { + hdac_unlock(sc); + return (-1); + } + w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); + if (w == NULL || + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID) { + hdac_unlock(sc); + return (-1); + } + orig = w->param.eapdbtl; + if (left == 0) + w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + else + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + if (orig != w->param.eapdbtl) { + uint32_t val; + + if (hdac_eapd_switch[i].hp_switch != 0) + hdac_hp_switch_handler(devinfo); + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, + w->nid, val), devinfo->codec->cad); + } + hdac_unlock(sc); + return (left | (left << 8)); + } + if (dev == SOUND_MIXER_VOLUME) + devinfo->function.audio.mvol = left | (right << 8); + + mlvol = devinfo->function.audio.mvol & 0x7f; + mrvol = (devinfo->function.audio.mvol >> 8) & 0x7f; + lvol = 0; + rvol = 0; + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL || ctl->enable == 0 || + !(ctl->ossmask & (1 << dev))) + continue; + switch (dev) { + case SOUND_MIXER_VOLUME: + lvol = ((ctl->ossval & 0x7f) * left) / 100; + lvol = (lvol * ctl->step) / 100; + rvol = (((ctl->ossval >> 8) & 0x7f) * right) / 100; + rvol = (rvol * ctl->step) / 100; + break; + default: + if (ctl->ossmask & SOUND_MASK_VOLUME) { + lvol = (left * mlvol) / 100; + lvol = (lvol * ctl->step) / 100; + rvol = (right * mrvol) / 100; + rvol = (rvol * ctl->step) / 100; + } else { + lvol = (left * ctl->step) / 100; + rvol = (right * ctl->step) / 100; + } + ctl->ossval = left | (right << 8); + break; + } + mute = 0; + if (ctl->step < 1) { + mute |= (left == 0) ? HDA_AMP_MUTE_LEFT : + (ctl->muted & HDA_AMP_MUTE_LEFT); + mute |= (right == 0) ? HDA_AMP_MUTE_RIGHT : + (ctl->muted & HDA_AMP_MUTE_RIGHT); + } else { + mute |= (lvol == 0) ? HDA_AMP_MUTE_LEFT : + (ctl->muted & HDA_AMP_MUTE_LEFT); + mute |= (rvol == 0) ? HDA_AMP_MUTE_RIGHT : + (ctl->muted & HDA_AMP_MUTE_RIGHT); + } + hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); + } + hdac_unlock(sc); + + return (left | (right << 8)); +} + +static int +hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + uint32_t ret = src, target; + int i, j; + + target = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (src & (1 << i)) { + target = 1 << i; + break; + } + } + + hdac_lock(sc); + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if ((target == SOUND_MASK_VOLUME && + cw->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + (target != SOUND_MASK_VOLUME && + cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)) + continue; + if (cw->ctlflags & target) { + hdac_widget_connection_select(w, j); + ret = target; + j += w->nconns; + } + } + } + + hdac_unlock(sc); + + return (ret); +} + +static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { + KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init), + KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set), + KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(hdac_audio_ctl_ossmixer); + +/**************************************************************************** + * int hdac_attach(device_t) + * + * Attach the device into the kernel. Interrupts usually won't be enabled + * when this function is called. Setup everything that doesn't require + * interrupts and defer probing of codecs until interrupts are enabled. + ****************************************************************************/ +static int +hdac_attach(device_t dev) +{ + struct hdac_softc *sc; + int result; + int i = 0; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return (ENOMEM); + } + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); + if (sc->lock == NULL) { + device_printf(dev, "mutex creation failed\n"); + free(sc, M_DEVBUF); + return (ENOMEM); + } + + sc->dev = dev; + sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; + sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff; + + sc->chan_size = pcm_getbuffersize(dev, + HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_DEFAULT); + if (resource_int_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "blocksize", &i) == 0 && + i > 0) { + sc->chan_blkcnt = sc->chan_size / i; + i = 0; + while (sc->chan_blkcnt >> i) + i++; + sc->chan_blkcnt = 1 << (i - 1); + if (sc->chan_blkcnt < HDA_BDL_MIN) + sc->chan_blkcnt = HDA_BDL_MIN; + else if (sc->chan_blkcnt > HDA_BDL_MAX) + sc->chan_blkcnt = HDA_BDL_MAX; + } else + sc->chan_blkcnt = HDA_BDL_DEFAULT; + + result = bus_dma_tag_create(NULL, /* parent */ + HDAC_DMA_ALIGNMENT, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, /* filtfunc */ + NULL, /* fistfuncarg */ + sc->chan_size, /* maxsize */ + 1, /* nsegments */ + sc->chan_size, /* maxsegsz */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &sc->chan_dmat); /* dmat */ + if (result != 0) { + device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", + __func__, result); + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + return (ENXIO); + } + + + sc->hdabus = NULL; + for (i = 0; i < HDAC_CODEC_MAX; i++) + sc->codecs[i] = NULL; + + pci_enable_busmaster(dev); + + /* Allocate resources */ + result = hdac_mem_alloc(sc); + if (result != 0) + goto hdac_attach_fail; + result = hdac_irq_alloc(sc); + if (result != 0) + goto hdac_attach_fail; + + /* Get Capabilities */ + result = hdac_get_capabilities(sc); + if (result != 0) + goto hdac_attach_fail; + + /* Allocate CORB and RIRB dma memory */ + result = hdac_dma_alloc(sc, &sc->corb_dma, + sc->corb_size * sizeof(uint32_t)); + if (result != 0) + goto hdac_attach_fail; + result = hdac_dma_alloc(sc, &sc->rirb_dma, + sc->rirb_size * sizeof(struct hdac_rirb)); + if (result != 0) + goto hdac_attach_fail; + + /* Quiesce everything */ + hdac_reset(sc); + + /* Disable PCI-Express QOS */ + pci_write_config(sc->dev, 0x44, + pci_read_config(sc->dev, 0x44, 1) & 0xf8, 1); + + /* Initialize the CORB and RIRB */ + hdac_corb_init(sc); + hdac_rirb_init(sc); + + /* Defer remaining of initialization until interrupts are enabled */ + sc->intrhook.ich_func = hdac_attach2; + sc->intrhook.ich_arg = (void *)sc; + if (cold == 0 || config_intrhook_establish(&sc->intrhook) != 0) { + sc->intrhook.ich_func = NULL; + hdac_attach2((void *)sc); + } + + return (0); + +hdac_attach_fail: + hdac_dma_free(&sc->rirb_dma); + hdac_dma_free(&sc->corb_dma); + hdac_irq_free(sc); + hdac_mem_free(sc); + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + + return (ENXIO); +} + +static void +hdac_audio_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + uint32_t res; + int i; + nid_t cad, nid; + + cad = devinfo->codec->cad; + nid = devinfo->nid; + + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), cad); + + DELAY(100); + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); + + devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); + devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); + devinfo->endnode = devinfo->startnode + devinfo->nodecnt; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, " Vendor: 0x%08x\n", + devinfo->vendor_id); + device_printf(sc->dev, " Device: 0x%08x\n", + devinfo->device_id); + device_printf(sc->dev, " Revision: 0x%08x\n", + devinfo->revision_id); + device_printf(sc->dev, " Stepping: 0x%08x\n", + devinfo->stepping_id); + device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", + sc->pci_subvendor); + device_printf(sc->dev, " Nodes: start=%d " + "endnode=%d total=%d\n", + devinfo->startnode, devinfo->endnode, devinfo->nodecnt); + ); + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), + cad); + devinfo->function.audio.supp_stream_formats = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), + cad); + devinfo->function.audio.supp_pcm_size_rate = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), + cad); + devinfo->function.audio.outamp_cap = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), + cad); + devinfo->function.audio.inamp_cap = res; + + if (devinfo->nodecnt > 0) { + hdac_unlock(sc); + devinfo->widget = (struct hdac_widget *)malloc( + sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, + M_NOWAIT | M_ZERO); + hdac_lock(sc); + } else + devinfo->widget = NULL; + + if (devinfo->widget == NULL) { + device_printf(sc->dev, "unable to allocate widgets!\n"); + devinfo->endnode = devinfo->startnode; + devinfo->nodecnt = 0; + return; + } + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) + device_printf(sc->dev, "Ghost widget! nid=%d!\n", i); + else { + w->devinfo = devinfo; + w->nid = i; + w->enable = 1; + w->selconn = -1; + w->pflags = 0; + w->ctlflags = 0; + w->param.eapdbtl = HDAC_INVALID; + hdac_widget_parse(w); + } + } +} + +static void +hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_ctl *ctls; + struct hdac_widget *w, *cw; + int i, j, cnt, max, ocap, icap; + int mute, offset, step, size; + + /* XXX This is redundant */ + max = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->param.outamp_cap != 0) + max++; + if (w->param.inamp_cap != 0) { + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + max++; + } + break; + default: + max++; + break; + } + } + } + + devinfo->function.audio.ctlcnt = max; + + if (max < 1) + return; + + hdac_unlock(sc); + ctls = (struct hdac_audio_ctl *)malloc( + sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); + hdac_lock(sc); + + if (ctls == NULL) { + /* Blekh! */ + device_printf(sc->dev, "unable to allocate ctls!\n"); + devinfo->function.audio.ctlcnt = 0; + return; + } + + cnt = 0; + for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { + if (cnt >= max) { + device_printf(sc->dev, "%s: Ctl overflow!\n", + __func__); + break; + } + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + ocap = w->param.outamp_cap; + icap = w->param.inamp_cap; + if (ocap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: BUGGY outamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_OUT; + } + + if (icap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: BUGGY inamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + if (cnt >= max) { + device_printf(sc->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + cw = hdac_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].childwidget = cw; + ctls[cnt].index = j; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_IN; + } + break; + default: + if (cnt >= max) { + device_printf(sc->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_IN; + break; + } + } + } + + devinfo->function.audio.ctl = ctls; +} + +static const struct { + uint32_t model; + uint32_t id; + uint32_t set, unset; +} hdac_quirks[] = { + /* + * XXX Fixed rate quirk. Other than 48000 + * sounds pretty much like train wreck. + * + * XXX Force stereo quirk. Monoural recording / playback + * on few codecs (especially ALC880) seems broken or + * perhaps unsupported. + */ + { HDA_MATCH_ALL, HDA_MATCH_ALL, + HDA_QUIRK_FIXEDRATE | HDA_QUIRK_FORCESTEREO, 0 }, + { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, + HDA_QUIRK_GPIO1, 0 }, + { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, + HDA_QUIRK_GPIO1, 0 }, + { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { ASUS_A8JC_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { HDA_MATCH_ALL, HDA_CODEC_CXVENICE, + 0, HDA_QUIRK_FORCESTEREO }, + { HDA_MATCH_ALL, HDA_CODEC_STACXXXX, + HDA_QUIRK_SOFTPCMVOL, 0 } +}; +#define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) + +static void +hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + uint32_t id, subvendor; + int i; + + id = hdac_codec_id(devinfo); + subvendor = devinfo->codec->sc->pci_subvendor; + + /* + * Quirks + */ + for (i = 0; i < HDAC_QUIRKS_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subvendor) && + HDA_DEV_MATCH(hdac_quirks[i].id, id))) + continue; + if (hdac_quirks[i].set != 0) + devinfo->function.audio.quirks |= + hdac_quirks[i].set; + if (hdac_quirks[i].unset != 0) + devinfo->function.audio.quirks &= + ~(hdac_quirks[i].unset); + } + + switch (id) { + case HDA_CODEC_ALC260: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->nid != 5) + w->enable = 0; + } + break; + case HDA_CODEC_ALC880: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->nid != 9 && w->nid != 29) { + w->enable = 0; + } else if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET && + w->nid == 29) { + w->type = + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET; + w->param.widget_cap &= + ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; + w->param.widget_cap |= + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; + strlcpy(w->name, "beep widget", sizeof(w->name)); + } + } + break; + case HDA_CODEC_AD1981HD: + w = hdac_widget_get(devinfo, 11); + if (w != NULL && w->enable != 0 && w->nconns > 3) + w->selconn = 3; + if (subvendor == IBM_M52_SUBVENDOR) { + struct hdac_audio_ctl *ctl; + + ctl = hdac_audio_ctl_amp_get(devinfo, 7, 0, 1); + if (ctl != NULL) + ctl->ossmask = SOUND_MASK_SPEAKER; + } + break; + case HDA_CODEC_AD1986A: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (w->nid != 3) + w->enable = 0; + } + break; + case HDA_CODEC_STAC9221: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (w->nid != 2) + w->enable = 0; + } + break; + case HDA_CODEC_STAC9221D: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->nid != 6) + w->enable = 0; + + } + break; + default: + break; + } +} + +static int +hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *devinfo) +{ + int *dev = &devinfo->function.audio.ossidx; + + while (*dev < SOUND_MIXER_NRDEVICES) { + switch (*dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + case SOUND_MIXER_PCM: + case SOUND_MIXER_SPEAKER: + case SOUND_MIXER_LINE: + case SOUND_MIXER_MIC: + case SOUND_MIXER_CD: + case SOUND_MIXER_RECLEV: + case SOUND_MIXER_OGAIN: /* reserved for EAPD switch */ + (*dev)++; + break; + default: + return (*dev)++; + break; + } + } + + return (-1); +} + +static int +hdac_widget_find_dac_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w; + int i, ret = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + w->pflags |= HDA_DAC_PATH; + ret = 1; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + for (i = 0; i < w->nconns; i++) { + if (hdac_widget_find_dac_path(devinfo, + w->conns[i], depth + 1) != 0) { + if (w->selconn == -1) + w->selconn = i; + ret = 1; + w->pflags |= HDA_DAC_PATH; + } + } + break; + default: + break; + } + return (ret); +} + +static int +hdac_widget_find_adc_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w; + int i, conndev, ret = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (i = 0; i < w->nconns; i++) { + if (hdac_widget_find_adc_path(devinfo, w->conns[i], + depth + 1) != 0) { + if (w->selconn == -1) + w->selconn = i; + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + } + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && + (conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_CD || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN)) { + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + break; + /*case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + if (w->pflags & HDA_DAC_PATH) { + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + break;*/ + default: + break; + } + return (ret); +} + +static uint32_t +hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo, + nid_t nid, nid_t pnid, int index, int depth) +{ + struct hdac_widget *w, *pw; + struct hdac_audio_ctl *ctl; + uint32_t fl = 0; + int i, ossdev, conndev, strategy; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + + pw = hdac_widget_get(devinfo, pnid); + strategy = devinfo->function.audio.parsing_strategy; + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER + || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) { + for (i = 0; i < w->nconns; i++) { + fl |= hdac_audio_ctl_outamp_build(devinfo, w->conns[i], + w->nid, i, depth + 1); + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT && + (w->pflags & HDA_DAC_PATH)) { + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if ((ctl->widget->nid == w->nid) || + (ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) { + /*if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_PCM; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_PCM; + ctl->ossdev = SOUND_MIXER_PCM;*/ + if (!(w->ctlflags & SOUND_MASK_PCM) || + (pw != NULL && + !(pw->ctlflags & SOUND_MASK_PCM))) { + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_PCM; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_PCM; + ctl->ossdev = SOUND_MIXER_PCM; + w->ctlflags |= SOUND_MASK_VOLUME; + w->ctlflags |= SOUND_MASK_PCM; + if (pw != NULL) { + if (pw->selconn == -1) + pw->selconn = index; + pw->ctlflags |= + SOUND_MASK_VOLUME; + pw->ctlflags |= + SOUND_MASK_PCM; + } + } + } + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX + && HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && + (w->pflags & HDA_ADC_PATH)) { + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if (((ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { + if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + ossdev = 0; + switch (conndev) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + ossdev = SOUND_MIXER_MIC; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + ossdev = SOUND_MIXER_LINE; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + ossdev = SOUND_MIXER_CD; + break; + default: + ossdev = + hdac_audio_ctl_ossmixer_getnextdev( + devinfo); + if (ossdev < 0) + ossdev = 0; + break; + } + if (strategy == HDA_PARSE_MIXER) { + fl |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_VOLUME; + } + fl |= 1 << ossdev; + ctl->ossmask |= 1 << ossdev; + ctl->ossdev = ossdev; + } + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if (((ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { + if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_SPEAKER; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_SPEAKER; + ctl->ossdev = SOUND_MIXER_SPEAKER; + } + } + w->ctlflags |= fl; + return (fl); + } + return (0); +} + +static uint32_t +hdac_audio_ctl_inamp_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + uint32_t fl; + int i; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + /*if (!(w->pflags & HDA_ADC_PATH)) + return (0); + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + return (0);*/ + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (ctl->widget->nid == nid) { + ctl->ossmask |= SOUND_MASK_RECLEV; + w->ctlflags |= SOUND_MASK_RECLEV; + return (SOUND_MASK_RECLEV); + } + } + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) + continue; + fl = hdac_audio_ctl_inamp_build(devinfo, cw->nid, depth + 1); + if (fl != 0) { + cw->ctlflags |= fl; + w->ctlflags |= fl; + return (fl); + } + } + return (0); +} + +static int +hdac_audio_ctl_recsel_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + int i, child = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + /*if (!(w->pflags & HDA_ADC_PATH)) + return (0); + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + return (0);*/ + /* XXX weak! */ + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL) + continue; + if (++child > 1) { + w->pflags |= HDA_ADC_RECSEL; + return (1); + } + } + for (i = 0; i < w->nconns; i++) { + if (hdac_audio_ctl_recsel_build(devinfo, + w->conns[i], depth + 1) != 0) + return (1); + } + return (0); +} + +static int +hdac_audio_build_tree_strategy(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w, *cw; + int i, j, conndev, found_dac = 0; + int strategy; + + strategy = devinfo->function.audio.parsing_strategy; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (!HDA_PARAM_PIN_CAP_OUTPUT_CAP(w->wclass.pin.cap)) + continue; + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (!(conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (strategy == HDA_PARSE_MIXER && !(cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || + cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + continue; + if (hdac_widget_find_dac_path(devinfo, cw->nid, 0) + != 0) { + if (w->selconn == -1) + w->selconn = j; + w->pflags |= HDA_DAC_PATH; + found_dac++; + } + } + } + + return (found_dac); +} + +static void +hdac_audio_build_tree(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + int i, j, dacs, strategy; + + /* Construct DAC path */ + strategy = HDA_PARSE_MIXER; + devinfo->function.audio.parsing_strategy = strategy; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: HDA Widget Parser - Revision %d\n", + HDA_WIDGET_PARSER_REV); + ); + dacs = hdac_audio_build_tree_strategy(devinfo); + if (dacs == 0) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: 0 DAC path found! " + "Retrying parser " + "using HDA_PARSE_DIRECT strategy.\n"); + ); + strategy = HDA_PARSE_DIRECT; + devinfo->function.audio.parsing_strategy = strategy; + dacs = hdac_audio_build_tree_strategy(devinfo); + } + + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: Found %d DAC path using HDA_PARSE_%s " + "strategy.\n", + dacs, (strategy == HDA_PARSE_MIXER) ? "MIXER" : "DIRECT"); + ); + + /* Construct ADC path */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + (void)hdac_widget_find_adc_path(devinfo, w->nid, 0); + } + + /* Output mixers */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if ((strategy == HDA_PARSE_MIXER && + (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) + && (w->pflags & HDA_DAC_PATH)) || + (strategy == HDA_PARSE_DIRECT && (w->pflags & + (HDA_DAC_PATH | HDA_ADC_PATH)))) { + w->ctlflags |= hdac_audio_ctl_outamp_build(devinfo, + w->nid, devinfo->startnode - 1, 0, 0); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { + j = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &j)) != + NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (ctl->widget->nid != w->nid) + continue; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_SPEAKER; + ctl->ossdev = SOUND_MIXER_SPEAKER; + w->ctlflags |= SOUND_MASK_VOLUME; + w->ctlflags |= SOUND_MASK_SPEAKER; + } + } + } + + /* Input mixers (rec) */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->pflags & HDA_ADC_PATH)) + continue; + hdac_audio_ctl_inamp_build(devinfo, w->nid, 0); + hdac_audio_ctl_recsel_build(devinfo, w->nid, 0); + } +} + +#define HDA_COMMIT_CONN (1 << 0) +#define HDA_COMMIT_CTRL (1 << 1) +#define HDA_COMMIT_EAPD (1 << 2) +#define HDA_COMMIT_GPIO (1 << 3) +#define HDA_COMMIT_ALL (HDA_COMMIT_CONN | HDA_COMMIT_CTRL | \ + HDA_COMMIT_EAPD | HDA_COMMIT_GPIO) + +static void +hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + nid_t cad, nid; + int i, gpioval; + + if (!(cfl & HDA_COMMIT_ALL)) + return; + + cad = devinfo->codec->cad; + + if (cfl & HDA_COMMIT_GPIO) { + nid = devinfo->nid; + for (i = 0; i < HDA_GPIO_MAX; i++) { + if (!(devinfo->function.audio.quirks & (1 << i))) + continue; + gpioval = (1 << i) - 1; + hdac_command(sc, + HDA_CMD_SET_GPIO_ENABLE_MASK(cad, nid, gpioval), + cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DIRECTION(cad, nid, gpioval), + cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DATA(cad, nid, gpioval), + cad); + } + } + + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL || w->enable == 0) + continue; + if (cfl & HDA_COMMIT_CONN) { + if (w->selconn == -1) + w->selconn = 0; + if (w->nconns > 0) + hdac_widget_connection_select(w, w->selconn); + } + if ((cfl & HDA_COMMIT_CTRL) && + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if ((w->pflags & (HDA_DAC_PATH | HDA_ADC_PATH)) == + (HDA_DAC_PATH | HDA_ADC_PATH)) + device_printf(sc->dev, "WARNING: node %d " + "participate both for DAC/ADC!\n", w->nid); + if (w->pflags & HDA_DAC_PATH) { + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) != + HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + } else if (w->pflags & HDA_ADC_PATH) { + w->wclass.pin.ctrl &= + ~(HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE); + } else + w->wclass.pin.ctrl &= ~( + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE); + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, + w->wclass.pin.ctrl), cad); + } + if ((cfl & HDA_COMMIT_EAPD) && + w->param.eapdbtl != HDAC_INVALID) { + uint32_t val; + + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & + HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, + val), cad); + + } + DELAY(1000); + } +} + +static void +hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_ctl *ctl; + int i; + + devinfo->function.audio.mvol = 100 | (100 << 8); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, "[%2d] Ctl nid=%d", + i, (ctl->widget != NULL) ? + ctl->widget->nid : -1); + if (ctl->childwidget != NULL) + printf(" childnid=%d", + ctl->childwidget->nid); + if (ctl->widget == NULL) + printf(" NULL WIDGET!"); + printf(" DISABLED\n"); + ); + continue; + } + HDA_BOOTVERBOSE( + if (ctl->ossmask == 0) { + device_printf(sc->dev, "[%2d] Ctl nid=%d", + i, ctl->widget->nid); + if (ctl->childwidget != NULL) + printf(" childnid=%d", + ctl->childwidget->nid); + printf(" Bind to NONE\n"); + } + ); + if (ctl->step > 0) { + ctl->ossval = (ctl->left * 100) / ctl->step; + ctl->ossval |= ((ctl->right * 100) / ctl->step) << 8; + } else + ctl->ossval = 0; + hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, + ctl->left, ctl->right); + } +} + +static int +hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) +{ + struct hdac_chan *ch; + struct hdac_widget *w; + uint32_t cap, fmtcap, pcmcap, path; + int i, type, ret, max; + + if (dir == PCMDIR_PLAY) { + type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT; + ch = &devinfo->codec->sc->play; + path = HDA_DAC_PATH; + } else { + type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT; + ch = &devinfo->codec->sc->rec; + path = HDA_ADC_PATH; + } + + ch->caps = hdac_caps; + ch->caps.fmtlist = ch->fmtlist; + ch->bit16 = 1; + ch->bit32 = 0; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + + ret = 0; + fmtcap = devinfo->function.audio.supp_stream_formats; + pcmcap = devinfo->function.audio.supp_pcm_size_rate; + max = (sizeof(ch->io) / sizeof(ch->io[0])) - 1; + + for (i = devinfo->startnode; i < devinfo->endnode && ret < max; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0 || w->type != type || + !(w->pflags & path)) + continue; + cap = w->param.widget_cap; + /*if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(cap)) + continue;*/ + if (!HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(cap)) + continue; + cap = w->param.supp_stream_formats; + /*if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) { + } + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) { + }*/ + if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + continue; + ch->io[ret++] = i; + fmtcap &= w->param.supp_stream_formats; + pcmcap &= w->param.supp_pcm_size_rate; + } + ch->io[ret] = -1; + + ch->supp_stream_formats = fmtcap; + ch->supp_pcm_size_rate = pcmcap; + + /* + * 8bit = 0 + * 16bit = 1 + * 20bit = 2 + * 24bit = 3 + * 32bit = 4 + */ + if (ret > 0) { + cap = pcmcap; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + ch->bit16 = 1; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) + ch->bit16 = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + ch->bit32 = 4; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + ch->bit32 = 3; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + ch->bit32 = 2; + i = 0; + if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S16_LE; + ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; + if (ch->bit32 > 0) { + if (!(devinfo->function.audio.quirks & + HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S32_LE; + ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; + } + ch->fmtlist[i] = 0; + i = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + ch->pcmrates[i++] = 8000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + ch->pcmrates[i++] = 11025; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + ch->pcmrates[i++] = 16000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + ch->pcmrates[i++] = 22050; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + ch->pcmrates[i++] = 32000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + ch->pcmrates[i++] = 44100; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(cap)) */ + ch->pcmrates[i++] = 48000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + ch->pcmrates[i++] = 88200; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + ch->pcmrates[i++] = 96000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + ch->pcmrates[i++] = 176400; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + ch->pcmrates[i++] = 192000; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(cap)) */ + ch->pcmrates[i] = 0; + if (i > 0) { + ch->caps.minspeed = ch->pcmrates[0]; + ch->caps.maxspeed = ch->pcmrates[i - 1]; + } + } + + return (ret); +} + +static void +hdac_dump_ctls(struct hdac_devinfo *devinfo, const char *banner, uint32_t flag) +{ + struct hdac_audio_ctl *ctl; + struct hdac_softc *sc = devinfo->codec->sc; + int i; + uint32_t fl = 0; + + + if (flag == 0) { + fl = SOUND_MASK_VOLUME | SOUND_MASK_PCM | + SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | + SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_OGAIN; + } + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL || + ctl->widget->enable == 0) + continue; + if ((flag == 0 && (ctl->ossmask & ~fl)) || + (flag != 0 && (ctl->ossmask & flag))) { + if (banner != NULL) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "%s\n", banner); + } + goto hdac_ctl_dump_it_all; + } + } + + return; + +hdac_ctl_dump_it_all: + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL || + ctl->widget->enable == 0) + continue; + if (!((flag == 0 && (ctl->ossmask & ~fl)) || + (flag != 0 && (ctl->ossmask & flag)))) + continue; + if (flag == 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Unknown Ctl (OSS: %s)\n", + hdac_audio_ctl_ossmixer_mask2name(ctl->ossmask)); + } + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " +- nid: %2d index: %2d ", + ctl->widget->nid, ctl->index); + if (ctl->childwidget != NULL) + printf("(nid: %2d) ", ctl->childwidget->nid); + else + printf(" "); + printf("mute: %d step: %3d size: %3d off: %3d dir=0x%x ossmask=0x%08x\n", + ctl->mute, ctl->step, ctl->size, ctl->offset, ctl->dir, + ctl->ossmask); + } +} + +static void +hdac_dump_audio_formats(struct hdac_softc *sc, uint32_t fcap, uint32_t pcmcap) +{ + uint32_t cap; + + cap = fcap; + if (cap != 0) { + device_printf(sc->dev, " Stream cap: 0x%08x\n", cap); + device_printf(sc->dev, " Format:"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) + printf(" AC3"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) + printf(" FLOAT32"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + printf(" PCM"); + printf("\n"); + } + cap = pcmcap; + if (cap != 0) { + device_printf(sc->dev, " PCM cap: 0x%08x\n", cap); + device_printf(sc->dev, " PCM size:"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + printf(" 20"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + printf(" 24"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + printf(" 32"); + printf("\n"); + device_printf(sc->dev, " PCM rate:"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + printf(" 11"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + printf(" 22"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + printf(" 32"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + printf(" 44"); + printf(" 48"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + printf(" 88"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + printf(" 96"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + printf(" 176"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + printf(" 192"); + printf("\n"); + } +} + +static void +hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) +{ + uint32_t pincap, wcap; + + pincap = w->wclass.pin.cap; + wcap = w->param.widget_cap; + + device_printf(sc->dev, " Pin cap: 0x%08x\n", pincap); + device_printf(sc->dev, " "); + if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap)) + printf(" ISC"); + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) + printf(" TRQD"); + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) + printf(" PDC"); + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + printf(" HP"); + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + printf(" OUT"); + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + printf(" IN"); + if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) + printf(" BAL"); + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) + printf(" EAPD"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(wcap)) + printf(" : UNSOL"); + printf("\n"); + device_printf(sc->dev, " Pin config: 0x%08x\n", + w->wclass.pin.config); + device_printf(sc->dev, " Pin control: 0x%08x", w->wclass.pin.ctrl); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE) + printf(" HP"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) + printf(" IN"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) + printf(" OUT"); + printf("\n"); +} + +static void +hdac_dump_amp(struct hdac_softc *sc, uint32_t cap, char *banner) +{ + device_printf(sc->dev, " %s amp: 0x%08x\n", banner, cap); + device_printf(sc->dev, " " + "mute=%d step=%d size=%d offset=%d\n", + HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap), + HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap), + HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap), + HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap)); +} + +static void +hdac_dump_nodes(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w, *cw; + int i, j; + + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Default Parameter\n"); + device_printf(sc->dev, "-----------------\n"); + hdac_dump_audio_formats(sc, + devinfo->function.audio.supp_stream_formats, + devinfo->function.audio.supp_pcm_size_rate); + device_printf(sc->dev, " IN amp: 0x%08x\n", + devinfo->function.audio.inamp_cap); + device_printf(sc->dev, " OUT amp: 0x%08x\n", + devinfo->function.audio.outamp_cap); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) { + device_printf(sc->dev, "Ghost widget nid=%d\n", i); + continue; + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid: %d [%s]%s\n", w->nid, + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap) ? + "DIGITAL" : "ANALOG", + (w->enable == 0) ? " [DISABLED]" : ""); + device_printf(sc->dev, " name: %s\n", w->name); + device_printf(sc->dev, " widget_cap: 0x%08x\n", + w->param.widget_cap); + device_printf(sc->dev, " Parse flags: 0x%08x\n", + w->pflags); + device_printf(sc->dev, " Ctl flags: 0x%08x\n", + w->ctlflags); + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdac_dump_audio_formats(sc, + w->param.supp_stream_formats, + w->param.supp_pcm_size_rate); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_dump_pin(sc, w); + if (w->param.eapdbtl != HDAC_INVALID) + device_printf(sc->dev, " EAPD: 0x%08x\n", + w->param.eapdbtl); + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) && + w->param.outamp_cap != 0) + hdac_dump_amp(sc, w->param.outamp_cap, "Output"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && + w->param.inamp_cap != 0) + hdac_dump_amp(sc, w->param.inamp_cap, " Input"); + device_printf(sc->dev, " connections: %d\n", w->nconns); + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " + <- nid=%d [%s]", + w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); + if (cw == NULL) + printf(" [UNKNOWN]"); + else if (cw->enable == 0) + printf(" [DISABLED]"); + if (w->nconns > 1 && w->selconn == j && w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + printf(" (selected)"); + printf("\n"); + } + } + +} + +static int +hdac_dump_dac_internal(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + int i; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0 || !(w->pflags & HDA_DAC_PATH)) + return (0); + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); + device_printf(sc->dev, " ^\n"); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " +-----<------+\n"); + } else { + device_printf(sc->dev, " ^\n"); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " "); + printf(" nid=%d [%s]\n", w->nid, w->name); + } + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) { + return (1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (hdac_dump_dac_internal(devinfo, cw->nid, + depth + 1) != 0) + return (1); + } + } else if ((w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && + w->selconn > -1 && w->selconn < w->nconns) { + if (hdac_dump_dac_internal(devinfo, w->conns[w->selconn], + depth + 1) != 0) + return (1); + } + + return (0); +} + +static void +hdac_dump_dac(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + struct hdac_softc *sc = devinfo->codec->sc; + int i, printed = 0; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + !(w->pflags & HDA_DAC_PATH)) + continue; + if (printed == 0) { + printed = 1; + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Playback path:\n"); + } + hdac_dump_dac_internal(devinfo, w->nid, 0); + } +} + +static void +hdac_dump_adc(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + int i, j; + int printed = 0; + char ossdevs[256]; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + if (printed == 0) { + printed = 1; + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Recording sources:\n"); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + hdac_audio_ctl_ossmixer_mask2allname(cw->ctlflags, + ossdevs, sizeof(ossdevs)); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " + <- nid=%d [%s]", + cw->nid, cw->name); + if (strlen(ossdevs) > 0) { + printf(" [recsrc: %s]", ossdevs); + } + printf("\n"); + } + } +} + +static void +hdac_dump_pcmchannels(struct hdac_softc *sc, int pcnt, int rcnt) +{ + nid_t *nids; + + if (pcnt > 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " PCM Playback: %d\n", pcnt); + hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, + sc->play.supp_pcm_size_rate); + device_printf(sc->dev, " DAC:"); + for (nids = sc->play.io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } + + if (rcnt > 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " PCM Record: %d\n", rcnt); + hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, + sc->rec.supp_pcm_size_rate); + device_printf(sc->dev, " ADC:"); + for (nids = sc->rec.io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } +} + +static void +hdac_release_resources(struct hdac_softc *sc) +{ + struct hdac_devinfo *devinfo = NULL; + device_t *devlist = NULL; + int i, devcount; + + if (sc == NULL) + return; + + hdac_lock(sc); + hdac_reset(sc); + hdac_unlock(sc); + snd_mtxfree(sc->lock); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo == NULL) + continue; + if (devinfo->widget != NULL) + free(devinfo->widget, M_HDAC); + if (devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && + devinfo->function.audio.ctl != NULL) + free(devinfo->function.audio.ctl, M_HDAC); + free(devinfo, M_HDAC); + device_delete_child(sc->dev, devlist[i]); + } + if (devlist != NULL) + free(devlist, M_TEMP); + + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (sc->codecs[i] != NULL) + free(sc->codecs[i], M_HDAC); + sc->codecs[i] = NULL; + } + + hdac_dma_free(&sc->rirb_dma); + hdac_dma_free(&sc->corb_dma); + if (sc->play.blkcnt > 0) + hdac_dma_free(&sc->play.bdl_dma); + if (sc->rec.blkcnt > 0) + hdac_dma_free(&sc->rec.bdl_dma); + hdac_irq_free(sc); + hdac_mem_free(sc); + free(sc, M_DEVBUF); + +} + +/* This function surely going to make its way into upper level someday. */ +static void +hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) +{ + const char *res = NULL; + int i = 0, j, k, len, inv; + + if (on != NULL) + *on = 0; + if (off != NULL) + *off = 0; + if (sc == NULL) + return; + if (resource_string_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "config", &res) != 0) + return; + if (!(res != NULL && strlen(res) > 0)) + return; + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: HDA Config:"); + ); + for (;;) { + while (res[i] != '\0' && + (res[i] == ',' || isspace(res[i]) != 0)) + i++; + if (res[i] == '\0') { + HDA_BOOTVERBOSE( + printf("\n"); + ); + return; + } + j = i; + while (res[j] != '\0' && + !(res[j] == ',' || isspace(res[j]) != 0)) + j++; + len = j - i; + if (len > 2 && strncmp(res + i, "no", 2) == 0) + inv = 2; + else + inv = 0; + for (k = 0; len > inv && k < HDAC_QUIRKS_TAB_LEN; k++) { + if (strncmp(res + i + inv, + hdac_quirks_tab[k].key, len - inv) != 0) + continue; + if (len - inv != strlen(hdac_quirks_tab[k].key)) + break; + HDA_BOOTVERBOSE( + printf(" %s%s", (inv != 0) ? "no" : "", + hdac_quirks_tab[k].key); + ); + if (inv == 0 && on != NULL) + *on |= hdac_quirks_tab[k].value; + else if (inv != 0 && off != NULL) + *off |= hdac_quirks_tab[k].value; + break; + } + i = j; + } +} + +static void +hdac_attach2(void *arg) +{ + struct hdac_softc *sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t quirks_on, quirks_off; + int pcnt, rcnt; + int i; + char status[SND_STATUSLEN]; + device_t *devlist = NULL; + int devcount; + struct hdac_devinfo *devinfo = NULL; + + sc = (struct hdac_softc *)arg; + + hdac_config_fetch(sc, &quirks_on, &quirks_off); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: HDA Config: on=0x%08x off=0x%08x\n", + quirks_on, quirks_off); + ); + + hdac_lock(sc); + + /* Remove ourselves from the config hooks */ + if (sc->intrhook.ich_func != NULL) { + config_intrhook_disestablish(&sc->intrhook); + sc->intrhook.ich_func = NULL; + } + + /* Start the corb and rirb engines */ + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Starting CORB Engine...\n"); + ); + hdac_corb_start(sc); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Starting RIRB Engine...\n"); + ); + hdac_rirb_start(sc); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Enabling controller interrupt...\n"); + ); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | + HDAC_GCTL_UNSOL); + + DELAY(1000); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Scanning HDA codecs...\n"); + ); + hdac_scan_codecs(sc); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo != NULL && devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { + break; + } else + devinfo = NULL; + } + if (devlist != NULL) + free(devlist, M_TEMP); + + if (devinfo == NULL) { + hdac_unlock(sc); + device_printf(sc->dev, "Audio Function Group not found!\n"); + hdac_release_resources(sc); + return; + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Parsing AFG nid=%d cad=%d\n", + devinfo->nid, devinfo->codec->cad); + ); + hdac_audio_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Parsing Ctls...\n"); + ); + hdac_audio_ctl_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Parsing vendor patch...\n"); + ); + hdac_vendor_patch_parse(devinfo); + if (quirks_on != 0) + devinfo->function.audio.quirks |= quirks_on; + if (quirks_off != 0) + devinfo->function.audio.quirks &= ~quirks_off; + + /* XXX Disable all DIGITAL path. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) + continue; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + w->enable = 0; + continue; + } + /* XXX Disable useless pin ? */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) + w->enable = 0; + } + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL) + continue; + w = ctl->widget; + if (w->enable == 0) + ctl->enable = 0; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + ctl->enable = 0; + w = ctl->childwidget; + if (w == NULL) + continue; + if (w->enable == 0 || + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + ctl->enable = 0; + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Building AFG tree...\n"); + ); + hdac_audio_build_tree(devinfo); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: AFG commit...\n"); + ); + hdac_audio_commit(devinfo, HDA_COMMIT_ALL); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Ctls commit...\n"); + ); + hdac_audio_ctl_commit(devinfo); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: PCMDIR_PLAY setup...\n"); + ); + pcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_PLAY); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: PCMDIR_REC setup...\n"); + ); + rcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_REC); + + hdac_unlock(sc); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: OSS mixer initialization...\n"); + ); + + /* + * There is no point of return after this. If the driver failed, + * so be it. Let the detach procedure do all the cleanup. + */ + if (mixer_init(sc->dev, &hdac_audio_ctl_ossmixer_class, devinfo) != 0) + device_printf(sc->dev, "Can't register mixer\n"); + + if (pcnt > 0) + pcnt = 1; + if (rcnt > 0) + rcnt = 1; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Registering PCM channels...\n"); + ); + if (pcm_register(sc->dev, devinfo, pcnt, rcnt) != 0) + device_printf(sc->dev, "Can't register PCM\n"); + + sc->registered++; + + for (i = 0; i < pcnt; i++) + pcm_addchan(sc->dev, PCMDIR_PLAY, &hdac_channel_class, devinfo); + for (i = 0; i < rcnt; i++) + pcm_addchan(sc->dev, PCMDIR_REC, &hdac_channel_class, devinfo); + + snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s [%s]", + rman_get_start(sc->mem.mem_res), + rman_get_start(sc->irq.irq_res), + PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV); + pcm_setstatus(sc->dev, status); + device_printf(sc->dev, "\n", hdac_codec_name(devinfo)); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "\n", + hdac_codec_id(devinfo)); + ); + device_printf(sc->dev, "\n", HDA_DRV_TEST_REV); + + HDA_BOOTVERBOSE( + if (devinfo->function.audio.quirks != 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "HDA config/quirks:"); + for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { + if (devinfo->function.audio.quirks & + hdac_quirks_tab[i].value) + printf(" %s", hdac_quirks_tab[i].key); + } + printf("\n"); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+-------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA NODES |\n"); + device_printf(sc->dev, "+-------------------+\n"); + hdac_dump_nodes(devinfo); + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "\n"); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + device_printf(sc->dev, "%3d: nid=%d", i, + (ctl->widget != NULL) ? ctl->widget->nid : -1); + if (ctl->childwidget != NULL) + printf(" cnid=%d", ctl->childwidget->nid); + printf(" dir=0x%x index=%d " + "ossmask=0x%08x ossdev=%d%s\n", + ctl->dir, ctl->index, + ctl->ossmask, ctl->ossdev, + (ctl->enable == 0) ? " [DISABLED]" : ""); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+-----------------------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA AUDIO/VOLUME CONTROLS |\n"); + device_printf(sc->dev, "+-----------------------------------+\n"); + hdac_dump_ctls(devinfo, "Master Volume (OSS: vol)", SOUND_MASK_VOLUME); + hdac_dump_ctls(devinfo, "PCM Volume (OSS: pcm)", SOUND_MASK_PCM); + hdac_dump_ctls(devinfo, "CD Volume (OSS: cd)", SOUND_MASK_CD); + hdac_dump_ctls(devinfo, "Microphone Volume (OSS: mic)", SOUND_MASK_MIC); + hdac_dump_ctls(devinfo, "Line-in Volume (OSS: line)", SOUND_MASK_LINE); + hdac_dump_ctls(devinfo, "Recording Level (OSS: rec)", SOUND_MASK_RECLEV); + hdac_dump_ctls(devinfo, "Speaker/Beep (OSS: speaker)", SOUND_MASK_SPEAKER); + hdac_dump_ctls(devinfo, NULL, 0); + hdac_dump_dac(devinfo); + hdac_dump_adc(devinfo); + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+--------------------------------------+\n"); + device_printf(sc->dev, "| DUMPING PCM Playback/Record Channels |\n"); + device_printf(sc->dev, "+--------------------------------------+\n"); + hdac_dump_pcmchannels(sc, pcnt, rcnt); + ); +} + +/**************************************************************************** + * int hdac_detach(device_t) + * + * Detach and free up resources utilized by the hdac device. + ****************************************************************************/ +static int +hdac_detach(device_t dev) +{ + struct hdac_softc *sc = NULL; + struct hdac_devinfo *devinfo = NULL; + int err; + + devinfo = (struct hdac_devinfo *)pcm_getdevinfo(dev); + if (devinfo != NULL && devinfo->codec != NULL) + sc = devinfo->codec->sc; + if (sc == NULL) + return (0); + + if (sc->registered > 0) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + } + + hdac_release_resources(sc); + + return (0); +} + +static device_method_t hdac_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdac_probe), + DEVMETHOD(device_attach, hdac_attach), + DEVMETHOD(device_detach, hdac_detach), + { 0, 0 } +}; + +static driver_t hdac_driver = { + "pcm", + hdac_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_hda, pci, hdac_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_hda, 1); --- sys/dev/sound/pci/hda/hdac.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/hda/hdac.h,v 1.1 2006/10/01 11:12:59 ariff Exp $ + */ + +#ifndef _HDAC_H_ +#define _HDAC_H_ + + +#if 0 +/**************************************************************************** + * Miscellanious defines + ****************************************************************************/ + +/**************************************************************************** + * Helper Macros + ****************************************************************************/ + +/**************************************************************************** + * Simplified Accessors for HDA devices + ****************************************************************************/ +enum hdac_device_ivars { + HDAC_IVAR_CODEC_ID, + HDAC_IVAR_NODE_ID, + HDAC_IVAR_VENDOR_ID, + HDAC_IVAR_DEVICE_ID, + HDAC_IVAR_REVISION_ID, + HDAC_IVAR_STEPPING_ID, + HDAC_IVAR_NODE_TYPE, +}; + +#define HDAC_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(hdac, var, HDAC, ivar, type) + +HDAC_ACCESSOR(codec_id, CODEC_ID, uint8_t); +HDAC_ACCESSOR(node_id, NODE_ID, uint8_t); +HDAC_ACCESSOR(vendor_id, VENDOR_ID, uint16_t); +HDAC_ACCESSOR(device_id, DEVICE_ID, uint16_t); +HDAC_ACCESSOR(revision_id, REVISION_ID, uint8_t); +HDAC_ACCESSOR(stepping_id, STEPPING_ID, uint8_t); +HDAC_ACCESSOR(node_type, NODE_TYPE, uint8_t); +#endif + +#define PCIS_MULTIMEDIA_HDA 0x03 + +#endif --- sys/dev/sound/pci/hda/hdac_private.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac_private.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,332 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.2 2006/10/06 18:59:27 ariff Exp $ + */ + +#ifndef _HDAC_PRIVATE_H_ +#define _HDAC_PRIVATE_H_ + + +/**************************************************************************** + * Miscellanious defines + ****************************************************************************/ +#define HDAC_DMA_ALIGNMENT 128 +#define HDAC_CODEC_MAX 16 + +#define HDAC_MTX_NAME "hdac driver mutex" + +/**************************************************************************** + * Helper Macros + ****************************************************************************/ +#define HDAC_READ_1(mem, offset) \ + bus_space_read_1((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_READ_2(mem, offset) \ + bus_space_read_2((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_READ_4(mem, offset) \ + bus_space_read_4((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_WRITE_1(mem, offset, value) \ + bus_space_write_1((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) +#define HDAC_WRITE_2(mem, offset, value) \ + bus_space_write_2((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) +#define HDAC_WRITE_4(mem, offset, value) \ + bus_space_write_4((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) + +#define HDAC_ISDCTL(sc, n) (_HDAC_ISDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDSTS(sc, n) (_HDAC_ISDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDPICB(sc, n) (_HDAC_ISDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDCBL(sc, n) (_HDAC_ISDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDLVI(sc, n) (_HDAC_ISDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDFIFOD(sc, n) (_HDAC_ISDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDFMT(sc, n) (_HDAC_ISDFMT((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDBDPL(sc, n) (_HDAC_ISDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDBDPU(sc, n) (_HDAC_ISDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + +#define HDAC_OSDCTL(sc, n) (_HDAC_OSDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDSTS(sc, n) (_HDAC_OSDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDPICB(sc, n) (_HDAC_OSDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDCBL(sc, n) (_HDAC_OSDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDLVI(sc, n) (_HDAC_OSDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDFIFOD(sc, n) (_HDAC_OSDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDBDPL(sc, n) (_HDAC_OSDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDBDPU(sc, n) (_HDAC_OSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + +#define HDAC_BSDCTL(sc, n) (_HDAC_BSDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDSTS(sc, n) (_HDAC_BSDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDPICB(sc, n) (_HDAC_BSDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDCBL(sc, n) (_HDAC_BSDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDLVI(sc, n) (_HDAC_BSDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDFIFOD(sc, n) (_HDAC_BSDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDBDPL(sc, n) (_HDAC_BSDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDBDPU(sc, n) (_HDAC_BSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + + +/**************************************************************************** + * Custom hdac malloc type + ****************************************************************************/ +MALLOC_DECLARE(M_HDAC); + +/**************************************************************************** + * struct hdac_mem + * + * Holds the resources necessary to describe the physical memory associated + * with the device. + ****************************************************************************/ +struct hdac_mem { + struct resource *mem_res; + int mem_rid; + bus_space_tag_t mem_tag; + bus_space_handle_t mem_handle; +}; + +/**************************************************************************** + * struct hdac_irq + * + * Holds the resources necessary to describe the irq associated with the + * device. + ****************************************************************************/ +struct hdac_irq { + struct resource *irq_res; + int irq_rid; + void *irq_handle; +}; + +/**************************************************************************** + * struct hdac_dma + * + * This structure is used to hold all the information to manage the dma + * states. + ****************************************************************************/ +struct hdac_dma { + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_addr_t dma_paddr; + caddr_t dma_vaddr; +}; + +/**************************************************************************** + * struct hdac_rirb + * + * Hold a response from a verb sent to a codec received via the rirb. + ****************************************************************************/ +struct hdac_rirb { + uint32_t response; + uint32_t response_ex; +}; + +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN_MASK 0x0000000f +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN_OFFSET 0 +#define HDAC_RIRB_RESPONSE_EX_UNSOLICITED 0x00000010 + +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN(response_ex) \ + (((response_ex) & HDAC_RIRB_RESPONSE_EX_SDATA_IN_MASK) >> \ + HDAC_RIRB_RESPONSE_EX_SDATA_IN_OFFSET) + +/**************************************************************************** + * struct hdac_command_list + * + * This structure holds the list of verbs that are to be sent to the codec + * via the corb and the responses received via the rirb. It's allocated by + * the codec driver and is owned by it. + ****************************************************************************/ +struct hdac_command_list { + int num_commands; + uint32_t *verbs; + uint32_t *responses; +}; + +typedef int nid_t; + +struct hdac_softc; +/**************************************************************************** + * struct hdac_codec + * + ****************************************************************************/ +struct hdac_codec { + int verbs_sent; + int responses_received; + nid_t cad; + struct hdac_command_list *commands; + struct hdac_softc *sc; +}; + +struct hdac_bdle { + volatile uint32_t addrl; + volatile uint32_t addrh; + volatile uint32_t len; + volatile uint32_t ioc; +} __packed; + +#define HDA_MAX_CONNS 32 +#define HDA_MAX_NAMELEN 32 + +struct hdac_devinfo; + +struct hdac_widget { + nid_t nid; + int type; + int enable; + int nconns, selconn; + uint32_t pflags, ctlflags; + nid_t conns[HDA_MAX_CONNS]; + char name[HDA_MAX_NAMELEN]; + struct hdac_devinfo *devinfo; + struct { + uint32_t widget_cap; + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + uint32_t eapdbtl; + int outpath; + } param; + union { + struct { + uint32_t config; + uint32_t cap; + uint32_t ctrl; + } pin; + } wclass; +}; + +struct hdac_audio_ctl { + struct hdac_widget *widget, *childwidget; + int enable; + int index; + int mute, step, size, offset; + int left, right; + uint32_t muted; + int ossdev; + uint32_t dir, ossmask, ossval; +}; + +/**************************************************************************** + * struct hdac_devinfo + * + * Holds all the parameters of a given codec function group. This is stored + * in the ivar of each child of the hdac bus + ****************************************************************************/ +struct hdac_devinfo { + device_t dev; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision_id; + uint8_t stepping_id; + uint8_t node_type; + nid_t nid; + nid_t startnode, endnode; + int nodecnt; + struct hdac_codec *codec; + struct hdac_widget *widget; + union { + struct { + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + int ctlcnt, pcnt, rcnt; + struct hdac_audio_ctl *ctl; + uint32_t mvol; + uint32_t quirks; + int ossidx; + int playcnt, reccnt; + int parsing_strategy; + } audio; + /* XXX undefined: modem, hdmi. */ + } function; +}; + +struct hdac_chan { + struct snd_dbuf *b; + struct pcm_channel *c; + struct pcmchan_caps caps; + struct hdac_devinfo *devinfo; + struct hdac_dma bdl_dma; + uint32_t spd, fmt, fmtlist[8], pcmrates[16]; + uint32_t supp_stream_formats, supp_pcm_size_rate; + int ptr, prevptr, blkcnt, blksz; + int dir; + int off; + int sid; + int bit16, bit32; + nid_t io[16]; +}; + +/**************************************************************************** + * struct hdac_softc + * + * This structure holds the current state of the hdac driver. + ****************************************************************************/ +struct hdac_softc { + device_t dev; + device_t hdabus; + struct mtx *lock; + + struct intr_config_hook intrhook; + + struct hdac_mem mem; + struct hdac_irq irq; + uint32_t pci_subvendor; + + + int num_iss; + int num_oss; + int num_bss; + int support_64bit; + int streamcnt; + + int corb_size; + struct hdac_dma corb_dma; + int corb_wp; + + int rirb_size; + struct hdac_dma rirb_dma; + int rirb_rp; + + struct hdac_chan play, rec; + bus_dma_tag_t chan_dmat; + int chan_size; + int chan_blkcnt; + +#define HDAC_UNSOLQ_MAX 64 +#define HDAC_UNSOLQ_READY 0 +#define HDAC_UNSOLQ_BUSY 1 + int unsolq_rp; + int unsolq_wp; + int unsolq_st; + uint32_t unsolq[HDAC_UNSOLQ_MAX]; + + struct hdac_codec *codecs[HDAC_CODEC_MAX]; + + int registered; +}; + +/**************************************************************************** + * struct hdac_command flags + ****************************************************************************/ +#define HDAC_COMMAND_FLAG_WAITOK 0x0000 +#define HDAC_COMMAND_FLAG_NOWAIT 0x0001 + +#endif --- sys/dev/sound/pci/hda/hdac_reg.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac_reg.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,266 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/hda/hdac_reg.h,v 1.1 2006/10/01 11:12:59 ariff Exp $ + */ + +#ifndef _HDAC_REG_H_ +#define _HDAC_REG_H_ + +/**************************************************************************** + * HDA Controller Register Set + ****************************************************************************/ +#define HDAC_GCAP 0x00 /* 2 - Global Capabilities*/ +#define HDAC_VMIN 0x02 /* 1 - Minor Version */ +#define HDAC_VMAJ 0x03 /* 1 - Major Version */ +#define HDAC_OUTPAY 0x04 /* 2 - Output Payload Capability */ +#define HDAC_INPAY 0x06 /* 2 - Input Payload Capability */ +#define HDAC_GCTL 0x08 /* 4 - Global Control */ +#define HDAC_WAKEEN 0x0c /* 2 - Wake Enable */ +#define HDAC_STATESTS 0x0e /* 2 - State Change Status */ +#define HDAC_GSTS 0x10 /* 2 - Global Status */ +#define HDAC_OUTSTRMPAY 0x18 /* 2 - Output Stream Payload Capability */ +#define HDAC_INSTRMPAY 0x1a /* 2 - Input Stream Payload Capability */ +#define HDAC_INTCTL 0x20 /* 4 - Interrupt Control */ +#define HDAC_INTSTS 0x24 /* 4 - Interrupt Status */ +#define HDAC_WALCLK 0x30 /* 4 - Wall Clock Counter */ +#define HDAC_SSYNC 0x38 /* 4 - Stream Synchronization */ +#define HDAC_CORBLBASE 0x40 /* 4 - CORB Lower Base Address */ +#define HDAC_CORBUBASE 0x44 /* 4 - CORB Upper Base Address */ +#define HDAC_CORBWP 0x48 /* 2 - CORB Write Pointer */ +#define HDAC_CORBRP 0x4a /* 2 - CORB Read Pointer */ +#define HDAC_CORBCTL 0x4c /* 1 - CORB Control */ +#define HDAC_CORBSTS 0x4d /* 1 - CORB Status */ +#define HDAC_CORBSIZE 0x4e /* 1 - CORB Size */ +#define HDAC_RIRBLBASE 0x50 /* 4 - RIRB Lower Base Address */ +#define HDAC_RIRBUBASE 0x54 /* 4 - RIRB Upper Base Address */ +#define HDAC_RIRBWP 0x58 /* 2 - RIRB Write Pointer */ +#define HDAC_RINTCNT 0x5a /* 2 - Response Interrupt Count */ +#define HDAC_RIRBCTL 0x5c /* 1 - RIRB Control */ +#define HDAC_RIRBSTS 0x5d /* 1 - RIRB Status */ +#define HDAC_RIRBSIZE 0x5e /* 1 - RIRB Size */ +#define HDAC_ICOI 0x60 /* 4 - Immediate Command Output Interface */ +#define HDAC_ICII 0x64 /* 4 - Immediate Command Input Interface */ +#define HDAC_ICIS 0x68 /* 2 - Immediate Command Status */ +#define HDAC_DPIBLBASE 0x70 /* 4 - DMA Position Buffer Lower Base */ +#define HDAC_DPIBUBASE 0x74 /* 4 - DMA Position Buffer Upper Base */ +#define HDAC_SDCTL0 0x80 /* 3 - Stream Descriptor Control */ +#define HDAC_SDCTL1 0x81 /* 3 - Stream Descriptor Control */ +#define HDAC_SDCTL2 0x82 /* 3 - Stream Descriptor Control */ +#define HDAC_SDSTS 0x83 /* 1 - Stream Descriptor Status */ +#define HDAC_SDLPIB 0x84 /* 4 - Link Position in Buffer */ +#define HDAC_SDCBL 0x88 /* 4 - Cyclic Buffer Length */ +#define HDAC_SDLVI 0x8C /* 2 - Last Valid Index */ +#define HDAC_SDFIFOS 0x90 /* 2 - FIFOS */ +#define HDAC_SDFMT 0x92 /* 2 - fmt */ +#define HDAC_SDBDPL 0x98 /* 4 - Buffer Descriptor Pointer Lower Base */ +#define HDAC_SDBDPU 0x9C /* 4 - Buffer Descriptor Pointer Upper Base */ + +#define _HDAC_ISDOFFSET(n, iss, oss) (0x80 + ((n) * 0x20)) +#define _HDAC_ISDCTL(n, iss, oss) (0x00 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDSTS(n, iss, oss) (0x03 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDPICB(n, iss, oss) (0x04 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDCBL(n, iss, oss) (0x08 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDLVI(n, iss, oss) (0x0c + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDFIFOD(n, iss, oss) (0x10 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDFMT(n, iss, oss) (0x12 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDBDPL(n, iss, oss) (0x18 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDBDPU(n, iss, oss) (0x1c + _HDAC_ISDOFFSET(n, iss, oss)) + +#define _HDAC_OSDOFFSET(n, iss, oss) (0x80 + ((iss) * 0x20) + ((n) * 0x20)) +#define _HDAC_OSDCTL(n, iss, oss) (0x00 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDSTS(n, iss, oss) (0x03 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDPICB(n, iss, oss) (0x04 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDCBL(n, iss, oss) (0x08 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDLVI(n, iss, oss) (0x0c + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDFIFOD(n, iss, oss) (0x10 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDFMT(n, iss, oss) (0x12 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDBDPL(n, iss, oss) (0x18 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDBDPU(n, iss, oss) (0x1c + _HDAC_OSDOFFSET(n, iss, oss)) + +#define _HDAC_BSDOFFSET(n, iss, oss) (0x80 + ((iss) * 0x20) + ((oss) * 0x20) + ((n) * 0x20)) +#define _HDAC_BSDCTL(n, iss, oss) (0x00 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDSTS(n, iss, oss) (0x03 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDPICB(n, iss, oss) (0x04 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDCBL(n, iss, oss) (0x08 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDLVI(n, iss, oss) (0x0c + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDFIFOD(n, iss, oss) (0x10 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDFMT(n, iss, oss) (0x12 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDBDPL(n, iss, oss) (0x18 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDBDBU(n, iss, oss) (0x1c + _HDAC_BSDOFFSET(n, iss, oss)) + +/**************************************************************************** + * HDA Controller Register Fields + ****************************************************************************/ + +/* GCAP - Global Capabilities */ +#define HDAC_GCAP_64OK 0x0001 +#define HDAC_GCAP_NSDO_MASK 0x0006 +#define HDAC_GCAP_NSDO_SHIFT 1 +#define HDAC_GCAP_BSS_MASK 0x00f8 +#define HDAC_GCAP_BSS_SHIFT 3 +#define HDAC_GCAP_ISS_MASK 0x0f00 +#define HDAC_GCAP_ISS_SHIFT 8 +#define HDAC_GCAP_OSS_MASK 0xf000 +#define HDAC_GCAP_OSS_SHIFT 12 + +#define HDAC_GCAP_NSDO_1SDO 0x00 +#define HDAC_GCAP_NSDO_2SDO 0x02 +#define HDAC_GCAP_NSDO_4SDO 0x04 + +#define HDAC_GCAP_BSS(gcap) \ + (((gcap) & HDAC_GCAP_BSS_MASK) >> HDAC_GCAP_BSS_SHIFT) +#define HDAC_GCAP_ISS(gcap) \ + (((gcap) & HDAC_GCAP_ISS_MASK) >> HDAC_GCAP_ISS_SHIFT) +#define HDAC_GCAP_OSS(gcap) \ + (((gcap) & HDAC_GCAP_OSS_MASK) >> HDAC_GCAP_OSS_SHIFT) + +/* GCTL - Global Control */ +#define HDAC_GCTL_CRST 0x00000001 +#define HDAC_GCTL_FCNTRL 0x00000002 +#define HDAC_GCTL_UNSOL 0x00000100 + +/* WAKEEN - Wake Enable */ +#define HDAC_WAKEEN_SDIWEN_MASK 0x7fff +#define HDAC_WAKEEN_SDIWEN_SHIFT 0 + +/* STATESTS - State Change Status */ +#define HDAC_STATESTS_SDIWAKE_MASK 0x7fff +#define HDAC_STATESTS_SDIWAKE_SHIFT 0 + +#define HDAC_STATESTS_SDIWAKE(statests, n) \ + (((((statests) & HDAC_STATESTS_SDIWAKE_MASK) >> \ + HDAC_STATESTS_SDIWAKE_SHIFT) >> (n)) & 0x0001) + +/* GSTS - Global Status */ +#define HDAC_GSTS_FSTS 0x0002 + +/* INTCTL - Interrut Control */ +#define HDAC_INTCTL_SIE_MASK 0x3fffffff +#define HDAC_INTCTL_SIE_SHIFT 0 +#define HDAC_INTCTL_CIE 0x40000000 +#define HDAC_INTCTL_GIE 0x80000000 + +/* INTSTS - Interrupt Status */ +#define HDAC_INTSTS_SIS_MASK 0x3fffffff +#define HDAC_INTSTS_SIS_SHIFT 0 +#define HDAC_INTSTS_CIS 0x40000000 +#define HDAC_INTSTS_GIS 0x80000000 + +/* SSYNC - Stream Synchronization */ +#define HDAC_SSYNC_SSYNC_MASK 0x3fffffff +#define HDAC_SSYNC_SSYNC_SHIFT 0 + +/* CORBWP - CORB Write Pointer */ +#define HDAC_CORBWP_CORBWP_MASK 0x00ff +#define HDAC_CORBWP_CORBWP_SHIFT 0 + +/* CORBRP - CORB Read Pointer */ +#define HDAC_CORBRP_CORBRP_MASK 0x00ff +#define HDAC_CORBRP_CORBRP_SHIFT 0 +#define HDAC_CORBRP_CORBRPRST 0x8000 + +/* CORBCTL - CORB Control */ +#define HDAC_CORBCTL_CMEIE 0x01 +#define HDAC_CORBCTL_CORBRUN 0x02 + +/* CORBSTS - CORB Status */ +#define HDAC_CORBSTS_CMEI 0x01 + +/* CORBSIZE - CORB Size */ +#define HDAC_CORBSIZE_CORBSIZE_MASK 0x03 +#define HDAC_CORBSIZE_CORBSIZE_SHIFT 0 +#define HDAC_CORBSIZE_CORBSZCAP_MASK 0xf0 +#define HDAC_CORBSIZE_CORBSZCAP_SHIFT 4 + +#define HDAC_CORBSIZE_CORBSIZE_2 0x00 +#define HDAC_CORBSIZE_CORBSIZE_16 0x01 +#define HDAC_CORBSIZE_CORBSIZE_256 0x02 + +#define HDAC_CORBSIZE_CORBSZCAP_2 0x10 +#define HDAC_CORBSIZE_CORBSZCAP_16 0x20 +#define HDAC_CORBSIZE_CORBSZCAP_256 0x40 + +#define HDAC_CORBSIZE_CORBSIZE(corbsize) \ + (((corbsize) & HDAC_CORBSIZE_CORBSIZE_MASK) >> HDAC_CORBSIZE_CORBSIZE_SHIFT) + +/* RIRBWP - RIRB Write Pointer */ +#define HDAC_RIRBWP_RIRBWP_MASK 0x00ff +#define HDAC_RIRBWP_RIRBWP_SHIFT 0 +#define HDAC_RIRBWP_RIRBWPRST 0x8000 + +/* RINTCTN - Response Interrupt Count */ +#define HDAC_RINTCNT_MASK 0x00ff +#define HDAC_RINTCNT_SHIFT 0 + +/* RIRBCTL - RIRB Control */ +#define HDAC_RIRBCTL_RINTCTL 0x01 +#define HDAC_RIRBCTL_RIRBDMAEN 0x02 +#define HDAC_RIRBCTL_RIRBOIC 0x04 + +/* RIRBSTS - RIRB Status */ +#define HDAC_RIRBSTS_RINTFL 0x01 +#define HDAC_RIRBSTS_RIRBOIS 0x04 + +/* RIRBSIZE - RIRB Size */ +#define HDAC_RIRBSIZE_RIRBSIZE_MASK 0x03 +#define HDAC_RIRBSIZE_RIRBSIZE_SHIFT 0 +#define HDAC_RIRBSIZE_RIRBSZCAP_MASK 0xf0 +#define HDAC_RIRBSIZE_RIRBSZCAP_SHIFT 4 + +#define HDAC_RIRBSIZE_RIRBSIZE_2 0x00 +#define HDAC_RIRBSIZE_RIRBSIZE_16 0x01 +#define HDAC_RIRBSIZE_RIRBSIZE_256 0x02 + +#define HDAC_RIRBSIZE_RIRBSZCAP_2 0x10 +#define HDAC_RIRBSIZE_RIRBSZCAP_16 0x20 +#define HDAC_RIRBSIZE_RIRBSZCAP_256 0x40 + +#define HDAC_RIRBSIZE_RIRBSIZE(rirbsize) \ + (((rirbsize) & HDAC_RIRBSIZE_RIRBSIZE_MASK) >> HDAC_RIRBSIZE_RIRBSIZE_SHIFT) + +/* DPLBASE - DMA Position Lower Base Address */ +#define HDAC_DPLBASE_DPLBASE_MASK 0xffffff80 +#define HDAC_DPLBASE_DPLBASE_SHIFT 7 +#define HDAC_DPLBASE_DPLBASE_DMAPBE 0x00000001 + +/* SDCTL - Stream Descriptor Control */ +#define HDAC_SDCTL_SRST 0x000001 +#define HDAC_SDCTL_RUN 0x000002 +#define HDAC_SDCTL_IOCE 0x000004 +#define HDAC_SDCTL_FEIE 0x000008 +#define HDAC_SDCTL_DEIE 0x000010 +#define HDAC_SDCTL_STRIPE_MASK 0x030000 +#define HDAC_SDCTL_STRIPE_SHIFT 16 +#define HDAC_SDCTL_TP 0x040000 +#define HDAC_SDCTL_DIR 0x080000 +#define HDAC_SDCTL2_STRM_MASK 0xf0 +#define HDAC_SDCTL2_STRM_SHIFT 4 + +#define HDAC_SDSTS_DESE (1 << 4) +#define HDAC_SDSTS_FIFOE (1 << 3) +#define HDAC_SDSTS_BCIS (1 << 2) + +#endif --- sys/dev/sound/pcm/ac97.c.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/pcm/ac97.c Sun Oct 1 22:28:51 2006 @@ -136,7 +136,7 @@ { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, { 0x41445372, 0x00, 0, "AD1981A", 0 }, - { 0x41445374, 0x00, 0, "AD1981B", 0 }, + { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, { 0x414b4d00, 0x00, 1, "AK4540", 0 }, @@ -545,40 +545,6 @@ } } -static void -ac97_fix_volume(struct ac97_info *codec) -{ - struct snddev_info *d = device_get_softc(codec->dev); - -#if 0 - /* XXX For the sake of debugging purposes */ - ac97_wrcd(codec, AC97_MIX_PCM, 0); - bzero(&codec->mix[SOUND_MIXER_PCM], - sizeof(codec->mix[SOUND_MIXER_PCM])); - codec->flags |= AC97_F_SOFTVOL; - if (d) - d->flags |= SD_F_SOFTVOL; - return; -#endif - switch (codec->id) { - case 0x434d4941: /* CMI9738 */ - case 0x434d4961: /* CMI9739 */ - case 0x434d4978: /* CMI9761 */ - case 0x434d4982: /* CMI9761 */ - case 0x434d4983: /* CMI9761 */ - ac97_wrcd(codec, AC97_MIX_PCM, 0); - break; - default: - return; - break; - } - bzero(&codec->mix[SOUND_MIXER_PCM], - sizeof(codec->mix[SOUND_MIXER_PCM])); - codec->flags |= AC97_F_SOFTVOL; - if (d) - d->flags |= SD_F_SOFTVOL; -} - static const char* ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) { @@ -684,7 +650,6 @@ } ac97_fix_auxout(codec); ac97_fix_tone(codec); - ac97_fix_volume(codec); if (codec_patch) codec_patch(codec); @@ -758,8 +723,6 @@ if (bootverbose) { if (codec->flags & AC97_F_RDCD_BUG) device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); - if (codec->flags & AC97_F_SOFTVOL) - device_printf(codec->dev, "Soft PCM volume\n"); device_printf(codec->dev, "Codec features "); for (i = j = 0; i < 10; i++) if (codec->caps & (1 << i)) @@ -827,15 +790,16 @@ struct ac97_info * ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { - struct ac97_info *codec; + struct ac97_info *codec; + int eapd_inv; - codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); + codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO); if (codec == NULL) return NULL; snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); - codec->methods = kobj_create(cls, M_AC97, M_WAITOK); + codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); if (codec->methods == NULL) { snd_mtxlock(codec->lock); snd_mtxfree(codec->lock); @@ -846,6 +810,11 @@ codec->dev = dev; codec->devinfo = devinfo; codec->flags = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "ac97_eapd_inv", &eapd_inv) == 0) { + if (eapd_inv != 0) + codec->flags |= AC97_F_EAPD_INV; + } return codec; } @@ -877,6 +846,7 @@ ac97mix_init(struct snd_mixer *m) { struct ac97_info *codec = mix_getdevinfo(m); + struct snddev_info *d; u_int32_t i, mask; if (codec == NULL) @@ -889,6 +859,46 @@ for (i = 0; i < 32; i++) mask |= codec->mix[i].enable? 1 << i : 0; mix_setdevs(m, mask); + + switch (codec->id) { + case 0x41445374: /* AD1981B */ + /*mask = 0; + if (codec->mix[SOUND_MIXER_OGAIN].enable) + mask |= SOUND_MASK_OGAIN; + if (codec->mix[SOUND_MIXER_PHONEOUT].enable) + mask |= SOUND_MASK_PHONEOUT;*/ + /* Tie ogain/phone to master volume */ + /*if (codec->mix[SOUND_MIXER_VOLUME].enable) + mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); + else { + mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); + mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); + }*/ + break; + case 0x434d4941: /* CMI9738 */ + case 0x434d4961: /* CMI9739 */ + case 0x434d4978: /* CMI9761 */ + case 0x434d4982: /* CMI9761 */ + case 0x434d4983: /* CMI9761 */ + ac97_wrcd(codec, AC97_MIX_PCM, 0); + bzero(&codec->mix[SOUND_MIXER_PCM], + sizeof(codec->mix[SOUND_MIXER_PCM])); + d = device_get_softc(codec->dev); + if (d != NULL) + d->flags |= SD_F_SOFTPCMVOL; + /* XXX How about master volume ? */ + break; + default: + break; + } + +#if 0 + /* XXX For the sake of debugging purposes */ + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM | SOUND_MASK_CD); + mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); + ac97_wrcd(codec, AC97_MIX_MASTER, 0); +#endif mask = 0; for (i = 0; i < 32; i++) --- sys/dev/sound/pcm/ac97_patch.c.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/pcm/ac97_patch.c Sun Oct 1 22:28:51 2006 @@ -46,6 +46,12 @@ ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420); } +void ad1981b_patch(struct ac97_info* codec) +{ + /*ac97_wrcd(codec, AC97_AD_JACK_SPDIF, + ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800);*/ +} + void cmi9739_patch(struct ac97_info* codec) { /* --- sys/dev/sound/pcm/ac97_patch.h.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/pcm/ac97_patch.h Sun Oct 1 22:28:51 2006 @@ -29,4 +29,5 @@ void ad1886_patch(struct ac97_info*); void ad198x_patch(struct ac97_info*); +void ad1981b_patch(struct ac97_info*); void cmi9739_patch(struct ac97_info*); --- sys/dev/sound/pcm/channel.c.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/pcm/channel.c Sun Oct 1 22:28:51 2006 @@ -1346,7 +1346,7 @@ c->feederflags &= ~(1 << FEEDER_VOLUME); if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && - c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTVOL) && + c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTPCMVOL) && c->parentsnddev->mixer_dev) c->feederflags |= 1 << FEEDER_VOLUME; flags = c->feederflags; @@ -1404,7 +1404,10 @@ sndbuf_setfmt(c->bufhard, hwfmt); if ((flags & (1 << FEEDER_VOLUME))) { - int vol = 100 | (100 << 8); + uint32_t parent = SOUND_MIXER_NONE; + int vol, left, right; + + vol = 100 | (100 << 8); CHN_UNLOCK(c); /* @@ -1414,9 +1417,26 @@ */ if (mixer_ioctl(c->parentsnddev->mixer_dev, MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0) - device_printf(c->dev, "Soft Volume: Failed to read default value\n"); + device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n"); + left = vol & 0x7f; + right = (vol >> 8) & 0x7f; + if (c->parentsnddev != NULL && + c->parentsnddev->mixer_dev != NULL && + c->parentsnddev->mixer_dev->si_drv1 != NULL) + parent = mix_getparent( + c->parentsnddev->mixer_dev->si_drv1, + SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) { + vol = 100 | (100 << 8); + if (mixer_ioctl(c->parentsnddev->mixer_dev, + MIXER_READ(parent), + (caddr_t)&vol, -1, NULL) != 0) + device_printf(c->dev, "Soft Volume: Failed to read parent default value\n"); + left = (left * (vol & 0x7f)) / 100; + right = (right * ((vol >> 8) & 0x7f)) / 100; + } CHN_LOCK(c); - chn_setvolume(c, vol & 0x7f, (vol >> 8) & 0x7f); + chn_setvolume(c, left, right); } return 0; --- sys/dev/sound/pcm/mixer.c.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/pcm/mixer.c Sun Oct 1 22:28:51 2006 @@ -47,6 +47,9 @@ u_int32_t recdevs; u_int32_t recsrc; u_int16_t level[32]; + u_int8_t parent[32]; + u_int32_t child[32]; + u_int8_t realdev[32]; char name[MIXER_NAMELEN]; struct mtx *lock; }; @@ -112,48 +115,94 @@ #endif static int -mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) +mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d, + unsigned left, unsigned right) +{ + struct snddev_channel *sce; + struct pcm_channel *ch; +#ifdef USING_MUTEX + int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; + + if (locked) + snd_mtxunlock(mixer->lock); +#endif + SLIST_FOREACH(sce, &d->channels, link) { + ch = sce->channel; + CHN_LOCK(ch); + if (ch->direction == PCMDIR_PLAY && + (ch->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume(ch, left, right); + CHN_UNLOCK(ch); + } +#ifdef USING_MUTEX + if (locked) + snd_mtxlock(mixer->lock); +#endif + return 0; +} + +static int +mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) { struct snddev_info *d; - unsigned l, r; - int v; + unsigned l, r, tl, tr; + u_int32_t parent = SOUND_MIXER_NONE, child = 0; + u_int32_t realdev; + int i; - if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || + (0 == (m->devs & (1 << dev)))) return -1; l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); + realdev = m->realdev[dev]; - d = device_get_softc(mixer->dev); - if (dev == SOUND_MIXER_PCM && d && - (d->flags & SD_F_SOFTVOL)) { - struct snddev_channel *sce; - struct pcm_channel *ch; -#ifdef USING_MUTEX - int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; + d = device_get_softc(m->dev); + if (d == NULL) + return -1; - if (locked) - snd_mtxunlock(mixer->lock); -#endif - SLIST_FOREACH(sce, &d->channels, link) { - ch = sce->channel; - CHN_LOCK(ch); - if (ch->direction == PCMDIR_PLAY && - (ch->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(ch, l, r); - CHN_UNLOCK(ch); + /* TODO: recursive handling */ + parent = m->parent[dev]; + if (parent >= SOUND_MIXER_NRDEVICES) + parent = SOUND_MIXER_NONE; + if (parent == SOUND_MIXER_NONE) + child = m->child[dev]; + + if (parent != SOUND_MIXER_NONE) { + tl = (l * (m->level[parent] & 0x00ff)) / 100; + tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; + if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, tl, tr); + else if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, tl, tr) < 0) + return -1; + } else if (child != 0) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(child & (1 << i)) || m->parent[i] != dev) + continue; + realdev = m->realdev[i]; + tl = (l * (m->level[i] & 0x00ff)) / 100; + tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; + if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, tl, tr); + else if (realdev != SOUND_MIXER_NONE) + MIXER_SET(m, realdev, tl, tr); } -#ifdef USING_MUTEX - if (locked) - snd_mtxlock(mixer->lock); -#endif + realdev = m->realdev[dev]; + if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, l, r) < 0) + return -1; } else { - v = MIXER_SET(mixer, dev, l, r); - if (v < 0) + if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, l, r); + else if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, l, r) < 0) return -1; } - mixer->level[dev] = l | (r << 8); + m->level[dev] = l | (r << 8); + return 0; } @@ -184,9 +233,20 @@ void mix_setdevs(struct snd_mixer *m, u_int32_t v) { - struct snddev_info *d = device_get_softc(m->dev); - if (d && (d->flags & SD_F_SOFTVOL)) + struct snddev_info *d; + int i; + + if (m == NULL) + return; + + d = device_get_softc(m->dev); + if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) v |= SOUND_MASK_PCM; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + v |= 1 << m->parent[i]; + v |= m->child[i]; + } m->devs = v; } @@ -196,6 +256,54 @@ m->recdevs = v; } +void +mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) +{ + u_int32_t mask = 0; + int i; + + if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) + return; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (i == parent) + continue; + if (childs & (1 << i)) { + mask |= 1 << i; + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + m->child[m->parent[i]] &= ~(1 << i); + m->parent[i] = parent; + m->child[i] = 0; + } + } + mask &= ~(1 << parent); + m->child[parent] = mask; +} + +void +mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || + !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) + return; + m->realdev[dev] = realdev; +} + +u_int32_t +mix_getparent(struct snd_mixer *m, u_int32_t dev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) + return SOUND_MIXER_NONE; + return m->parent[dev]; +} + +u_int32_t +mix_getchild(struct snd_mixer *m, u_int32_t dev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) + return 0; + return m->child[dev]; +} + u_int32_t mix_getdevs(struct snd_mixer *m) { @@ -230,6 +338,11 @@ m->devinfo = devinfo; m->busy = 0; m->dev = dev; + for (i = 0; i < 32; i++) { + m->parent[i] = SOUND_MIXER_NONE; + m->child[i] = 0; + m->realdev[i] = i; + } if (MIXER_INIT(m)) goto bad; @@ -255,6 +368,30 @@ pdev->si_drv1 = m; snddev = device_get_softc(dev); snddev->mixer_dev = pdev; + + if (bootverbose) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(m->devs & (1 << i))) + continue; + if (m->realdev[i] != i) { + device_printf(dev, "Mixer \"%s\" -> \"%s\":", + snd_mixernames[i], + (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? + snd_mixernames[m->realdev[i]] : "none"); + } else { + device_printf(dev, "Mixer \"%s\":", + snd_mixernames[i]); + } + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + printf(" parent=\"%s\"", + snd_mixernames[m->parent[i]]); + if (m->child[i] != 0) + printf(" child=0x%08x", m->child[i]); + printf("\n"); + } + if (snddev->flags & SD_F_SOFTPCMVOL) + device_printf(dev, "Soft PCM mixer ENABLED\n"); + } return 0; --- sys/dev/sound/pcm/mixer.h.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/pcm/mixer.h Sun Oct 1 22:28:51 2006 @@ -39,6 +39,10 @@ void mix_setrecdevs(struct snd_mixer *m, u_int32_t v); u_int32_t mix_getdevs(struct snd_mixer *m); u_int32_t mix_getrecdevs(struct snd_mixer *m); +void mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs); +void mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev); +u_int32_t mix_getparent(struct snd_mixer *m, u_int32_t dev); +u_int32_t mix_getchild(struct snd_mixer *m, u_int32_t dev); void *mix_getdevinfo(struct snd_mixer *m); /* --- sys/dev/sound/pcm/sound.h.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/pcm/sound.h Sun Oct 1 22:28:51 2006 @@ -135,7 +135,7 @@ #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 -#define SD_F_SOFTVOL 0x00000004 +#define SD_F_SOFTPCMVOL 0x00000004 #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) --- sys/dev/sound/usb/uaudio_pcm.c.orig Sun Oct 1 22:27:23 2006 +++ sys/dev/sound/usb/uaudio_pcm.c Sun Oct 1 22:28:51 2006 @@ -243,12 +243,20 @@ d = device_get_softc(ua->sc_dev); mask = uaudio_query_mix_info(pa_dev); - if (d && !(mask & SOUND_MIXER_PCM)) { - /* - * Emulate missing pcm mixer controller - * through FEEDER_VOLUME - */ - d->flags |= SD_F_SOFTVOL; + if (d != NULL) { + if (!(mask & SOUND_MASK_PCM)) { + /* + * Emulate missing pcm mixer controller + * through FEEDER_VOLUME + */ + d->flags |= SD_F_SOFTPCMVOL; + } + if (!(mask & SOUND_MASK_VOLUME)) { + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + } } mix_setdevs(m, mask); --- sys/modules/sound/driver/Makefile.orig Sun Oct 1 22:27:23 2006 +++ sys/modules/sound/driver/Makefile Sun Oct 1 22:28:51 2006 @@ -4,7 +4,7 @@ SUBDIR = audiocs es137x .else SUBDIR = als4000 ad1816 atiixp cmi cs4281 csa ds1 emu10k1 es137x ess -SUBDIR += fm801 ich maestro maestro3 mss neomagic sb16 sb8 sbc solo +SUBDIR += fm801 hda ich maestro maestro3 mss neomagic sb16 sb8 sbc solo SUBDIR += t4dwave via8233 via82c686 vibes SUBDIR += driver uaudio .endif --- sys/modules/sound/driver/hda/Makefile.orig Thu Jan 1 07:30:00 1970 +++ sys/modules/sound/driver/hda/Makefile Sun Oct 1 22:28:51 2006 @@ -0,0 +1,9 @@ +# $FreeBSD: src/sys/modules/sound/driver/hda/Makefile,v 1.1 2006/10/01 11:13:00 ariff Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci/hda + +KMOD= snd_hda +SRCS= device_if.h bus_if.h pci_if.h channel_if.h mixer_if.h +SRCS+= hdac.c hdac_private.h hdac_reg.h hda_reg.h hdac.h + +.include