diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/sound/pci/hda/hda_reg.h | 8 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdaa.c | 5901 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdaa.h | 230 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdaa_patches.c | 638 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac.c | 7818 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac.h | 542 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac_if.m | 114 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac_private.h | 220 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac_reg.h | 8 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdacc.c | 662 |
10 files changed, 8829 insertions, 7312 deletions
diff --git a/sys/dev/sound/pci/hda/hda_reg.h b/sys/dev/sound/pci/hda/hda_reg.h index 0eeea64..847a626 100644 --- a/sys/dev/sound/pci/hda/hda_reg.h +++ b/sys/dev/sound/pci/hda/hda_reg.h @@ -400,7 +400,7 @@ 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_MASK 0x3f #define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 #define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG(param) \ @@ -418,14 +418,10 @@ (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_PRESENCE_DETECT 0x80000000 #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) diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c new file mode 100644 index 0000000..cee159e --- /dev/null +++ b/sys/dev/sound/pci/hda/hdaa.c @@ -0,0 +1,5901 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * 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 (Audio function) driver for FreeBSD. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include <dev/sound/pcm/sound.h> + +#include <sys/ctype.h> +#include <sys/taskqueue.h> + +#include <dev/sound/pci/hda/hdac.h> +#include <dev/sound/pci/hda/hdaa.h> +#include <dev/sound/pci/hda/hda_reg.h> + +#include "mixer_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +#define hdaa_lock(devinfo) snd_mtxlock((devinfo)->lock) +#define hdaa_unlock(devinfo) snd_mtxunlock((devinfo)->lock) +#define hdaa_lockassert(devinfo) snd_mtxassert((devinfo)->lock) +#define hdaa_lockowned(devinfo) mtx_owned((devinfo)->lock) + +static const struct { + char *key; + uint32_t value; +} hdaa_quirks_tab[] = { + { "softpcmvol", HDAA_QUIRK_SOFTPCMVOL }, + { "fixedrate", HDAA_QUIRK_FIXEDRATE }, + { "forcestereo", HDAA_QUIRK_FORCESTEREO }, + { "eapdinv", HDAA_QUIRK_EAPDINV }, + { "senseinv", HDAA_QUIRK_SENSEINV }, + { "ivref50", HDAA_QUIRK_IVREF50 }, + { "ivref80", HDAA_QUIRK_IVREF80 }, + { "ivref100", HDAA_QUIRK_IVREF100 }, + { "ovref50", HDAA_QUIRK_OVREF50 }, + { "ovref80", HDAA_QUIRK_OVREF80 }, + { "ovref100", HDAA_QUIRK_OVREF100 }, + { "ivref", HDAA_QUIRK_IVREF }, + { "ovref", HDAA_QUIRK_OVREF }, + { "vref", HDAA_QUIRK_VREF }, +}; +#define HDAA_QUIRKS_TAB_LEN \ + (sizeof(hdaa_quirks_tab) / sizeof(hdaa_quirks_tab[0])) + +#define HDA_BDL_MIN 2 +#define HDA_BDL_MAX 256 +#define HDA_BDL_DEFAULT HDA_BDL_MIN + +#define HDA_BLK_MIN HDA_DMA_ALIGNMENT +#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) + +#define HDA_BUFSZ_MIN 4096 +#define HDA_BUFSZ_MAX 65536 +#define HDA_BUFSZ_DEFAULT 16384 + +#define HDA_PARSE_MAXDEPTH 10 + +MALLOC_DEFINE(M_HDAA, "hdaa", "HDA Audio"); + +const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue", "Green", "Red", + "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B", "Res.C", "Res.D", + "White", "Other"}; + +const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD", + "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in", + "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"}; + +const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; + +const char *HDA_CONNECTORS[16] = { + "Unknown", "1/8", "1/4", "ATAPI", "RCA", "Optical", "Digital", "Analog", + "DIN", "XLR", "RJ-11", "Combo", "0xc", "0xd", "0xe", "Other" }; + +const char *HDA_LOCS[64] = { + "0x00", "Rear", "Front", "Left", "Right", "Top", "Bottom", "Rear-panel", + "Drive-bay", "0x09", "0x0a", "0x0b", "0x0c", "0x0d", "0x0e", "0x0f", + "Internal", "0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "Riser", + "0x18", "Onboard", "0x1a", "0x1b", "0x1c", "0x1d", "0x1e", "0x1f", + "External", "Ext-Rear", "Ext-Front", "Ext-Left", "Ext-Right", "Ext-Top", "Ext-Bottom", "0x07", + "0x28", "0x29", "0x2a", "0x2b", "0x2c", "0x2d", "0x2e", "0x2f", + "Other", "0x31", "0x32", "0x33", "0x34", "0x35", "Other-Bott", "Lid-In", + "Lid-Out", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "0x3f" }; + +const char *HDA_GPIO_ACTIONS[8] = { + "keep", "set", "clear", "disable", "input", "0x05", "0x06", "0x07"}; + +/* Default */ +static uint32_t hdaa_fmt[] = { + SND_FORMAT(AFMT_S16_LE, 2, 0), + 0 +}; + +static struct pcmchan_caps hdaa_caps = {48000, 48000, hdaa_fmt, 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])) + +/**************************************************************************** + * Function prototypes + ****************************************************************************/ +static int hdaa_pcmchannel_setup(struct hdaa_chan *); + +static void hdaa_widget_connection_select(struct hdaa_widget *, uint8_t); +static void hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *, + uint32_t, int, int); +static struct hdaa_audio_ctl *hdaa_audio_ctl_amp_get(struct hdaa_devinfo *, + nid_t, int, int, int); +static void hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *, + nid_t, int, int, int, int, int, int); + +static void hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf); + +static char * +hdaa_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; + } + } + return (buf); +} + +static struct hdaa_audio_ctl * +hdaa_audio_ctl_each(struct hdaa_devinfo *devinfo, int *index) +{ + if (devinfo == NULL || + index == NULL || devinfo->ctl == NULL || + devinfo->ctlcnt < 1 || + *index < 0 || *index >= devinfo->ctlcnt) + return (NULL); + return (&devinfo->ctl[(*index)++]); +} + +static struct hdaa_audio_ctl * +hdaa_audio_ctl_amp_get(struct hdaa_devinfo *devinfo, nid_t nid, int dir, + int index, int cnt) +{ + struct hdaa_audio_ctl *ctl; + int i, found = 0; + + if (devinfo == NULL || devinfo->ctl == NULL) + return (NULL); + + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if (ctl->widget->nid != nid) + continue; + if (dir && ctl->ndir != dir) + continue; + if (index >= 0 && ctl->ndir == HDAA_CTL_IN && + ctl->dir == ctl->ndir && ctl->index != index) + continue; + found++; + if (found == cnt || cnt <= 0) + return (ctl); + } + + return (NULL); +} + +/* + * Jack detection (Speaker/HP redirection) event handler. + */ +static void +hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid) +{ + struct hdaa_audio_as *as; + struct hdaa_widget *w; + struct hdaa_audio_ctl *ctl; + uint32_t val, res; + int j; + + as = &devinfo->as[asid]; + if (as->hpredir < 0) + return; + + w = hdaa_widget_get(devinfo, as->pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + return; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PIN_SENSE(0, as->pins[15])); + + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Pin sense: nid=%d sence=0x%08x", + as->pins[15], res); + ); + + res = (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) != 0; + if (devinfo->quirks & HDAA_QUIRK_SENSEINV) + res ^= 1; + + HDA_BOOTVERBOSE( + printf(" %sconnected\n", res == 0 ? "dis" : ""); + ); + + /* (Un)Mute headphone pin. */ + ctl = hdaa_audio_ctl_amp_get(devinfo, + as->pins[15], HDAA_CTL_IN, -1, 1); + if (ctl != NULL && ctl->mute) { + /* If pin has muter - use it. */ + val = (res != 0) ? 0 : 1; + if (val != ctl->forcemute) { + ctl->forcemute = val; + hdaa_audio_ctl_amp_set(ctl, + HDAA_AMP_MUTE_DEFAULT, + HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT); + } + } else { + /* If there is no muter - disable pin output. */ + w = hdaa_widget_get(devinfo, as->pins[15]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (res != 0) + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hda_command(devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, + w->nid, w->wclass.pin.ctrl)); + } + } + } + /* (Un)Mute other pins. */ + for (j = 0; j < 15; j++) { + if (as->pins[j] <= 0) + continue; + ctl = hdaa_audio_ctl_amp_get(devinfo, + as->pins[j], HDAA_CTL_IN, -1, 1); + if (ctl != NULL && ctl->mute) { + /* If pin has muter - use it. */ + val = (res != 0) ? 1 : 0; + if (val == ctl->forcemute) + continue; + ctl->forcemute = val; + hdaa_audio_ctl_amp_set(ctl, + HDAA_AMP_MUTE_DEFAULT, + HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT); + continue; + } + /* If there is no muter - disable pin output. */ + w = hdaa_widget_get(devinfo, as->pins[j]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (res != 0) + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hda_command(devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, + w->nid, w->wclass.pin.ctrl)); + } + } + } +} + +/* + * Callback for poll based jack detection. + */ +static void +hdaa_jack_poll_callback(void *arg) +{ + struct hdaa_devinfo *devinfo = arg; + int i; + + hdaa_lock(devinfo); + if (devinfo->poll_ival == 0) { + hdaa_unlock(devinfo); + return; + } + for (i = 0; i < devinfo->ascnt; i++) { + if (devinfo->as[i].hpredir < 0) + continue; + hdaa_hp_switch_handler(devinfo, i); + } + callout_reset(&devinfo->poll_jack, devinfo->poll_ival, + hdaa_jack_poll_callback, devinfo); + hdaa_unlock(devinfo); +} + +/* + * Jack detection initializer. + */ +static void +hdaa_hp_switch_init(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i, poll = 0; + + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].hpredir < 0) + continue; + + w = hdaa_widget_get(devinfo, as[i].pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || + (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) { + device_printf(devinfo->dev, + "No jack detection support at pin %d\n", + as[i].pins[15]); + continue; + } + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { + as[i].unsol = HDAC_UNSOL_ALLOC( + device_get_parent(devinfo->dev), devinfo->dev, + w->nid); + hda_command(devinfo->dev, + HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | + as[i].unsol)); + } else + poll = 1; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Headphones redirection " + "for as=%d nid=%d using %s.\n", + i, w->nid, + (poll != 0) ? "polling" : "unsolicited responses"); + ); + hdaa_hp_switch_handler(devinfo, i); + } + if (poll) { + callout_reset(&devinfo->poll_jack, 1, + hdaa_jack_poll_callback, devinfo); + } +} + +static void +hdaa_hp_switch_deinit(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i; + + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].unsol < 0) + continue; + w = hdaa_widget_get(devinfo, as[i].pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hda_command(devinfo->dev, + HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, 0)); + HDAC_UNSOL_FREE( + device_get_parent(devinfo->dev), devinfo->dev, + as[i].unsol); + as[i].unsol = -1; + } +} + +uint32_t +hdaa_widget_pin_patch(uint32_t config, const char *str) +{ + char buf[256]; + char *key, *value, *rest, *bad; + int ival, i; + + strlcpy(buf, str, sizeof(buf)); + rest = buf; + while ((key = strsep(&rest, "=")) != NULL) { + value = strsep(&rest, " \t"); + if (value == NULL) + break; + ival = strtol(value, &bad, 10); + if (strcmp(key, "seq") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK); + } else if (strcmp(key, "as") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) & + HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK); + } else if (strcmp(key, "misc") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) & + HDA_CONFIG_DEFAULTCONF_MISC_MASK); + } else if (strcmp(key, "color") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) & + HDA_CONFIG_DEFAULTCONF_COLOR_MASK); + }; + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_COLORS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT); + break; + } + } + } else if (strcmp(key, "ctype") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK); + } + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_CONNECTORS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT); + break; + } + } + } else if (strcmp(key, "device") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK); + continue; + }; + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_DEVS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT); + break; + } + } + } else if (strcmp(key, "loc") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) & + HDA_CONFIG_DEFAULTCONF_LOCATION_MASK); + continue; + } + for (i = 0; i < 64; i++) { + if (strcasecmp(HDA_LOCS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT); + break; + } + } + } else if (strcmp(key, "conn") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + continue; + }; + for (i = 0; i < 4; i++) { + if (strcasecmp(HDA_CONNS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT); + break; + } + } + } + } + return (config); +} + +uint32_t +hdaa_gpio_patch(uint32_t gpio, const char *str) +{ + char buf[256]; + char *key, *value, *rest; + int ikey, i; + + strlcpy(buf, str, sizeof(buf)); + rest = buf; + while ((key = strsep(&rest, "=")) != NULL) { + value = strsep(&rest, " \t"); + if (value == NULL) + break; + ikey = strtol(key, NULL, 10); + if (ikey < 0 || ikey > 7) + continue; + for (i = 0; i < 7; i++) { + if (strcasecmp(HDA_GPIO_ACTIONS[i], value) == 0) { + gpio &= ~HDAA_GPIO_MASK(ikey); + gpio |= i << HDAA_GPIO_SHIFT(ikey); + break; + } + } + } + return (gpio); +} + +static void +hdaa_local_patch_pin(struct hdaa_widget *w) +{ + device_t dev = w->devinfo->dev; + const char *res = NULL; + uint32_t config, orig; + char buf[32]; + + config = orig = w->wclass.pin.config; + snprintf(buf, sizeof(buf), "cad%u.nid%u.config", + hda_get_codec_id(dev), w->nid); + if (resource_string_value(device_get_name( + device_get_parent(device_get_parent(dev))), + device_get_unit(device_get_parent(device_get_parent(dev))), + buf, &res) == 0) { + if (strncmp(res, "0x", 2) == 0) { + config = strtol(res + 2, NULL, 16); + } else { + config = hdaa_widget_pin_patch(config, res); + } + } + snprintf(buf, sizeof(buf), "nid%u.config", w->nid); + if (resource_string_value(device_get_name(dev), device_get_unit(dev), + buf, &res) == 0) { + if (strncmp(res, "0x", 2) == 0) { + config = strtol(res + 2, NULL, 16); + } else { + config = hdaa_widget_pin_patch(config, res); + } + } + HDA_BOOTVERBOSE( + if (config != orig) + device_printf(w->devinfo->dev, + "Patching pin config nid=%u 0x%08x -> 0x%08x\n", + w->nid, orig, config); + ); + w->wclass.pin.newconf = w->wclass.pin.config = config; +} + +static int +hdaa_sysctl_config(SYSCTL_HANDLER_ARGS) +{ + char buf[256]; + int error; + uint32_t conf; + + conf = *(uint32_t *)oidp->oid_arg1; + snprintf(buf, sizeof(buf), "0x%08x as=%d seq=%d " + "device=%s conn=%s ctype=%s loc=%s color=%s misc=%d", + conf, + HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), + HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), + HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], + HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], + HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)], + HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)], + HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], + HDA_CONFIG_DEFAULTCONF_MISC(conf)); + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + conf = strtol(buf + 2, NULL, 16); + else + conf = hdaa_widget_pin_patch(conf, buf); + *(uint32_t *)oidp->oid_arg1 = conf; + return (0); +} + +static void +hdaa_config_fetch(const char *str, uint32_t *on, uint32_t *off) +{ + int i = 0, j, k, len, inv; + + for (;;) { + while (str[i] != '\0' && + (str[i] == ',' || isspace(str[i]) != 0)) + i++; + if (str[i] == '\0') + return; + j = i; + while (str[j] != '\0' && + !(str[j] == ',' || isspace(str[j]) != 0)) + j++; + len = j - i; + if (len > 2 && strncmp(str + i, "no", 2) == 0) + inv = 2; + else + inv = 0; + for (k = 0; len > inv && k < HDAA_QUIRKS_TAB_LEN; k++) { + if (strncmp(str + i + inv, + hdaa_quirks_tab[k].key, len - inv) != 0) + continue; + if (len - inv != strlen(hdaa_quirks_tab[k].key)) + continue; + if (inv == 0) { + *on |= hdaa_quirks_tab[k].value; + *off &= ~hdaa_quirks_tab[k].value; + } else { + *off |= hdaa_quirks_tab[k].value; + *on &= ~hdaa_quirks_tab[k].value; + } + break; + } + i = j; + } +} + +static int +hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS) +{ + char buf[256]; + int error, n = 0, i; + uint32_t quirks, quirks_off; + + quirks = *(uint32_t *)oidp->oid_arg1; + buf[0] = 0; + for (i = 0; i < HDAA_QUIRKS_TAB_LEN; i++) { + if ((quirks & hdaa_quirks_tab[i].value) != 0) + n += snprintf(buf + n, sizeof(buf) - n, "%s%s", + n != 0 ? "," : "", hdaa_quirks_tab[i].key); + } + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + quirks = strtol(buf + 2, NULL, 16); + else { + quirks = 0; + hdaa_config_fetch(buf, &quirks, &quirks_off); + } + *(uint32_t *)oidp->oid_arg1 = quirks; + return (0); +} + +static void +hdaa_local_patch(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + const char *res = NULL; + uint32_t quirks_on = 0, quirks_off = 0, x; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdaa_local_patch_pin(w); + } + + if (resource_string_value(device_get_name(devinfo->dev), + device_get_unit(devinfo->dev), "config", &res) == 0) { + if (res != NULL && strlen(res) > 0) + hdaa_config_fetch(res, &quirks_on, &quirks_off); + devinfo->quirks |= quirks_on; + devinfo->quirks &= ~quirks_off; + } + if (devinfo->newquirks == -1) + devinfo->newquirks = devinfo->quirks; + else + devinfo->quirks = devinfo->newquirks; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + "Config options: 0x%08x\n", devinfo->quirks); + ); + + if (resource_string_value(device_get_name(devinfo->dev), + device_get_unit(devinfo->dev), "gpio_config", &res) == 0) { + if (strncmp(res, "0x", 2) == 0) { + devinfo->gpio = strtol(res + 2, NULL, 16); + } else { + devinfo->gpio = hdaa_gpio_patch(devinfo->gpio, res); + } + } + if (devinfo->newgpio == -1) + devinfo->newgpio = devinfo->gpio; + else + devinfo->gpio = devinfo->newgpio; + if (devinfo->newgpo == -1) + devinfo->newgpo = devinfo->gpo; + else + devinfo->gpo = devinfo->newgpo; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, "GPIO config options:"); + for (i = 0; i < 7; i++) { + x = (devinfo->gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i); + if (x != 0) + printf(" %d=%s", i, HDA_GPIO_ACTIONS[x]); + } + printf("\n"); + ); +} + +static void +hdaa_widget_connection_parse(struct hdaa_widget *w) +{ + uint32_t res; + int i, j, max, ents, entnum; + nid_t nid = w->nid; + nid_t cnid, addcnid, prevcnid; + + w->nconns = 0; + + res = hda_command(w->devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_CONN_LIST_LENGTH)); + + ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); + + if (ents < 1) + return; + + entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; + max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; + prevcnid = 0; + +#define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) +#define CONN_NMASK(e) (CONN_RMASK(e) - 1) +#define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) +#define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) +#define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) + + for (i = 0; i < ents; i += entnum) { + res = hda_command(w->devinfo->dev, + HDA_CMD_GET_CONN_LIST_ENTRY(0, nid, i)); + for (j = 0; j < entnum; j++) { + cnid = CONN_CNID(res, entnum, j); + if (cnid == 0) { + if (w->nconns < ents) + device_printf(w->devinfo->dev, + "WARNING: nid=%d has zero cnid " + "entnum=%d j=%d index=%d " + "entries=%d found=%d res=0x%08x\n", + nid, entnum, j, i, + ents, w->nconns, res); + else + goto getconns_out; + } + if (cnid < w->devinfo->startnode || + cnid >= w->devinfo->endnode) { + HDA_BOOTVERBOSE( + device_printf(w->devinfo->dev, + "WARNING: nid=%d has cnid outside " + "of the AFG range j=%d " + "entnum=%d index=%d res=0x%08x\n", + nid, j, entnum, i, res); + ); + } + if (CONN_RANGE(res, entnum, j) == 0) + addcnid = cnid; + else if (prevcnid == 0 || prevcnid >= cnid) { + device_printf(w->devinfo->dev, + "WARNING: Invalid child range " + "nid=%d index=%d j=%d entnum=%d " + "prevcnid=%d cnid=%d res=0x%08x\n", + nid, i, j, entnum, prevcnid, + cnid, res); + addcnid = cnid; + } else + addcnid = prevcnid + 1; + while (addcnid <= cnid) { + if (w->nconns > max) { + device_printf(w->devinfo->dev, + "Adding %d (nid=%d): " + "Max connection reached! max=%d\n", + addcnid, nid, max + 1); + goto getconns_out; + } + w->connsenable[w->nconns] = 1; + w->conns[w->nconns++] = addcnid++; + } + prevcnid = cnid; + } + } + +getconns_out: + return; +} + +static void +hdaa_widget_parse(struct hdaa_widget *w) +{ + device_t dev = w->devinfo->dev; + uint32_t wcap, cap; + nid_t nid = w->nid; + char buf[64]; + + w->param.widget_cap = wcap = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_AUDIO_WIDGET_CAP)); + w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); + + hdaa_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 = + hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_OUTPUT_AMP_CAP)); + else + w->param.outamp_cap = + w->devinfo->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 = + hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_INPUT_AMP_CAP)); + else + w->param.inamp_cap = + w->devinfo->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 = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_SUPP_STREAM_FORMATS)); + w->param.supp_stream_formats = (cap != 0) ? cap : + w->devinfo->supp_stream_formats; + cap = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_SUPP_PCM_SIZE_RATE)); + w->param.supp_pcm_size_rate = (cap != 0) ? cap : + w->devinfo->supp_pcm_size_rate; + } else { + w->param.supp_stream_formats = + w->devinfo->supp_stream_formats; + w->param.supp_pcm_size_rate = + w->devinfo->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) { + w->wclass.pin.original = w->wclass.pin.newconf = + w->wclass.pin.config = hda_command(dev, + HDA_CMD_GET_CONFIGURATION_DEFAULT(0, w->nid)); + w->wclass.pin.cap = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, w->nid, HDA_PARAM_PIN_CAP));; + w->wclass.pin.ctrl = hda_command(dev, + HDA_CMD_GET_PIN_WIDGET_CTRL(0, nid)); + if (HDA_PARAM_PIN_CAP_EAPD_CAP(w->wclass.pin.cap)) { + w->param.eapdbtl = hda_command(dev, + HDA_CMD_GET_EAPD_BTL_ENABLE(0, nid)); + w->param.eapdbtl &= 0x7; + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + } else + w->param.eapdbtl = HDA_INVALID; + + hdaa_unlock(w->devinfo); + snprintf(buf, sizeof(buf), "nid%d_config", w->nid); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + buf, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + &w->wclass.pin.newconf, sizeof(&w->wclass.pin.newconf), + hdaa_sysctl_config, "A", "Current pin configuration"); + snprintf(buf, sizeof(buf), "nid%d_original", w->nid); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + buf, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + &w->wclass.pin.original, sizeof(&w->wclass.pin.original), + hdaa_sysctl_config, "A", "Original pin configuration"); + hdaa_lock(w->devinfo); + } +} + +static void +hdaa_widget_postprocess(struct hdaa_widget *w) +{ + char *typestr; + + w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(w->param.widget_cap); + 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 (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + uint32_t config; + const char *devstr; + int conn, color; + + config = w->wclass.pin.config; + devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> + HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT]; + conn = (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT; + color = (config & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> + HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT; + strlcat(w->name, ": ", sizeof(w->name)); + strlcat(w->name, devstr, sizeof(w->name)); + strlcat(w->name, " (", sizeof(w->name)); + if (conn == 0 && color != 0 && color != 15) { + strlcat(w->name, HDA_COLORS[color], sizeof(w->name)); + strlcat(w->name, " ", sizeof(w->name)); + } + strlcat(w->name, HDA_CONNS[conn], sizeof(w->name)); + strlcat(w->name, ")", sizeof(w->name)); + } +} + +struct hdaa_widget * +hdaa_widget_get(struct hdaa_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 +hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *devinfo, nid_t nid, + int index, int lmute, int rmute, + int left, int right, int dir) +{ + uint16_t v = 0; + + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + "Setting amplifier nid=%d index=%d %s mute=%d/%d vol=%d/%d\n", + nid,index,dir ? "in" : "out",lmute,rmute,left,right); + ); + if (left != right || lmute != rmute) { + v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | + (lmute << 7) | left; + hda_command(devinfo->dev, + HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v)); + v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | + (rmute << 7) | right; + } else + v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | + (lmute << 7) | left; + + hda_command(devinfo->dev, + HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v)); +} + +static void +hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *ctl, uint32_t mute, + int left, int right) +{ + nid_t nid; + int lmute, rmute; + + nid = ctl->widget->nid; + + /* Save new values if valid. */ + if (mute != HDAA_AMP_MUTE_DEFAULT) + ctl->muted = mute; + if (left != HDAA_AMP_VOL_DEFAULT) + ctl->left = left; + if (right != HDAA_AMP_VOL_DEFAULT) + ctl->right = right; + /* Prepare effective values */ + if (ctl->forcemute) { + lmute = 1; + rmute = 1; + left = 0; + right = 0; + } else { + lmute = HDAA_AMP_LEFT_MUTED(ctl->muted); + rmute = HDAA_AMP_RIGHT_MUTED(ctl->muted); + left = ctl->left; + right = ctl->right; + } + /* Apply effective values */ + if (ctl->dir & HDAA_CTL_OUT) + hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index, + lmute, rmute, left, right, 0); + if (ctl->dir & HDAA_CTL_IN) + hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index, + lmute, rmute, left, right, 1); +} + +static void +hdaa_widget_connection_select(struct hdaa_widget *w, uint8_t index) +{ + if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) + return; + HDA_BOOTHVERBOSE( + device_printf(w->devinfo->dev, + "Setting selector nid=%d index=%d\n", w->nid, index); + ); + hda_command(w->devinfo->dev, + HDA_CMD_SET_CONNECTION_SELECT_CONTROL(0, w->nid, index)); + w->selconn = index; +} + +/**************************************************************************** + * Device Methods + ****************************************************************************/ + +static void * +hdaa_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct hdaa_chan *ch = data; + struct hdaa_pcm_devinfo *pdevinfo = ch->pdevinfo; + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + + hdaa_lock(devinfo); + if (devinfo->quirks & HDAA_QUIRK_FIXEDRATE) { + ch->caps.minspeed = ch->caps.maxspeed = 48000; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + } + ch->dir = dir; + ch->b = b; + ch->c = c; + ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt; + ch->blkcnt = pdevinfo->chan_blkcnt; + hdaa_unlock(devinfo); + + if (sndbuf_alloc(ch->b, bus_get_dma_tag(devinfo->dev), + hda_get_dma_nocache(devinfo->dev) ? BUS_DMA_NOCACHE : 0, + pdevinfo->chan_size) != 0) + return (NULL); + + return (ch); +} + +static int +hdaa_channel_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct hdaa_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 uint32_t +hdaa_channel_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + struct hdaa_chan *ch = data; + uint32_t spd = 0, threshold; + int i; + + /* First look for equal or multiple frequency. */ + for (i = 0; ch->pcmrates[i] != 0; i++) { + spd = ch->pcmrates[i]; + if (speed != 0 && spd / speed * speed == spd) { + ch->spd = spd; + return (spd); + } + } + /* If no match, just find nearest. */ + for (i = 0; ch->pcmrates[i] != 0; i++) { + spd = ch->pcmrates[i]; + threshold = spd + ((ch->pcmrates[i + 1] != 0) ? + ((ch->pcmrates[i + 1] - spd) >> 1) : 0); + if (speed < threshold) + break; + } + ch->spd = spd; + return (spd); +} + +static uint16_t +hdaa_stream_format(struct hdaa_chan *ch) +{ + int i; + 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; + } + } + fmt |= (AFMT_CHANNEL(ch->fmt) - 1); + + return (fmt); +} + +static void +hdaa_audio_setup(struct hdaa_chan *ch) +{ + struct hdaa_audio_as *as = &ch->devinfo->as[ch->as]; + struct hdaa_widget *w; + int i, chn, totalchn, c; + uint16_t fmt, dfmt; + uint16_t chmap[2][5] = {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x0231 }, /* 5.1 */ + { 0x0010, 0x0001, 0x2001, 0x2031, 0x2431 }};/* 7.1 */ + int map = -1; + + totalchn = AFMT_CHANNEL(ch->fmt); + HDA_BOOTHVERBOSE( + device_printf(ch->pdevinfo->dev, + "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->fmt, ch->spd); + ); + fmt = hdaa_stream_format(ch); + + /* Set channel mapping for known speaker setups. */ + if ((as->pinset == 0x0007 || as->pinset == 0x0013)) /* Standard 5.1 */ + map = 0; + else if (as->pinset == 0x0017) /* Standard 7.1 */ + map = 1; + + dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; + if (ch->fmt & AFMT_AC3) + dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO; + + chn = 0; + for (i = 0; ch->io[i] != -1; i++) { + w = hdaa_widget_get(ch->devinfo, ch->io[i]); + if (w == NULL) + continue; + + /* If HP redirection is enabled, but failed to use same + DAC, make last DAC to duplicate first one. */ + if (as->fakeredir && i == (as->pincnt - 1)) { + c = (ch->sid << 4); + } else { + if (map >= 0) /* Map known speaker setups. */ + chn = (((chmap[map][totalchn / 2] >> i * 4) & + 0xf) - 1) * 2; + if (chn < 0 || chn >= totalchn) { + c = 0; + } else { + c = (ch->sid << 4) | chn; + } + } + HDA_BOOTHVERBOSE( + device_printf(ch->pdevinfo->dev, + "PCMDIR_%s: Stream setup nid=%d: " + "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->io[i], fmt, dfmt, c); + ); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_CONV_FMT(0, ch->io[i], fmt)); + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + hda_command(ch->devinfo->dev, + HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], dfmt)); + } + hda_command(ch->devinfo->dev, + HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], c)); +#if 0 + hda_command(ch->devinfo->dev, + HDA_CMD_SET_CONV_CHAN_COUNT(0, ch->io[i], 1)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_CHAN_SLOT(0, ch->io[i], 0x00)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_CHAN_SLOT(0, ch->io[i], 0x11)); +#endif + chn += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; + } +} + +/* + * Greatest Common Divisor. + */ +static unsigned +gcd(unsigned a, unsigned b) +{ + u_int c; + + while (b != 0) { + c = a; + a = b; + b = (c % b); + } + return (a); +} + +/* + * Least Common Multiple. + */ +static unsigned +lcm(unsigned a, unsigned b) +{ + + return ((a * b) / gcd(a, b)); +} + +static int +hdaa_channel_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) +{ + struct hdaa_chan *ch = data; + + blksz -= blksz % lcm(HDA_DMA_ALIGNMENT, sndbuf_getalign(ch->b)); + + if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) + blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; + if (blksz < HDA_BLK_MIN) + blksz = HDA_BLK_MIN; + if (blkcnt > HDA_BDL_MAX) + blkcnt = HDA_BDL_MAX; + if (blkcnt < HDA_BDL_MIN) + blkcnt = HDA_BDL_MIN; + + while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { + if ((blkcnt >> 1) >= HDA_BDL_MIN) + blkcnt >>= 1; + else if ((blksz >> 1) >= HDA_BLK_MIN) + blksz >>= 1; + else + break; + } + + if ((sndbuf_getblksz(ch->b) != blksz || + sndbuf_getblkcnt(ch->b) != blkcnt) && + sndbuf_resize(ch->b, blkcnt, blksz) != 0) + device_printf(ch->devinfo->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + ch->blksz = sndbuf_getblksz(ch->b); + ch->blkcnt = sndbuf_getblkcnt(ch->b); + + return (0); +} + +static uint32_t +hdaa_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct hdaa_chan *ch = data; + + hdaa_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt); + + return (ch->blksz); +} + +static void +hdaa_channel_stop(struct hdaa_chan *ch) +{ + struct hdaa_devinfo *devinfo = ch->devinfo; + struct hdaa_widget *w; + int i; + + ch->flags &= ~HDAA_CHN_RUNNING; + HDAC_STREAM_STOP(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); + for (i = 0; ch->io[i] != -1; i++) { + w = hdaa_widget_get(ch->devinfo, ch->io[i]); + if (w == NULL) + continue; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + hda_command(devinfo->dev, + HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], 0)); + } + hda_command(devinfo->dev, + HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], + 0)); + } + HDAC_STREAM_FREE(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); +} + +static int +hdaa_channel_start(struct hdaa_chan *ch) +{ + struct hdaa_devinfo *devinfo = ch->devinfo; + + ch->ptr = 0; + ch->prevptr = 0; + ch->sid = HDAC_STREAM_ALLOC(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, hdaa_stream_format(ch), &ch->dmapos); + if (ch->sid <= 0) + return (EBUSY); + hdaa_audio_setup(ch); + HDAC_STREAM_RESET(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); + HDAC_STREAM_START(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid, + sndbuf_getbufaddr(ch->b), ch->blksz, ch->blkcnt); + ch->flags |= HDAA_CHN_RUNNING; + return (0); +} + +static int +hdaa_channel_trigger(kobj_t obj, void *data, int go) +{ + struct hdaa_chan *ch = data; + int error = 0; + + if (!PCMTRIG_COMMON(go)) + return (0); + + hdaa_lock(ch->devinfo); + switch (go) { + case PCMTRIG_START: + error = hdaa_channel_start(ch); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + hdaa_channel_stop(ch); + break; + default: + break; + } + hdaa_unlock(ch->devinfo); + + return (error); +} + +static uint32_t +hdaa_channel_getptr(kobj_t obj, void *data) +{ + struct hdaa_chan *ch = data; + struct hdaa_devinfo *devinfo = ch->devinfo; + uint32_t ptr; + + hdaa_lock(devinfo); + if (ch->dmapos != NULL) { + ptr = *(ch->dmapos); + } else { + ptr = HDAC_STREAM_GETPTR( + device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); + } + hdaa_unlock(devinfo); + + /* + * Round to available space and force 128 bytes aligment. + */ + ptr %= ch->blksz * ch->blkcnt; + ptr &= HDA_BLK_ALIGN; + + return (ptr); +} + +static struct pcmchan_caps * +hdaa_channel_getcaps(kobj_t obj, void *data) +{ + return (&((struct hdaa_chan *)data)->caps); +} + +static kobj_method_t hdaa_channel_methods[] = { + KOBJMETHOD(channel_init, hdaa_channel_init), + KOBJMETHOD(channel_setformat, hdaa_channel_setformat), + KOBJMETHOD(channel_setspeed, hdaa_channel_setspeed), + KOBJMETHOD(channel_setblocksize, hdaa_channel_setblocksize), + KOBJMETHOD(channel_setfragments, hdaa_channel_setfragments), + KOBJMETHOD(channel_trigger, hdaa_channel_trigger), + KOBJMETHOD(channel_getptr, hdaa_channel_getptr), + KOBJMETHOD(channel_getcaps, hdaa_channel_getcaps), + KOBJMETHOD_END +}; +CHANNEL_DECLARE(hdaa_channel); + +static int +hdaa_audio_ctl_ossmixer_init(struct snd_mixer *m) +{ + struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + uint32_t mask, recmask; + int i, j, softpcmvol; + + hdaa_lock(devinfo); + + /* Make sure that in case of soft volume it won't stay muted. */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + pdevinfo->left[i] = 100; + pdevinfo->right[i] = 100; + } + + mask = 0; + recmask = 0; + + /* Declate EAPD as ogain control. */ + if (pdevinfo->playas >= 0) { + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDA_INVALID || + w->bindas != pdevinfo->playas) + continue; + mask |= SOUND_MASK_OGAIN; + break; + } + } + + /* Declare volume controls assigned to this association. */ + i = 0; + ctl = NULL; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if ((pdevinfo->playas >= 0 && + ctl->widget->bindas == pdevinfo->playas) || + (pdevinfo->recas >= 0 && + ctl->widget->bindas == pdevinfo->recas) || + (ctl->widget->bindas == -2 && pdevinfo->index == 0)) + mask |= ctl->ossmask; + } + + /* Declare record sources available to this association. */ + if (pdevinfo->recas >= 0) { + for (i = 0; i < 16; i++) { + if (devinfo->as[pdevinfo->recas].dacs[0][i] < 0) + continue; + w = hdaa_widget_get(devinfo, + devinfo->as[pdevinfo->recas].dacs[0][i]); + if (w == NULL || w->enable == 0) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->bindas != pdevinfo->recas && + cw->bindas != -2) + continue; + recmask |= cw->ossmask; + } + } + } + + /* Declare soft PCM volume if needed. */ + if (pdevinfo->playas >= 0) { + ctl = NULL; + if ((mask & SOUND_MASK_PCM) == 0 || + (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL)) { + softpcmvol = 1; + mask |= SOUND_MASK_PCM; + } else { + softpcmvol = 0; + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if (ctl->widget->bindas != pdevinfo->playas && + (ctl->widget->bindas != -2 || pdevinfo->index != 0)) + continue; + if (!(ctl->ossmask & SOUND_MASK_PCM)) + continue; + if (ctl->step > 0) + break; + } + } + + if (softpcmvol == 1 || ctl == NULL) { + pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "%s Soft PCM volume\n", + (softpcmvol == 1) ? "Forcing" : "Enabling"); + ); + } + } + + /* Declare master volume if needed. */ + if (pdevinfo->playas >= 0) { + if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) == + SOUND_MASK_PCM) { + mask |= SOUND_MASK_VOLUME; + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "Forcing master volume with PCM\n"); + ); + } + } + + recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + + mix_setrecdevs(m, recmask); + mix_setdevs(m, mask); + + hdaa_unlock(devinfo); + + return (0); +} + +static int +hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + struct hdaa_audio_ctl *ctl; + uint32_t mute; + int lvol, rvol; + int i, j; + + hdaa_lock(devinfo); + /* Save new values. */ + pdevinfo->left[dev] = left; + pdevinfo->right[dev] = right; + + /* 'ogain' is the special case implemented with EAPD. */ + if (dev == SOUND_MIXER_OGAIN) { + uint32_t orig; + w = NULL; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDA_INVALID) + continue; + break; + } + if (i >= devinfo->endnode) { + hdaa_unlock(devinfo); + 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; + + val = w->param.eapdbtl; + if (devinfo->quirks & HDAA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hda_command(devinfo->dev, + HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid, val)); + } + hdaa_unlock(devinfo); + return (left | (left << 8)); + } + + /* Recalculate all controls related to this OSS device. */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || + !(ctl->ossmask & (1 << dev))) + continue; + if (!((pdevinfo->playas >= 0 && + ctl->widget->bindas == pdevinfo->playas) || + (pdevinfo->recas >= 0 && + ctl->widget->bindas == pdevinfo->recas) || + ctl->widget->bindas == -2)) + continue; + + lvol = 100; + rvol = 100; + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if (ctl->ossmask & (1 << j)) { + lvol = lvol * pdevinfo->left[j] / 100; + rvol = rvol * pdevinfo->right[j] / 100; + } + } + mute = (lvol == 0) ? HDAA_AMP_MUTE_LEFT : 0; + mute |= (rvol == 0) ? HDAA_AMP_MUTE_RIGHT : 0; + lvol = (lvol * ctl->step + 50) / 100; + rvol = (rvol * ctl->step + 50) / 100; + hdaa_audio_ctl_amp_set(ctl, mute, lvol, rvol); + } + hdaa_unlock(devinfo); + + return (left | (right << 8)); +} + +/* + * Commutate specified record source. + */ +static uint32_t +hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + char buf[64]; + int i, muted; + uint32_t res = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->bindas == -1) + continue; + /* Call recursively to trace signal to it's source if needed. */ + if ((src & cw->ossmask) != 0) { + if (cw->ossdev < 0) { + res |= hdaa_audio_ctl_recsel_comm(pdevinfo, src, + w->conns[i], depth + 1); + } else { + res |= cw->ossmask; + } + } + /* We have two special cases: mixers and others (selectors). */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { + ctl = hdaa_audio_ctl_amp_get(devinfo, + w->nid, HDAA_CTL_IN, i, 1); + if (ctl == NULL) + continue; + /* If we have input control on this node mute them + * according to requested sources. */ + muted = (src & cw->ossmask) ? 0 : 1; + if (muted != ctl->forcemute) { + ctl->forcemute = muted; + hdaa_audio_ctl_amp_set(ctl, + HDAA_AMP_MUTE_DEFAULT, + HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT); + } + HDA_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "Recsel (%s): nid %d source %d %s\n", + hdaa_audio_ctl_ossmixer_mask2allname( + src, buf, sizeof(buf)), + nid, i, muted?"mute":"unmute"); + ); + } else { + if (w->nconns == 1) + break; + if ((src & cw->ossmask) == 0) + continue; + /* If we found requested source - select it and exit. */ + hdaa_widget_connection_select(w, i); + HDA_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "Recsel (%s): nid %d source %d select\n", + hdaa_audio_ctl_ossmixer_mask2allname( + src, buf, sizeof(buf)), + nid, i); + ); + break; + } + } + return (res); +} + +static uint32_t +hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + struct hdaa_audio_as *as; + struct hdaa_chan *ch; + int i, j; + uint32_t ret = 0xffffffff; + + hdaa_lock(devinfo); + if (pdevinfo->recas < 0) { + hdaa_unlock(devinfo); + return (0); + } + as = &devinfo->as[pdevinfo->recas]; + + /* For non-mixed associations we always recording everything. */ + if (!as->mixed) { + hdaa_unlock(devinfo); + return (mix_getrecdevs(m)); + } + + /* Commutate requested recsrc for each ADC. */ + for (j = 0; j < as->num_chans; j++) { + ch = &devinfo->chans[as->chans[j]]; + for (i = 0; ch->io[i] >= 0; i++) { + w = hdaa_widget_get(devinfo, ch->io[i]); + if (w == NULL || w->enable == 0) + continue; + ret &= hdaa_audio_ctl_recsel_comm(pdevinfo, src, + ch->io[i], 0); + } + } + + hdaa_unlock(devinfo); + return ((ret == 0xffffffff)? 0 : ret); +} + +static kobj_method_t hdaa_audio_ctl_ossmixer_methods[] = { + KOBJMETHOD(mixer_init, hdaa_audio_ctl_ossmixer_init), + KOBJMETHOD(mixer_set, hdaa_audio_ctl_ossmixer_set), + KOBJMETHOD(mixer_setrecsrc, hdaa_audio_ctl_ossmixer_setrecsrc), + KOBJMETHOD_END +}; +MIXER_DECLARE(hdaa_audio_ctl_ossmixer); + +static void +hdaa_dump_gpi(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + int i; + uint32_t data, wake, unsol, sticky; + + if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap) > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPI_DATA(0, devinfo->nid)); + wake = hda_command(dev, + HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(0, devinfo->nid)); + unsol = hda_command(dev, + HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(0, devinfo->nid)); + sticky = hda_command(dev, + HDA_CMD_GET_GPI_STICKY_MASK(0, devinfo->nid)); + for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap); i++) { + device_printf(dev, " GPI%d:%s%s%s state=%d", i, + (sticky & (1 << i)) ? " sticky" : "", + (unsol & (1 << i)) ? " unsol" : "", + (wake & (1 << i)) ? " wake" : "", + (data >> i) & 1); + } + } +} + +static void +hdaa_dump_gpio(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + int i; + uint32_t data, dir, enable, wake, unsol, sticky; + + if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap) > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPIO_DATA(0, devinfo->nid)); + enable = hda_command(dev, + HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid)); + dir = hda_command(dev, + HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid)); + wake = hda_command(dev, + HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(0, devinfo->nid)); + unsol = hda_command(dev, + HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(0, devinfo->nid)); + sticky = hda_command(dev, + HDA_CMD_GET_GPIO_STICKY_MASK(0, devinfo->nid)); + for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); i++) { + device_printf(dev, " GPIO%d: ", i); + if ((enable & (1 << i)) == 0) { + printf("disabled\n"); + continue; + } + if ((dir & (1 << i)) == 0) { + printf("input%s%s%s", + (sticky & (1 << i)) ? " sticky" : "", + (unsol & (1 << i)) ? " unsol" : "", + (wake & (1 << i)) ? " wake" : ""); + } else + printf("output"); + printf(" state=%d\n", (data >> i) & 1); + } + } +} + +static void +hdaa_dump_gpo(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + int i; + uint32_t data; + + if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap) > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPO_DATA(0, devinfo->nid)); + for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); i++) { + device_printf(dev, " GPO%d: state=%d", i, + (data >> i) & 1); + } + } +} + +static void +hdaa_audio_parse(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + uint32_t res; + int i; + nid_t nid; + + nid = devinfo->nid; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_GPIO_COUNT)); + devinfo->gpio_cap = res; + + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "NumGPIO=%d NumGPO=%d " + "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap)); + hdaa_dump_gpi(devinfo); + hdaa_dump_gpio(devinfo); + hdaa_dump_gpo(devinfo); + ); + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_STREAM_FORMATS)); + devinfo->supp_stream_formats = res; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE)); + devinfo->supp_pcm_size_rate = res; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_OUTPUT_AMP_CAP)); + devinfo->outamp_cap = res; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_INPUT_AMP_CAP)); + devinfo->inamp_cap = res; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + device_printf(devinfo->dev, "Ghost widget! nid=%d!\n", i); + else { + w->devinfo = devinfo; + w->nid = i; + w->enable = 1; + w->selconn = -1; + w->pflags = 0; + w->ossdev = -1; + w->bindas = -1; + w->param.eapdbtl = HDA_INVALID; + hdaa_widget_parse(w); + } + } +} + +static void +hdaa_audio_postprocess(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + hdaa_widget_postprocess(w); + } +} + +static void +hdaa_audio_ctl_parse(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_ctl *ctls; + struct hdaa_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 = hdaa_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 = hdaa_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + max++; + } + break; + default: + max++; + break; + } + } + } + devinfo->ctlcnt = max; + + if (max < 1) + return; + + ctls = (struct hdaa_audio_ctl *)malloc( + sizeof(*ctls) * max, M_HDAA, M_ZERO | M_NOWAIT); + + if (ctls == NULL) { + /* Blekh! */ + device_printf(devinfo->dev, "unable to allocate ctls!\n"); + devinfo->ctlcnt = 0; + return; + } + + cnt = 0; + for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { + if (cnt >= max) { + device_printf(devinfo->dev, "%s: Ctl overflow!\n", + __func__); + break; + } + w = hdaa_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(devinfo->dev, + "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; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->waspin) + ctls[cnt].ndir = HDAA_CTL_IN; + else + ctls[cnt].ndir = HDAA_CTL_OUT; + ctls[cnt++].dir = HDAA_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(devinfo->dev, + "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(devinfo->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + cw = hdaa_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].ndir = HDAA_CTL_IN; + ctls[cnt++].dir = HDAA_CTL_IN; + } + break; + default: + if (cnt >= max) { + device_printf(devinfo->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; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + ctls[cnt].ndir = HDAA_CTL_OUT; + else + ctls[cnt].ndir = HDAA_CTL_IN; + ctls[cnt++].dir = HDAA_CTL_IN; + break; + } + } + } + + devinfo->ctl = ctls; +} + +static void +hdaa_audio_as_parse(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as; + struct hdaa_widget *w; + int i, j, cnt, max, type, dir, assoc, seq, first, hpredir; + + /* Count present associations */ + max = 0; + for (j = 1; j < 16; j++) { + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_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_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config) + != j) + continue; + max++; + if (j != 15) /* There could be many 1-pin assocs #15 */ + break; + } + } + + devinfo->ascnt = max; + + if (max < 1) + return; + + as = (struct hdaa_audio_as *)malloc( + sizeof(*as) * max, M_HDAA, M_ZERO | M_NOWAIT); + + if (as == NULL) { + /* Blekh! */ + device_printf(devinfo->dev, "unable to allocate assocs!\n"); + devinfo->ascnt = 0; + return; + } + + for (i = 0; i < max; i++) { + as[i].hpredir = -1; + as[i].digital = 0; + as[i].num_chans = 1; + as[i].unsol = -1; + as[i].location = -1; + } + + /* Scan associations skipping as=0. */ + cnt = 0; + for (j = 1; j < 16; j++) { + first = 16; + hpredir = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config); + seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config); + if (assoc != j) { + continue; + } + KASSERT(cnt < max, + ("%s: Associations owerflow (%d of %d)", + __func__, cnt, max)); + type = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + /* Get pin direction. */ + if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT) + dir = HDAA_CTL_OUT; + else + dir = HDAA_CTL_IN; + /* If this is a first pin - create new association. */ + if (as[cnt].pincnt == 0) { + as[cnt].enable = 1; + as[cnt].index = j; + as[cnt].dir = dir; + } + if (seq < first) + first = seq; + /* Check association correctness. */ + if (as[cnt].pins[seq] != 0) { + device_printf(devinfo->dev, "%s: Duplicate pin %d (%d) " + "in association %d! Disabling association.\n", + __func__, seq, w->nid, j); + as[cnt].enable = 0; + } + if (dir != as[cnt].dir) { + device_printf(devinfo->dev, "%s: Pin %d has wrong " + "direction for association %d! Disabling " + "association.\n", + __func__, w->nid, j); + as[cnt].enable = 0; + } + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap)) + as[cnt].digital = 3; + else if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) + as[cnt].digital = 2; + else + as[cnt].digital = 1; + } + if (as[cnt].location == -1) { + as[cnt].location = + HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config); + } else if (as[cnt].location != + HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config)) { + as[cnt].location = -2; + } + /* Headphones with seq=15 may mean redirection. */ + if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT && + seq == 15) + hpredir = 1; + as[cnt].pins[seq] = w->nid; + as[cnt].pincnt++; + /* Association 15 is a multiple unassociated pins. */ + if (j == 15) + cnt++; + } + if (j != 15 && as[cnt].pincnt > 0) { + if (hpredir && as[cnt].pincnt > 1) + as[cnt].hpredir = first; + cnt++; + } + } + for (i = 0; i < max; i++) { + if (as[i].dir == HDAA_CTL_IN && (as[i].pincnt == 1 || + as[i].pins[14] > 0 || as[i].pins[15] > 0)) + as[i].mixed = 1; + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "%d associations found:\n", max); + for (i = 0; i < max; i++) { + device_printf(devinfo->dev, + "Association %d (%d) %s%s:\n", + i, as[i].index, (as[i].dir == HDAA_CTL_IN)?"in":"out", + as[i].enable?"":" (disabled)"); + for (j = 0; j < 16; j++) { + if (as[i].pins[j] == 0) + continue; + device_printf(devinfo->dev, + " Pin nid=%d seq=%d\n", + as[i].pins[j], j); + } + } + ); + + devinfo->as = as; +} + +/* + * Trace path from DAC to pin. + */ +static nid_t +hdaa_audio_trace_dac(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid, + int dupseq, int min, int only, int depth) +{ + struct hdaa_widget *w; + int i, im = -1; + nid_t m = 0, ret; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + } + ); + /* Use only unused widgets */ + if (w->bindas >= 0 && w->bindas != as) { + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*snid %d busy by association %d\n", + depth + 1, "", w->nid, w->bindas); + } + ); + return (0); + } + if (dupseq < 0) { + if (w->bindseqmask != 0) { + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + } + ); + return (0); + } + } else { + /* If this is headphones - allow duplicate first pin. */ + if (w->bindseqmask != 0 && + (w->bindseqmask & (1 << dupseq)) == 0) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + ); + return (0); + } + } + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + /* Do not traverse input. AD1988 has digital monitor + for which we are not ready. */ + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + /* If we are tracing HP take only dac of first pin. */ + if ((only == 0 || only == w->nid) && + (w->nid >= min) && (dupseq < 0 || w->nid == + devinfo->as[as].dacs[0][dupseq])) + m = w->nid; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) + break; + /* Fall */ + default: + /* Find reachable DACs with smallest nid respecting constraints. */ + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + if (w->selconn != -1 && w->selconn != i) + continue; + if ((ret = hdaa_audio_trace_dac(devinfo, as, seq, + w->conns[i], dupseq, min, only, depth + 1)) != 0) { + if (m == 0 || ret < m) { + m = ret; + im = i; + } + if (only || dupseq >= 0) + break; + } + } + if (im >= 0 && only && ((w->nconns > 1 && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + w->selconn = im; + break; + } + if (m && only) { + w->bindas = as; + w->bindseqmask |= (1 << seq); + } + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, m); + } + ); + return (m); +} + +/* + * Trace path from widget to ADC. + */ +static nid_t +hdaa_audio_trace_adc(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid, + int mixed, int min, int only, int depth, int *length, int onlylength) +{ + struct hdaa_widget *w, *wc; + int i, j, im, lm = HDA_PARSE_MAXDEPTH; + nid_t m = 0, ret; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + ); + /* Use only unused widgets */ + if (w->bindas >= 0 && w->bindas != as) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + return (0); + } + if (!mixed && w->bindseqmask != 0) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + ); + return (0); + } + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + if ((only == 0 || only == w->nid) && (w->nid >= min) && + (onlylength == 0 || onlylength == depth)) { + m = w->nid; + if (length != NULL) + *length = depth; + } + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) + break; + /* Fall */ + default: + /* Try to find reachable ADCs with specified nid. */ + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + wc = hdaa_widget_get(devinfo, j); + if (wc == NULL || wc->enable == 0) + continue; + im = -1; + for (i = 0; i < wc->nconns; i++) { + if (wc->connsenable[i] == 0) + continue; + if (wc->conns[i] != nid) + continue; + if ((ret = hdaa_audio_trace_adc(devinfo, as, seq, + j, mixed, min, only, depth + 1, + length, onlylength)) != 0) { + if (m == 0 || ret < m || + (ret == m && length != NULL && + *length < lm)) { + m = ret; + im = i; + lm = *length; + } + if (only) + break; + } + } + if (im >= 0 && only && ((wc->nconns > 1 && + wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + wc->selconn = im; + } + break; + } + if (m && only) { + w->bindas = as; + w->bindseqmask |= (1 << seq); + } + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, m); + ); + return (m); +} + +/* + * Erase trace path of the specified association. + */ +static void +hdaa_audio_undo_trace(struct hdaa_devinfo *devinfo, int as, int seq) +{ + struct hdaa_widget *w; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == as) { + if (seq >= 0) { + w->bindseqmask &= ~(1 << seq); + if (w->bindseqmask == 0) { + w->bindas = -1; + w->selconn = -1; + } + } else { + w->bindas = -1; + w->bindseqmask = 0; + w->selconn = -1; + } + } + } +} + +/* + * Trace association path from DAC to output + */ +static int +hdaa_audio_trace_as_out(struct hdaa_devinfo *devinfo, int as, int seq) +{ + struct hdaa_audio_as *ases = devinfo->as; + int i, hpredir; + nid_t min, res; + + /* Find next pin */ + for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) + ; + /* Check if there is no any left. If so - we succeeded. */ + if (i == 16) + return (1); + + hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1; + min = 0; + do { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing pin %d with min nid %d", + ases[as].pins[i], min); + if (hpredir >= 0) + printf(" and hpredir %d", hpredir); + printf("\n"); + ); + /* Trace this pin taking min nid into account. */ + res = hdaa_audio_trace_dac(devinfo, as, i, + ases[as].pins[i], hpredir, min, 0, 0); + if (res == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Unable to trace pin %d seq %d with min " + "nid %d", + ases[as].pins[i], i, min); + if (hpredir >= 0) + printf(" and hpredir %d", hpredir); + printf("\n"); + ); + return (0); + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Pin %d traced to DAC %d", + ases[as].pins[i], res); + if (hpredir >= 0) + printf(" and hpredir %d", hpredir); + if (ases[as].fakeredir) + printf(" with fake redirection"); + printf("\n"); + ); + /* Trace again to mark the path */ + hdaa_audio_trace_dac(devinfo, as, i, + ases[as].pins[i], hpredir, min, res, 0); + ases[as].dacs[0][i] = res; + /* We succeeded, so call next. */ + if (hdaa_audio_trace_as_out(devinfo, as, i + 1)) + return (1); + /* If next failed, we should retry with next min */ + hdaa_audio_undo_trace(devinfo, as, i); + ases[as].dacs[0][i] = 0; + min = res + 1; + } while (1); +} + +/* + * Check equivalency of two DACs. + */ +static int +hdaa_audio_dacs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2) +{ + struct hdaa_devinfo *devinfo = w1->devinfo; + struct hdaa_widget *w3; + int i, j, c1, c2; + + if (memcmp(&w1->param, &w2->param, sizeof(w1->param))) + return (0); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w3 = hdaa_widget_get(devinfo, i); + if (w3 == NULL || w3->enable == 0) + continue; + if (w3->bindas != w1->bindas) + continue; + if (w3->nconns == 0) + continue; + c1 = c2 = -1; + for (j = 0; j < w3->nconns; j++) { + if (w3->connsenable[j] == 0) + continue; + if (w3->conns[j] == w1->nid) + c1 = j; + if (w3->conns[j] == w2->nid) + c2 = j; + } + if (c1 < 0) + continue; + if (c2 < 0) + return (0); + if (w3->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + return (0); + } + return (1); +} + +/* + * Check equivalency of two ADCs. + */ +static int +hdaa_audio_adcs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2) +{ + struct hdaa_devinfo *devinfo = w1->devinfo; + struct hdaa_widget *w3, *w4; + int i; + + if (memcmp(&w1->param, &w2->param, sizeof(w1->param))) + return (0); + if (w1->nconns != 1 || w2->nconns != 1) + return (0); + if (w1->conns[0] == w2->conns[0]) + return (1); + w3 = hdaa_widget_get(devinfo, w1->conns[0]); + if (w3 == NULL || w3->enable == 0) + return (0); + w4 = hdaa_widget_get(devinfo, w2->conns[0]); + if (w4 == NULL || w4->enable == 0) + return (0); + if (w3->bindas == w4->bindas && w3->bindseqmask == w4->bindseqmask) + return (1); + if (w4->bindas >= 0) + return (0); + if (w3->type != w4->type) + return (0); + if (memcmp(&w3->param, &w4->param, sizeof(w3->param))) + return (0); + if (w3->nconns != w4->nconns) + return (0); + for (i = 0; i < w3->nconns; i++) { + if (w3->conns[i] != w4->conns[i]) + return (0); + } + return (1); +} + +/* + * Look for equivalent DAC/ADC to implement second channel. + */ +static void +hdaa_audio_adddac(struct hdaa_devinfo *devinfo, int asid) +{ + struct hdaa_audio_as *as = &devinfo->as[asid]; + struct hdaa_widget *w1, *w2; + int i, pos; + nid_t nid1, nid2; + + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Looking for additional %sC " + "for association %d (%d)\n", + (as->dir == HDAA_CTL_OUT) ? "DA" : "AD", + asid, as->index); + ); + + /* Find the exisitng DAC position and return if found more the one. */ + pos = -1; + for (i = 0; i < 16; i++) { + if (as->dacs[0][i] <= 0) + continue; + if (pos >= 0 && as->dacs[0][i] != as->dacs[0][pos]) + return; + pos = i; + } + + nid1 = as->dacs[0][pos]; + w1 = hdaa_widget_get(devinfo, nid1); + w2 = NULL; + for (nid2 = devinfo->startnode; nid2 < devinfo->endnode; nid2++) { + w2 = hdaa_widget_get(devinfo, nid2); + if (w2 == NULL || w2->enable == 0) + continue; + if (w2->bindas >= 0) + continue; + if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) { + if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (hdaa_audio_dacs_equal(w1, w2)) + break; + } else { + if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (hdaa_audio_adcs_equal(w1, w2)) + break; + } + } + if (nid2 >= devinfo->endnode) + return; + w2->bindas = w1->bindas; + w2->bindseqmask = w1->bindseqmask; + if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " ADC %d considered equal to ADC %d\n", nid2, nid1); + ); + w1 = hdaa_widget_get(devinfo, w1->conns[0]); + w2 = hdaa_widget_get(devinfo, w2->conns[0]); + w2->bindas = w1->bindas; + w2->bindseqmask = w1->bindseqmask; + } else { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " DAC %d considered equal to DAC %d\n", nid2, nid1); + ); + } + for (i = 0; i < 16; i++) { + if (as->dacs[0][i] <= 0) + continue; + as->dacs[as->num_chans][i] = nid2; + } + as->num_chans++; +} + +/* + * Trace association path from input to ADC + */ +static int +hdaa_audio_trace_as_in(struct hdaa_devinfo *devinfo, int as) +{ + struct hdaa_audio_as *ases = devinfo->as; + struct hdaa_widget *w; + int i, j, k, length; + + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->bindas >= 0 && w->bindas != as) + continue; + + /* Find next pin */ + for (i = 0; i < 16; i++) { + if (ases[as].pins[i] == 0) + continue; + + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing pin %d to ADC %d\n", + ases[as].pins[i], j); + ); + /* Trace this pin taking goal into account. */ + if (hdaa_audio_trace_adc(devinfo, as, i, + ases[as].pins[i], 1, 0, j, 0, &length, 0) == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Unable to trace pin %d to ADC %d, undo traces\n", + ases[as].pins[i], j); + ); + hdaa_audio_undo_trace(devinfo, as, -1); + for (k = 0; k < 16; k++) + ases[as].dacs[0][k] = 0; + break; + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Pin %d traced to ADC %d\n", + ases[as].pins[i], j); + ); + ases[as].dacs[0][i] = j; + } + if (i == 16) + return (1); + } + return (0); +} + +/* + * Trace association path from input to multiple ADCs + */ +static int +hdaa_audio_trace_as_in_mch(struct hdaa_devinfo *devinfo, int as, int seq) +{ + struct hdaa_audio_as *ases = devinfo->as; + int i, length; + nid_t min, res; + + /* Find next pin */ + for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) + ; + /* Check if there is no any left. If so - we succeeded. */ + if (i == 16) + return (1); + + min = 0; + do { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing pin %d with min nid %d", + ases[as].pins[i], min); + printf("\n"); + ); + /* Trace this pin taking min nid into account. */ + res = hdaa_audio_trace_adc(devinfo, as, i, + ases[as].pins[i], 0, min, 0, 0, &length, 0); + if (res == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Unable to trace pin %d seq %d with min " + "nid %d", + ases[as].pins[i], i, min); + printf("\n"); + ); + return (0); + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Pin %d traced to ADC %d\n", + ases[as].pins[i], res); + ); + /* Trace again to mark the path */ + hdaa_audio_trace_adc(devinfo, as, i, + ases[as].pins[i], 0, min, res, 0, &length, length); + ases[as].dacs[0][i] = res; + /* We succeeded, so call next. */ + if (hdaa_audio_trace_as_in_mch(devinfo, as, i + 1)) + return (1); + /* If next failed, we should retry with next min */ + hdaa_audio_undo_trace(devinfo, as, i); + ases[as].dacs[0][i] = 0; + min = res + 1; + } while (1); +} + +/* + * Trace input monitor path from mixer to output association. + */ +static int +hdaa_audio_trace_to_out(struct hdaa_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdaa_audio_as *ases = devinfo->as; + struct hdaa_widget *w, *wc; + int i, j; + nid_t res = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + ); + /* Use only unused widgets */ + if (depth > 0 && w->bindas != -1) { + if (w->bindas < 0 || ases[w->bindas].dir == HDAA_CTL_OUT) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d found output association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + if (w->bindas >= 0) + w->pflags |= HDAA_ADC_MONITOR; + return (1); + } else { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by input association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + return (0); + } + } + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + /* Do not traverse input. AD1988 has digital monitor + for which we are not ready. */ + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) + break; + /* Fall */ + default: + /* Try to find reachable ADCs with specified nid. */ + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + wc = hdaa_widget_get(devinfo, j); + if (wc == NULL || wc->enable == 0) + continue; + for (i = 0; i < wc->nconns; i++) { + if (wc->connsenable[i] == 0) + continue; + if (wc->conns[i] != nid) + continue; + if (hdaa_audio_trace_to_out(devinfo, + j, depth + 1) != 0) { + res = 1; + if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + wc->selconn == -1) + wc->selconn = i; + } + } + } + break; + } + if (res && w->bindas == -1) + w->bindas = -2; + + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, res); + ); + return (res); +} + +/* + * Trace extra associations (beeper, monitor) + */ +static void +hdaa_audio_trace_as_extra(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int j; + + /* Input monitor */ + /* Find mixer associated with input, but supplying signal + for output associations. Hope it will be input monitor. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing input monitor\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Tracing nid %d to out\n", + j); + ); + if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " nid %d is input monitor\n", + w->nid); + ); + w->ossdev = SOUND_MIXER_IMIX; + } + } + + /* Other inputs monitor */ + /* Find input pins supplying signal for output associations. + Hope it will be input monitoring. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing other input monitors\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Tracing nid %d to out\n", + j); + ); + if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " nid %d is input monitor\n", + w->nid); + ); + } + } + + /* Beeper */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing beeper\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) + continue; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing nid %d to out\n", + j); + ); + if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " nid %d traced to out\n", + j); + ); + } + w->bindas = -2; + } +} + +/* + * Bind assotiations to PCM channels + */ +static void +hdaa_audio_bind_as(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + int i, j, cnt = 0, free; + + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable) + cnt += as[j].num_chans; + } + if (devinfo->num_chans == 0) { + devinfo->chans = (struct hdaa_chan *)malloc( + sizeof(struct hdaa_chan) * cnt, + M_HDAA, M_ZERO | M_NOWAIT); + if (devinfo->chans == NULL) { + device_printf(devinfo->dev, + "Channels memory allocation failed!\n"); + return; + } + } else { + devinfo->chans = (struct hdaa_chan *)realloc(devinfo->chans, + sizeof(struct hdaa_chan) * (devinfo->num_chans + cnt), + M_HDAA, M_ZERO | M_NOWAIT); + if (devinfo->chans == NULL) { + devinfo->num_chans = 0; + device_printf(devinfo->dev, + "Channels memory allocation failed!\n"); + return; + } + /* Fixup relative pointers after realloc */ + for (j = 0; j < devinfo->num_chans; j++) + devinfo->chans[j].caps.fmtlist = devinfo->chans[j].fmtlist; + } + free = devinfo->num_chans; + devinfo->num_chans += cnt; + + for (j = free; j < free + cnt; j++) { + devinfo->chans[j].devinfo = devinfo; + devinfo->chans[j].as = -1; + } + + /* Assign associations in order of their numbers, */ + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable == 0) + continue; + for (i = 0; i < as[j].num_chans; i++) { + devinfo->chans[free].as = j; + devinfo->chans[free].asindex = i; + devinfo->chans[free].dir = + (as[j].dir == HDAA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY; + hdaa_pcmchannel_setup(&devinfo->chans[free]); + as[j].chans[i] = free; + free++; + } + } +} + +static void +hdaa_audio_disable_nonaudio(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + /* Disable power and volume widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d due to it's" + " non-audio type.\n", + w->nid); + ); + } + } +} + +static void +hdaa_audio_disable_useless(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + int done, found, i, j, k; + + /* Disable useless pins. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling pin nid %d due" + " to None connectivity.\n", + w->nid); + ); + } else if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling unassociated" + " pin nid %d.\n", + w->nid); + ); + } + } + } + do { + done = 1; + /* Disable and mute controls for disabled widgets. */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if (ctl->widget->enable == 0 || + (ctl->childwidget != NULL && + ctl->childwidget->enable == 0)) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + if (ctl->ndir == HDAA_CTL_IN) + ctl->widget->connsenable[ctl->index] = 0; + done = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling ctl %d nid %d cnid %d due" + " to disabled widget.\n", i, + ctl->widget->nid, + (ctl->childwidget != NULL)? + ctl->childwidget->nid:-1); + ); + } + } + /* Disable useless widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + /* Disable inputs with disabled child widgets. */ + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) { + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) { + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d connection %d due" + " to disabled child widget.\n", + i, j); + ); + } + } + } + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + /* Disable mixers and selectors without inputs. */ + found = 0; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) { + found = 1; + break; + } + } + if (found == 0) { + w->enable = 0; + done = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d due to all it's" + " inputs disabled.\n", w->nid); + ); + } + /* Disable nodes without consumers. */ + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + found = 0; + for (k = devinfo->startnode; k < devinfo->endnode; k++) { + cw = hdaa_widget_get(devinfo, k); + if (cw == NULL || cw->enable == 0) + continue; + for (j = 0; j < cw->nconns; j++) { + if (cw->connsenable[j] && cw->conns[j] == i) { + found = 1; + break; + } + } + } + if (found == 0) { + w->enable = 0; + done = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d due to all it's" + " consumers disabled.\n", w->nid); + ); + } + } + } while (done == 0); + +} + +static void +hdaa_audio_disable_unas(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + int i, j, k; + + /* Disable unassosiated widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == -1) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling unassociated nid %d.\n", + w->nid); + ); + } + } + /* Disable input connections on input pin and + * output on output. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (w->bindas < 0) + continue; + if (as[w->bindas].dir == HDAA_CTL_IN) { + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling connection to input pin " + "nid %d conn %d.\n", + i, j); + ); + } + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_IN, -1, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + } else { + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_OUT, -1, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + for (k = devinfo->startnode; k < devinfo->endnode; k++) { + cw = hdaa_widget_get(devinfo, k); + if (cw == NULL || cw->enable == 0) + continue; + for (j = 0; j < cw->nconns; j++) { + if (cw->connsenable[j] && cw->conns[j] == i) { + cw->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling connection from output pin " + "nid %d conn %d cnid %d.\n", + k, j, i); + ); + if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + cw->nconns > 1) + continue; + ctl = hdaa_audio_ctl_amp_get(devinfo, k, + HDAA_CTL_IN, j, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + } + } + } + } + } +} + +static void +hdaa_audio_disable_notselected(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i, j; + + /* On playback path we can safely disable all unseleted inputs. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->nconns <= 1) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + if (w->bindas < 0 || as[w->bindas].dir == HDAA_CTL_IN) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + if (w->selconn < 0 || w->selconn == j) + continue; + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling unselected connection " + "nid %d conn %d.\n", + i, j); + ); + } + } +} + +static void +hdaa_audio_disable_crossas(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *ases = devinfo->as; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + int i, j; + + /* Disable crossassociatement and unwanted crosschannel connections. */ + /* ... using selectors */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->nconns <= 1) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + /* Allow any -> mix */ + if (w->bindas == -2) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || w->enable == 0) + continue; + /* Allow mix -> out. */ + if (cw->bindas == -2 && w->bindas >= 0 && + ases[w->bindas].dir == HDAA_CTL_OUT) + continue; + /* Allow mix -> mixed-in. */ + if (cw->bindas == -2 && w->bindas >= 0 && + ases[w->bindas].mixed) + continue; + /* Allow in -> mix. */ + if ((w->pflags & HDAA_ADC_MONITOR) && + cw->bindas >= 0 && + ases[cw->bindas].dir == HDAA_CTL_IN) + continue; + /* Allow if have common as/seqs. */ + if (w->bindas == cw->bindas && + (w->bindseqmask & cw->bindseqmask) != 0) + continue; + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling crossassociatement connection " + "nid %d conn %d cnid %d.\n", + i, j, cw->nid); + ); + } + } + /* ... using controls */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->childwidget == NULL) + continue; + /* Allow any -> mix */ + if (ctl->widget->bindas == -2) + continue; + /* Allow mix -> out. */ + if (ctl->childwidget->bindas == -2 && + ctl->widget->bindas >= 0 && + ases[ctl->widget->bindas].dir == HDAA_CTL_OUT) + continue; + /* Allow mix -> mixed-in. */ + if (ctl->childwidget->bindas == -2 && + ctl->widget->bindas >= 0 && + ases[ctl->widget->bindas].mixed) + continue; + /* Allow in -> mix. */ + if ((ctl->widget->pflags & HDAA_ADC_MONITOR) && + ctl->childwidget->bindas >= 0 && + ases[ctl->childwidget->bindas].dir == HDAA_CTL_IN) + continue; + /* Allow if have common as/seqs. */ + if (ctl->widget->bindas == ctl->childwidget->bindas && + (ctl->widget->bindseqmask & ctl->childwidget->bindseqmask) != 0) + continue; + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + if (ctl->ndir == HDAA_CTL_IN) + ctl->widget->connsenable[ctl->index] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling crossassociatement connection " + "ctl %d nid %d cnid %d.\n", i, + ctl->widget->nid, + ctl->childwidget->nid); + ); + } + +} + +#define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) + +/* + * Find controls to control amplification for source. + */ +static int +hdaa_audio_ctl_source_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, + int ossdev, int ctlable, int depth, int need) +{ + struct hdaa_widget *w, *wc; + struct hdaa_audio_ctl *ctl; + int i, j, conns = 0, rneed; + + if (depth > HDA_PARSE_MAXDEPTH) + return (need); + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (need); + + /* Count number of active inputs. */ + if (depth > 0) { + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) + conns++; + } + } + + /* If this is not a first step - use input mixer. + Pins have common input ctl so care must be taken. */ + if (depth > 0 && ctlable && (conns == 1 || + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN, + index, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + /* If widget has own ossdev - not traverse it. + It will be traversed on it's own. */ + if (w->ossdev >= 0 && depth > 0) + return (need); + + /* We must not traverse pin */ + if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && + depth > 0) + return (need); + + /* record that this widget exports such signal, */ + w->ossmask |= (1 << ossdev); + + /* If signals mixed, we can't assign controls farther. + * Ignore this on depth zero. Caller must knows why. + * Ignore this for static selectors if this input selected. + */ + if (conns > 1) + ctlable = 0; + + if (ctlable) { + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + rneed = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + wc = hdaa_widget_get(devinfo, i); + if (wc == NULL || wc->enable == 0) + continue; + for (j = 0; j < wc->nconns; j++) { + if (wc->connsenable[j] && wc->conns[j] == nid) { + rneed |= hdaa_audio_ctl_source_amp(devinfo, + wc->nid, j, ossdev, ctlable, depth + 1, need); + } + } + } + rneed &= need; + + return (rneed); +} + +/* + * Find controls to control amplification for destination. + */ +static void +hdaa_audio_ctl_dest_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, + int ossdev, int depth, int need) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w, *wc; + struct hdaa_audio_ctl *ctl; + int i, j, consumers; + + if (depth > HDA_PARSE_MAXDEPTH) + return; + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return; + + if (depth > 0) { + /* If this node produce output for several consumers, + we can't touch it. */ + consumers = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + wc = hdaa_widget_get(devinfo, i); + if (wc == NULL || wc->enable == 0) + continue; + for (j = 0; j < wc->nconns; j++) { + if (wc->connsenable[j] && wc->conns[j] == nid) + consumers++; + } + } + /* The only exception is if real HP redirection is configured + and this is a duplication point. + XXX: Actually exception is not completely correct. + XXX: Duplication point check is not perfect. */ + if ((consumers == 2 && (w->bindas < 0 || + as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || + (w->bindseqmask & (1 << 15)) == 0)) || + consumers > 2) + return; + + /* Else use it's output mixer. */ + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_OUT, -1, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + /* We must not traverse pin */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + depth > 0) + return; + + for (i = 0; i < w->nconns; i++) { + int tneed = need; + if (w->connsenable[i] == 0) + continue; + if (index >= 0 && i != index) + continue; + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_IN, i, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & tneed) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + tneed &= ~HDA_CTL_GIVE(ctl); + } + hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev, + depth + 1, tneed); + } +} + +/* + * Assign OSS names to sound sources + */ +static void +hdaa_audio_assign_names(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i, j; + int type = -1, use, used = 0; + static const int types[7][13] = { + { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, + SOUND_MIXER_LINE3, -1 }, /* line */ + { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */ + { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */ + { SOUND_MIXER_CD, -1 }, /* cd */ + { SOUND_MIXER_SPEAKER, -1 }, /* speaker */ + { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, + -1 }, /* digital */ + { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, + SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT, + SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1, + SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR, + -1 } /* others */ + }; + + /* Surely known names */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == -1) + continue; + use = -1; + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (as[w->bindas].dir == HDAA_CTL_OUT) + break; + type = -1; + switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + type = 0; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) + == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) + break; + type = 1; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + type = 3; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + type = 4; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: + type = 5; + break; + } + if (type == -1) + break; + j = 0; + while (types[type][j] >= 0 && + (used & (1 << types[type][j])) != 0) { + j++; + } + if (types[type][j] >= 0) + use = types[type][j]; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + use = SOUND_MIXER_PCM; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: + use = SOUND_MIXER_SPEAKER; + break; + default: + break; + } + if (use >= 0) { + w->ossdev = use; + used |= (1 << use); + } + } + /* Semi-known names */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev >= 0) + continue; + if (w->bindas == -1) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (as[w->bindas].dir == HDAA_CTL_OUT) + continue; + type = -1; + switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: + type = 0; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + type = 2; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: + type = 5; + break; + } + if (type == -1) + break; + j = 0; + while (types[type][j] >= 0 && + (used & (1 << types[type][j])) != 0) { + j++; + } + if (types[type][j] >= 0) { + w->ossdev = types[type][j]; + used |= (1 << types[type][j]); + } + } + /* Others */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev >= 0) + continue; + if (w->bindas == -1) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (as[w->bindas].dir == HDAA_CTL_OUT) + continue; + j = 0; + while (types[6][j] >= 0 && + (used & (1 << types[6][j])) != 0) { + j++; + } + if (types[6][j] >= 0) { + w->ossdev = types[6][j]; + used |= (1 << types[6][j]); + } + } +} + +static void +hdaa_audio_build_tree(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + int j, res; + + /* Trace all associations in order of their numbers. */ + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable == 0) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing association %d (%d)\n", j, as[j].index); + ); + if (as[j].dir == HDAA_CTL_OUT) { +retry: + res = hdaa_audio_trace_as_out(devinfo, j, 0); + if (res == 0 && as[j].hpredir >= 0 && + as[j].fakeredir == 0) { + /* If CODEC can't do analog HP redirection + try to make it using one more DAC. */ + as[j].fakeredir = 1; + goto retry; + } + } else if (as[j].mixed) + res = hdaa_audio_trace_as_in(devinfo, j); + else + res = hdaa_audio_trace_as_in_mch(devinfo, j, 0); + if (res) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Association %d (%d) trace succeeded\n", + j, as[j].index); + ); + } else { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Association %d (%d) trace failed\n", + j, as[j].index); + ); + as[j].enable = 0; + } + } + + /* Look for additional DACs/ADCs. */ + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable == 0) + continue; + hdaa_audio_adddac(devinfo, j); + } + + /* Trace mixer and beeper pseudo associations. */ + hdaa_audio_trace_as_extra(devinfo); +} + +static void +hdaa_audio_assign_mixers(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_audio_ctl *ctl; + struct hdaa_widget *w, *cw; + int i, j; + + /* Assign mixers to the tree. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET || + (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + as[w->bindas].dir == HDAA_CTL_IN)) { + if (w->ossdev < 0) + continue; + hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, 1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + SOUND_MIXER_RECLEV, 0, 1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + as[w->bindas].dir == HDAA_CTL_OUT) { + hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + SOUND_MIXER_VOLUME, 0, 1); + } + if (w->ossdev == SOUND_MIXER_IMIX) { + if (hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, 1)) { + /* If we are unable to control input monitor + as source - try to control it as destination. */ + hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + w->ossdev, 0, 1); + } + } + if (w->pflags & HDAA_ADC_MONITOR) { + for (j = 0; j < w->nconns; j++) { + if (!w->connsenable[j]) + continue; + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->bindas == -1) + continue; + if (cw->bindas >= 0 && + as[cw->bindas].dir != HDAA_CTL_IN) + continue; + hdaa_audio_ctl_dest_amp(devinfo, + w->nid, j, SOUND_MIXER_IGAIN, 0, 1); + } + } + } + /* Treat unrequired as possible. */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->ossmask == 0) + ctl->ossmask = ctl->possmask; + } +} + +static void +hdaa_audio_prepare_pin_ctrl(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + uint32_t pincap; + int i; + + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + w->waspin == 0) + continue; + + pincap = w->wclass.pin.cap; + + /* Disable everything. */ + 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 | + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); + + if (w->enable == 0) { + /* Pin is unused so left it disabled. */ + continue; + } else if (w->waspin) { + /* Enable input for beeper input. */ + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + } else if (w->bindas < 0 || as[w->bindas].enable == 0) { + /* Pin is unused so left it disabled. */ + continue; + } else if (as[w->bindas].dir == HDAA_CTL_IN) { + /* Input pin, configure for input. */ + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + + if ((devinfo->quirks & HDAA_QUIRK_IVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->quirks & HDAA_QUIRK_IVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->quirks & HDAA_QUIRK_IVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } else { + /* Output pin, configure for output. */ + 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_HEADPHONE_CAP(pincap) && + (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; + + if ((devinfo->quirks & HDAA_QUIRK_OVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->quirks & HDAA_QUIRK_OVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->quirks & HDAA_QUIRK_OVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } + } +} + +static void +hdaa_audio_ctl_commit(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_ctl *ctl; + int i, z; + + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->ossmask != 0) { + /* Mute disabled and mixer controllable controls. + * Last will be initialized by mixer_init(). + * This expected to reduce click on startup. */ + hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_ALL, 0, 0); + continue; + } + /* Init fixed controls to 0dB amplification. */ + z = ctl->offset; + if (z > ctl->step) + z = ctl->step; + hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_NONE, z, z); + } +} + +static void +hdaa_gpio_commit(struct hdaa_devinfo *devinfo) +{ + uint32_t gdata, gmask, gdir; + int i, numgpio; + + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); + if (devinfo->gpio != 0 && numgpio != 0) { + gdata = hda_command(devinfo->dev, + HDA_CMD_GET_GPIO_DATA(0, devinfo->nid)); + gmask = hda_command(devinfo->dev, + HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid)); + gdir = hda_command(devinfo->dev, + HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid)); + for (i = 0; i < numgpio; i++) { + if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_SET(i)) { + gdata |= (1 << i); + gmask |= (1 << i); + gdir |= (1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_CLEAR(i)) { + gdata &= ~(1 << i); + gmask |= (1 << i); + gdir |= (1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_DISABLE(i)) { + gmask &= ~(1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_INPUT(i)) { + gmask |= (1 << i); + gdir &= ~(1 << i); + } + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, "GPIO commit\n"); + ); + hda_command(devinfo->dev, + HDA_CMD_SET_GPIO_ENABLE_MASK(0, devinfo->nid, gmask)); + hda_command(devinfo->dev, + HDA_CMD_SET_GPIO_DIRECTION(0, devinfo->nid, gdir)); + hda_command(devinfo->dev, + HDA_CMD_SET_GPIO_DATA(0, devinfo->nid, gdata)); + HDA_BOOTVERBOSE( + hdaa_dump_gpio(devinfo); + ); + } +} + +static void +hdaa_gpo_commit(struct hdaa_devinfo *devinfo) +{ + uint32_t gdata; + int i, numgpo; + + numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); + if (devinfo->gpo != 0 && numgpo != 0) { + gdata = hda_command(devinfo->dev, + HDA_CMD_GET_GPO_DATA(0, devinfo->nid)); + for (i = 0; i < numgpo; i++) { + if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_SET(i)) { + gdata |= (1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_CLEAR(i)) { + gdata &= ~(1 << i); + } + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, "GPO commit\n"); + ); + hda_command(devinfo->dev, + HDA_CMD_SET_GPO_DATA(0, devinfo->nid, gdata)); + HDA_BOOTVERBOSE( + hdaa_dump_gpo(devinfo); + ); + } +} + +static void +hdaa_audio_commit(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + /* Commit controls. */ + hdaa_audio_ctl_commit(devinfo); + + /* Commit selectors, pins and EAPD. */ + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL) + continue; + if (w->selconn == -1) + w->selconn = 0; + if (w->nconns > 0) + hdaa_widget_connection_select(w, w->selconn); + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->waspin) { + hda_command(devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, w->nid, + w->wclass.pin.ctrl)); + } + if (w->param.eapdbtl != HDA_INVALID) { + uint32_t val; + + val = w->param.eapdbtl; + if (devinfo->quirks & + HDAA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hda_command(devinfo->dev, + HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid, + val)); + } + } + + hdaa_gpio_commit(devinfo); + hdaa_gpo_commit(devinfo); +} + +static void +hdaa_powerup(struct hdaa_devinfo *devinfo) +{ + int i; + + hda_command(devinfo->dev, + HDA_CMD_SET_POWER_STATE(0, + devinfo->nid, HDA_CMD_POWER_STATE_D0)); + DELAY(100); + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + hda_command(devinfo->dev, + HDA_CMD_SET_POWER_STATE(0, + i, HDA_CMD_POWER_STATE_D0)); + } + DELAY(1000); +} + +static int +hdaa_pcmchannel_setup(struct hdaa_chan *ch) +{ + struct hdaa_devinfo *devinfo = ch->devinfo; + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + uint32_t cap, fmtcap, pcmcap; + int i, j, ret, channels, onlystereo; + uint16_t pinset; + + ch->caps = hdaa_caps; + ch->caps.fmtlist = ch->fmtlist; + ch->bit16 = 1; + ch->bit32 = 0; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + + ret = 0; + channels = 0; + onlystereo = 1; + pinset = 0; + fmtcap = devinfo->supp_stream_formats; + pcmcap = devinfo->supp_pcm_size_rate; + + for (i = 0; i < 16; i++) { + /* Check as is correct */ + if (ch->as < 0) + break; + /* Cound only present DACs */ + if (as[ch->as].dacs[ch->asindex][i] <= 0) + continue; + /* Ignore duplicates */ + for (j = 0; j < ret; j++) { + if (ch->io[j] == as[ch->as].dacs[ch->asindex][i]) + break; + } + if (j < ret) + continue; + + w = hdaa_widget_get(devinfo, as[ch->as].dacs[ch->asindex][i]); + if (w == NULL || w->enable == 0) + continue; + cap = w->param.supp_stream_formats; + if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) && + !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) + continue; + /* Many CODECs does not declare AC3 support on SPDIF. + I don't beleave that they doesn't support it! */ + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK; + if (ret == 0) { + fmtcap = cap; + pcmcap = w->param.supp_pcm_size_rate; + } else { + fmtcap &= cap; + pcmcap &= w->param.supp_pcm_size_rate; + } + ch->io[ret++] = as[ch->as].dacs[ch->asindex][i]; + /* Do not count redirection pin/dac channels. */ + if (i == 15 && as[ch->as].hpredir >= 0) + continue; + channels += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; + if (HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) != 1) + onlystereo = 0; + pinset |= (1 << i); + } + ch->io[ret] = -1; + ch->channels = channels; + + if (as[ch->as].fakeredir) + ret--; + /* Standard speaks only about stereo pins and playback, ... */ + if ((!onlystereo) || as[ch->as].mixed) + pinset = 0; + /* ..., but there it gives us info about speakers layout. */ + as[ch->as].pinset = pinset; + + ch->supp_stream_formats = fmtcap; + ch->supp_pcm_size_rate = pcmcap; + + /* + * 8bit = 0 + * 16bit = 1 + * 20bit = 2 + * 24bit = 3 + * 32bit = 4 + */ + if (ret > 0) { + i = 0; + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) { + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap)) + ch->bit16 = 1; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap)) + ch->bit16 = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) + ch->bit32 = 4; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) + ch->bit32 = 3; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) + ch->bit32 = 2; + if (!(devinfo->quirks & HDAA_QUIRK_FORCESTEREO)) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 1, 0); + } + if (channels >= 2) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0); + } + if (channels == 4 || /* Any 4-channel */ + pinset == 0x0007 || /* 5.1 */ + pinset == 0x0013 || /* 5.1 */ + pinset == 0x0017) { /* 7.1 */ + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0); + } + if (channels == 6 || /* Any 6-channel */ + pinset == 0x0017) { /* 7.1 */ + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1); + } + if (channels == 8) { /* Any 8-channel */ + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1); + } + } + if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); + } + ch->fmtlist[i] = 0; + i = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap)) + ch->pcmrates[i++] = 8000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap)) + ch->pcmrates[i++] = 11025; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap)) + ch->pcmrates[i++] = 16000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap)) + ch->pcmrates[i++] = 22050; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap)) + ch->pcmrates[i++] = 32000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap)) + ch->pcmrates[i++] = 44100; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */ + ch->pcmrates[i++] = 48000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap)) + ch->pcmrates[i++] = 88200; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap)) + ch->pcmrates[i++] = 96000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap)) + ch->pcmrates[i++] = 176400; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap)) + ch->pcmrates[i++] = 192000; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */ + ch->pcmrates[i] = 0; + if (i > 0) { + ch->caps.minspeed = ch->pcmrates[0]; + ch->caps.maxspeed = ch->pcmrates[i - 1]; + } + } + + return (ret); +} + +static void +hdaa_create_pcms(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + int i, j, k, apdev = 0, ardev = 0, dpdev = 0, drdev = 0; + + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].enable == 0) + continue; + if (as[i].dir == HDAA_CTL_IN) { + if (as[i].digital) + drdev++; + else + ardev++; + } else { + if (as[i].digital) + dpdev++; + else + apdev++; + } + } + devinfo->num_devs = + max(ardev, apdev) + max(drdev, dpdev); + devinfo->devs = + (struct hdaa_pcm_devinfo *)malloc( + devinfo->num_devs * sizeof(struct hdaa_pcm_devinfo), + M_HDAA, M_ZERO | M_NOWAIT); + if (devinfo->devs == NULL) { + device_printf(devinfo->dev, + "Unable to allocate memory for devices\n"); + return; + } + for (i = 0; i < devinfo->num_devs; i++) { + devinfo->devs[i].index = i; + devinfo->devs[i].devinfo = devinfo; + devinfo->devs[i].playas = -1; + devinfo->devs[i].recas = -1; + devinfo->devs[i].digital = 255; + } + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].enable == 0) + continue; + for (j = 0; j < devinfo->num_devs; j++) { + if (devinfo->devs[j].digital != 255 && + (!devinfo->devs[j].digital) != + (!as[i].digital)) + continue; + if (as[i].dir == HDAA_CTL_IN) { + if (devinfo->devs[j].recas >= 0) + continue; + devinfo->devs[j].recas = i; + } else { + if (devinfo->devs[j].playas >= 0) + continue; + devinfo->devs[j].playas = i; + } + for (k = 0; k < as[i].num_chans; k++) { + devinfo->chans[as[i].chans[k]].pdevinfo = + &devinfo->devs[j]; + } + devinfo->devs[j].digital = as[i].digital; + break; + } + } + for (i = 0; i < devinfo->num_devs; i++) { + struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i]; + + pdevinfo->dev = device_add_child(devinfo->dev, "pcm", -1); + device_set_ivars(pdevinfo->dev, (void *)pdevinfo); + } +} + +static void +hdaa_dump_ctls(struct hdaa_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_audio_ctl *ctl; + char buf[64]; + int i, j, printed; + + if (flag == 0) { + flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM | + SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | + SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | + SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR); + } + + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if ((flag & (1 << j)) == 0) + continue; + i = 0; + printed = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || + ctl->widget->enable == 0) + continue; + if (!((pdevinfo->playas >= 0 && + ctl->widget->bindas == pdevinfo->playas) || + (pdevinfo->recas >= 0 && + ctl->widget->bindas == pdevinfo->recas) || + (ctl->widget->bindas == -2 && pdevinfo->index == 0))) + continue; + if ((ctl->ossmask & (1 << j)) == 0) + continue; + + if (printed == 0) { + device_printf(pdevinfo->dev, "\n"); + if (banner != NULL) { + device_printf(pdevinfo->dev, "%s", banner); + } else { + device_printf(pdevinfo->dev, "Unknown Ctl"); + } + printf(" (OSS: %s)\n", + hdaa_audio_ctl_ossmixer_mask2allname(1 << j, + buf, sizeof(buf))); + device_printf(pdevinfo->dev, " |\n"); + printed = 1; + } + device_printf(pdevinfo->dev, " +- ctl %2d (nid %3d %s", i, + ctl->widget->nid, + (ctl->ndir == HDAA_CTL_IN)?"in ":"out"); + if (ctl->ndir == HDAA_CTL_IN && ctl->ndir == ctl->dir) + printf(" %2d): ", ctl->index); + else + printf("): "); + if (ctl->step > 0) { + printf("%+d/%+ddB (%d steps)%s\n", + (0 - ctl->offset) * (ctl->size + 1) / 4, + (ctl->step - ctl->offset) * (ctl->size + 1) / 4, + ctl->step + 1, + ctl->mute?" + mute":""); + } else + printf("%s\n", ctl->mute?"mute":""); + } + } +} + +static void +hdaa_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap) +{ + uint32_t cap; + + cap = fcap; + if (cap != 0) { + device_printf(dev, " Stream cap: 0x%08x\n", cap); + device_printf(dev, " "); + 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(dev, " PCM cap: 0x%08x\n", cap); + device_printf(dev, " "); + 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(" bits,"); + 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(" KHz\n"); + } +} + +static void +hdaa_dump_pin(struct hdaa_widget *w) +{ + uint32_t pincap; + + pincap = w->wclass.pin.cap; + + device_printf(w->devinfo->dev, " Pin cap: 0x%08x\n", pincap); + device_printf(w->devinfo->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_HDMI(pincap)) + printf(" HDMI"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { + printf(" VREF["); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + printf(" 50"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + printf(" 80"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + printf(" 100"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) + printf(" GROUND"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) + printf(" HIZ"); + printf(" ]"); + } + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) + printf(" EAPD"); + if (HDA_PARAM_PIN_CAP_DP(pincap)) + printf(" DP"); + if (HDA_PARAM_PIN_CAP_HBR(pincap)) + printf(" HBR"); + printf("\n"); + device_printf(w->devinfo->dev, " Pin config: 0x%08x\n", + w->wclass.pin.config); + device_printf(w->devinfo->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"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) + printf(" VREFs"); + printf("\n"); +} + +static void +hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf) +{ + + device_printf(w->devinfo->dev, "%2d %08x %-2d %-2d " + "%-13s %-5s %-7s %-10s %-7s %d%s\n", + w->nid, conf, + HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), + HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), + HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], + HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], + HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)], + HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)], + HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], + HDA_CONFIG_DEFAULTCONF_MISC(conf), + (w->enable == 0)?" DISA":""); +} + +static void +hdaa_dump_pin_configs(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + device_printf(devinfo->dev, "nid 0x as seq " + "device conn jack loc color misc\n"); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hdaa_dump_pin_config(w, w->wclass.pin.config); + } +} + +static void +hdaa_dump_amp(device_t dev, uint32_t cap, char *banner) +{ + device_printf(dev, " %s amp: 0x%08x\n", banner, cap); + device_printf(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 +hdaa_dump_nodes(struct hdaa_devinfo *devinfo) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + struct hdaa_widget *w, *cw; + char buf[64]; + int i, j; + + device_printf(devinfo->dev, "\n"); + device_printf(devinfo->dev, "Default Parameter\n"); + device_printf(devinfo->dev, "-----------------\n"); + hdaa_dump_audio_formats(devinfo->dev, + devinfo->supp_stream_formats, + devinfo->supp_pcm_size_rate); + device_printf(devinfo->dev, " IN amp: 0x%08x\n", + devinfo->inamp_cap); + device_printf(devinfo->dev, " OUT amp: 0x%08x\n", + devinfo->outamp_cap); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) { + device_printf(devinfo->dev, "Ghost widget nid=%d\n", i); + continue; + } + device_printf(devinfo->dev, "\n"); + device_printf(devinfo->dev, " nid: %d%s\n", w->nid, + (w->enable == 0) ? " [DISABLED]" : ""); + device_printf(devinfo->dev, " Name: %s\n", w->name); + device_printf(devinfo->dev, " Widget cap: 0x%08x\n", + w->param.widget_cap); + if (w->param.widget_cap & 0x0ee1) { + device_printf(devinfo->dev, " "); + if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap)) + printf(" LRSWAP"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap)) + printf(" PWR"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + printf(" DIGITAL"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) + printf(" UNSOL"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) + printf(" PROC"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) + printf(" STRIPE"); + j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); + if (j == 1) + printf(" STEREO"); + else if (j > 1) + printf(" %dCH", j + 1); + printf("\n"); + } + if (w->bindas != -1) { + device_printf(devinfo->dev, " Association: %d (0x%08x)\n", + w->bindas, w->bindseqmask); + } + if (w->ossmask != 0 || w->ossdev >= 0) { + device_printf(devinfo->dev, " OSS: %s", + hdaa_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); + if (w->ossdev >= 0) + printf(" (%s)", ossname[w->ossdev]); + printf("\n"); + } + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdaa_dump_audio_formats(devinfo->dev, + w->param.supp_stream_formats, + w->param.supp_pcm_size_rate); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin) + hdaa_dump_pin(w); + if (w->param.eapdbtl != HDA_INVALID) + device_printf(devinfo->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) + hdaa_dump_amp(devinfo->dev, w->param.outamp_cap, "Output"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && + w->param.inamp_cap != 0) + hdaa_dump_amp(devinfo->dev, w->param.inamp_cap, " Input"); + if (w->nconns > 0) { + device_printf(devinfo->dev, " connections: %d\n", w->nconns); + device_printf(devinfo->dev, " |\n"); + } + for (j = 0; j < w->nconns; j++) { + cw = hdaa_widget_get(devinfo, w->conns[j]); + device_printf(devinfo->dev, " + %s<- nid=%d [%s]", + (w->connsenable[j] == 0)?"[DISABLED] ":"", + 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 void +hdaa_dump_dst_nid(struct hdaa_pcm_devinfo *pdevinfo, nid_t nid, int depth) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *cw; + char buf[64]; + int i, printed = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return; + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return; + + if (depth == 0) + device_printf(pdevinfo->dev, "%*s", 4, ""); + else + device_printf(pdevinfo->dev, "%*s + <- ", 4 + (depth - 1) * 7, ""); + printf("nid=%d [%s]", w->nid, w->name); + + if (depth > 0) { + if (w->ossmask == 0) { + printf("\n"); + return; + } + printf(" [src: %s]", + hdaa_audio_ctl_ossmixer_mask2allname( + w->ossmask, buf, sizeof(buf))); + if (w->ossdev >= 0) { + printf("\n"); + return; + } + } + printf("\n"); + + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->bindas == -1) + continue; + if (printed == 0) { + device_printf(pdevinfo->dev, "%*s |\n", 4 + (depth) * 7, ""); + printed = 1; + } + hdaa_dump_dst_nid(pdevinfo, w->conns[i], depth + 1); + } + +} + +static void +hdaa_dump_dac(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_audio_as *as; + struct hdaa_widget *w; + int i, printed = 0; + + if (pdevinfo->playas < 0) + return; + + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + w = hdaa_widget_get(devinfo, as->pins[i]); + if (w == NULL || w->enable == 0) + continue; + if (printed == 0) { + printed = 1; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Playback:\n"); + } + device_printf(pdevinfo->dev, "\n"); + hdaa_dump_dst_nid(pdevinfo, as->pins[i], 0); + } +} + +static void +hdaa_dump_adc(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + int i; + int printed = 0; + + if (pdevinfo->recas < 0) + return; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_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->bindas != pdevinfo->recas) + continue; + if (printed == 0) { + printed = 1; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Record:\n"); + } + device_printf(pdevinfo->dev, "\n"); + hdaa_dump_dst_nid(pdevinfo, i, 0); + } +} + +static void +hdaa_dump_mix(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + int i; + int printed = 0; + + if (pdevinfo->index != 0) + return; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev != SOUND_MIXER_IMIX) + continue; + if (printed == 0) { + printed = 1; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Input Mix:\n"); + } + device_printf(pdevinfo->dev, "\n"); + hdaa_dump_dst_nid(pdevinfo, i, 0); + } +} + +static void +hdaa_dump_pcmchannels(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + nid_t *nids; + int chid, i; + + if (pdevinfo->playas >= 0) { + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Playback:\n"); + device_printf(pdevinfo->dev, "\n"); + chid = devinfo->as[pdevinfo->playas].chans[0]; + hdaa_dump_audio_formats(pdevinfo->dev, + devinfo->chans[chid].supp_stream_formats, + devinfo->chans[chid].supp_pcm_size_rate); + for (i = 0; i < devinfo->as[pdevinfo->playas].num_chans; i++) { + chid = devinfo->as[pdevinfo->playas].chans[i]; + device_printf(pdevinfo->dev, " DAC:"); + for (nids = devinfo->chans[chid].io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } + } + if (pdevinfo->recas >= 0) { + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Record:\n"); + device_printf(pdevinfo->dev, "\n"); + chid = devinfo->as[pdevinfo->recas].chans[0]; + hdaa_dump_audio_formats(pdevinfo->dev, + devinfo->chans[chid].supp_stream_formats, + devinfo->chans[chid].supp_pcm_size_rate); + for (i = 0; i < devinfo->as[pdevinfo->recas].num_chans; i++) { + chid = devinfo->as[pdevinfo->recas].chans[i]; + device_printf(pdevinfo->dev, " DAC:"); + for (nids = devinfo->chans[chid].io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } + } +} + +static void +hdaa_pindump(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_widget *w; + uint32_t res, pincap, delay; + int i; + + device_printf(dev, "Dumping AFG pins:\n"); + device_printf(dev, "nid 0x as seq " + "device conn jack loc color misc\n"); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hdaa_dump_pin_config(w, w->wclass.pin.config); + pincap = w->wclass.pin.cap; + device_printf(dev, " Caps: %2s %3s %2s %4s %4s", + HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"", + HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"", + HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"", + HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"", + HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":""); + if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || + HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) { + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { + delay = 0; + hda_command(dev, + HDA_CMD_SET_PIN_SENSE(0, w->nid, 0)); + do { + res = hda_command(dev, + HDA_CMD_GET_PIN_SENSE(0, w->nid)); + if (res != 0x7fffffff && res != 0xffffffff) + break; + DELAY(10); + } while (++delay < 10000); + } else { + delay = 0; + res = hda_command(dev, HDA_CMD_GET_PIN_SENSE(0, + w->nid)); + } + printf(" Sense: 0x%08x (%sconnected)", res, + (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ? + "" : "dis"); + if (delay > 0) + printf(" delay %dus", delay * 10); + } + printf("\n"); + } + device_printf(dev, + "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap)); + hdaa_dump_gpi(devinfo); + hdaa_dump_gpio(devinfo); + hdaa_dump_gpo(devinfo); +} + +static void +hdaa_configure(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_audio_ctl *ctl; + int i; + + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying built-in patches...\n"); + ); + hdaa_patch(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying local patches...\n"); + ); + hdaa_local_patch(devinfo); + hdaa_audio_postprocess(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Parsing Ctls...\n"); + ); + hdaa_audio_ctl_parse(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling nonaudio...\n"); + ); + hdaa_audio_disable_nonaudio(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling useless...\n"); + ); + hdaa_audio_disable_useless(devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, "Patched pins configuration:\n"); + hdaa_dump_pin_configs(devinfo); + ); + HDA_BOOTHVERBOSE( + device_printf(dev, "Parsing pin associations...\n"); + ); + hdaa_audio_as_parse(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Building AFG tree...\n"); + ); + hdaa_audio_build_tree(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling unassociated " + "widgets...\n"); + ); + hdaa_audio_disable_unas(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling nonselected " + "inputs...\n"); + ); + hdaa_audio_disable_notselected(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling useless...\n"); + ); + hdaa_audio_disable_useless(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling " + "crossassociatement connections...\n"); + ); + hdaa_audio_disable_crossas(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling useless...\n"); + ); + hdaa_audio_disable_useless(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Binding associations to channels...\n"); + ); + hdaa_audio_bind_as(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Assigning names to signal sources...\n"); + ); + hdaa_audio_assign_names(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Assigning mixers to the tree...\n"); + ); + hdaa_audio_assign_mixers(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Preparing pin controls...\n"); + ); + hdaa_audio_prepare_pin_ctrl(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "AFG commit...\n"); + ); + hdaa_audio_commit(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying direct built-in patches...\n"); + ); + hdaa_patch_direct(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch init...\n"); + ); + hdaa_hp_switch_init(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Creating PCM devices...\n"); + ); + hdaa_create_pcms(devinfo); + + HDA_BOOTVERBOSE( + if (devinfo->quirks != 0) { + device_printf(dev, "FG config/quirks:"); + for (i = 0; i < HDAA_QUIRKS_TAB_LEN; i++) { + if ((devinfo->quirks & + hdaa_quirks_tab[i].value) == + hdaa_quirks_tab[i].value) + printf(" %s", hdaa_quirks_tab[i].key); + } + printf("\n"); + } + + device_printf(dev, "\n"); + device_printf(dev, "+-------------------+\n"); + device_printf(dev, "| DUMPING HDA NODES |\n"); + device_printf(dev, "+-------------------+\n"); + hdaa_dump_nodes(devinfo); + ); + + HDA_BOOTHVERBOSE( + device_printf(dev, "\n"); + device_printf(dev, "+------------------------+\n"); + device_printf(dev, "| DUMPING HDA AMPLIFIERS |\n"); + device_printf(dev, "+------------------------+\n"); + device_printf(dev, "\n"); + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + device_printf(dev, "%3d: nid %3d %s (%s) index %d", i, + (ctl->widget != NULL) ? ctl->widget->nid : -1, + (ctl->ndir == HDAA_CTL_IN)?"in ":"out", + (ctl->dir == HDAA_CTL_IN)?"in ":"out", + ctl->index); + if (ctl->childwidget != NULL) + printf(" cnid %3d", ctl->childwidget->nid); + else + printf(" "); + printf(" ossmask=0x%08x\n", + ctl->ossmask); + device_printf(dev, + " mute: %d step: %3d size: %3d off: %3d%s\n", + ctl->mute, ctl->step, ctl->size, ctl->offset, + (ctl->enable == 0) ? " [DISABLED]" : + ((ctl->ossmask == 0) ? " [UNUSED]" : "")); + } + ); + + HDA_BOOTVERBOSE( + device_printf(dev, "\n"); + ); +} + +static void +hdaa_unconfigure(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_widget *w; + int i, j; + + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch deinit...\n"); + ); + hdaa_hp_switch_deinit(devinfo); + free(devinfo->ctl, M_HDAA); + devinfo->ctl = NULL; + devinfo->ctlcnt = 0; + free(devinfo->as, M_HDAA); + devinfo->as = NULL; + devinfo->ascnt = 0; + free(devinfo->devs, M_HDAA); + devinfo->devs = NULL; + devinfo->num_devs = 0; + free(devinfo->chans, M_HDAA); + devinfo->chans = NULL; + devinfo->num_chans = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + w->enable = 1; + w->selconn = -1; + w->pflags = 0; + w->bindas = -1; + w->bindseqmask = 0; + w->ossdev = -1; + w->ossmask = 0; + for (j = 0; j < w->nconns; j++) + w->connsenable[j] = 1; + w->wclass.pin.config = w->wclass.pin.newconf; + } +} + +static int +hdaa_sysctl_gpi_state(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + device_t dev = devinfo->dev; + char buf[256]; + int n = 0, i, numgpi; + uint32_t data = 0; + + buf[0] = 0; + hdaa_lock(devinfo); + numgpi = HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap); + if (numgpi > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPI_DATA(0, devinfo->nid)); + } + hdaa_unlock(devinfo); + for (i = 0; i < numgpi; i++) { + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%d", + n != 0 ? " " : "", i, ((data >> i) & 1)); + } + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +hdaa_sysctl_gpio_state(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + device_t dev = devinfo->dev; + char buf[256]; + int n = 0, i, numgpio; + uint32_t data = 0, enable = 0, dir = 0; + + buf[0] = 0; + hdaa_lock(devinfo); + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); + if (numgpio > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPIO_DATA(0, devinfo->nid)); + enable = hda_command(dev, + HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid)); + dir = hda_command(dev, + HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid)); + } + hdaa_unlock(devinfo); + for (i = 0; i < numgpio; i++) { + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=", + n != 0 ? " " : "", i); + if ((enable & (1 << i)) == 0) { + n += snprintf(buf + n, sizeof(buf) - n, "disabled"); + continue; + } + n += snprintf(buf + n, sizeof(buf) - n, "%sput(%d)", + ((dir >> i) & 1) ? "out" : "in", ((data >> i) & 1)); + } + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +hdaa_sysctl_gpio_config(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + char buf[256]; + int error, n = 0, i, numgpio; + uint32_t gpio, x; + + gpio = devinfo->newgpio; + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); + buf[0] = 0; + for (i = 0; i < numgpio; i++) { + x = (gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i); + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%s", + n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]); + } + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + gpio = strtol(buf + 2, NULL, 16); + else + gpio = hdaa_gpio_patch(gpio, buf); + hdaa_lock(devinfo); + devinfo->newgpio = devinfo->gpio = gpio; + hdaa_gpio_commit(devinfo); + hdaa_unlock(devinfo); + return (0); +} + +static int +hdaa_sysctl_gpo_state(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + device_t dev = devinfo->dev; + char buf[256]; + int n = 0, i, numgpo; + uint32_t data = 0; + + buf[0] = 0; + hdaa_lock(devinfo); + numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); + if (numgpo > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPO_DATA(0, devinfo->nid)); + } + hdaa_unlock(devinfo); + for (i = 0; i < numgpo; i++) { + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%d", + n != 0 ? " " : "", i, ((data >> i) & 1)); + } + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +hdaa_sysctl_gpo_config(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + char buf[256]; + int error, n = 0, i, numgpo; + uint32_t gpo, x; + + gpo = devinfo->newgpo; + numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); + buf[0] = 0; + for (i = 0; i < numgpo; i++) { + x = (gpo & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i); + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%s", + n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]); + } + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + gpo = strtol(buf + 2, NULL, 16); + else + gpo = hdaa_gpio_patch(gpo, buf); + hdaa_lock(devinfo); + devinfo->newgpo = devinfo->gpo = gpo; + hdaa_gpo_commit(devinfo); + hdaa_unlock(devinfo); + return (0); +} + +static int +hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS) +{ + device_t dev; + struct hdaa_devinfo *devinfo; + int error, val; + + dev = oidp->oid_arg1; + devinfo = device_get_softc(dev); + if (devinfo == NULL) + return (EINVAL); + val = 0; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL || val == 0) + return (error); + + HDA_BOOTHVERBOSE( + device_printf(dev, "Reconfiguration...\n"); + ); + if ((error = device_delete_children(dev)) != 0) + return (error); + hdaa_lock(devinfo); + hdaa_unconfigure(dev); + hdaa_configure(dev); + hdaa_unlock(devinfo); + bus_generic_attach(dev); + HDA_BOOTHVERBOSE( + device_printf(dev, "Reconfiguration done\n"); + ); + return (0); +} + +static int +hdaa_suspend(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int i; + + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend...\n"); + ); + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Stop streams...\n"); + ); + for (i = 0; i < devinfo->num_chans; i++) { + if (devinfo->chans[i].flags & HDAA_CHN_RUNNING) { + devinfo->chans[i].flags |= HDAA_CHN_SUSPEND; + hdaa_channel_stop(&devinfo->chans[i]); + } + } + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch deinit...\n"); + ); + hdaa_hp_switch_deinit(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Power down FG" + " nid=%d to the D3 state...\n", + devinfo->nid); + ); + hda_command(devinfo->dev, + HDA_CMD_SET_POWER_STATE(0, + devinfo->nid, HDA_CMD_POWER_STATE_D3)); + callout_stop(&devinfo->poll_jack); + hdaa_unlock(devinfo); + callout_drain(&devinfo->poll_jack); + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend done\n"); + ); + return (0); +} + +static int +hdaa_resume(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int i; + + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume...\n"); + ); + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Power up audio FG nid=%d...\n", + devinfo->nid); + ); + hdaa_powerup(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "AFG commit...\n"); + ); + hdaa_audio_commit(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying direct built-in patches...\n"); + ); + hdaa_patch_direct(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch init...\n"); + ); + hdaa_hp_switch_init(devinfo); + + hdaa_unlock(devinfo); + for (i = 0; i < devinfo->num_devs; i++) { + struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i]; + HDA_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "OSS mixer reinitialization...\n"); + ); + if (mixer_reinit(pdevinfo->dev) == -1) + device_printf(pdevinfo->dev, + "unable to reinitialize the mixer\n"); + } + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Start streams...\n"); + ); + for (i = 0; i < devinfo->num_chans; i++) { + if (devinfo->chans[i].flags & HDAA_CHN_SUSPEND) { + devinfo->chans[i].flags &= ~HDAA_CHN_SUSPEND; + hdaa_channel_start(&devinfo->chans[i]); + } + } + hdaa_unlock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume done\n"); + ); + return (0); +} + +static int +hdaa_probe(device_t dev) +{ + char buf[128]; + + if (hda_get_node_type(dev) != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) + return (ENXIO); + snprintf(buf, sizeof(buf), "%s Audio Function Group", + device_get_desc(device_get_parent(dev))); + device_set_desc_copy(dev, buf); + return (BUS_PROBE_DEFAULT); +} + +static int +hdaa_attach(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + uint32_t res; + nid_t nid = hda_get_node_id(dev); + + devinfo->dev = dev; + devinfo->lock = HDAC_GET_MTX(device_get_parent(dev), dev); + devinfo->nid = nid; + devinfo->newquirks = -1; + devinfo->newgpio = -1; + devinfo->newgpo = -1; + callout_init(&devinfo->poll_jack, CALLOUT_MPSAFE); + devinfo->poll_ival = hz; + + hdaa_lock(devinfo); + res = hda_command(dev, + HDA_CMD_GET_PARAMETER(0 , nid, HDA_PARAM_SUB_NODE_COUNT)); + hdaa_unlock(devinfo); + + 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(dev, + "Audio Function Group at nid=%d: %d subnodes %d-%d\n", + nid, devinfo->nodecnt, + devinfo->startnode, devinfo->endnode - 1); + ); + + if (devinfo->nodecnt > 0) + devinfo->widget = (struct hdaa_widget *)malloc( + sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAA, + M_WAITOK | M_ZERO); + else + devinfo->widget = NULL; + + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Powering up...\n"); + ); + hdaa_powerup(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Parsing audio FG...\n"); + ); + hdaa_audio_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, "Original pins configuration:\n"); + hdaa_dump_pin_configs(devinfo); + ); + hdaa_configure(dev); + hdaa_unlock(devinfo); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + &devinfo->newquirks, sizeof(&devinfo->newquirks), + hdaa_sysctl_quirks, "A", "Configuration options"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpi_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpi_state, "A", "GPI state"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpio_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpio_state, "A", "GPIO state"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpio_config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpio_config, "A", "GPIO configuration"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpo_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpo_state, "A", "GPO state"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpo_config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpo_config, "A", "GPO configuration"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "reconfig", CTLTYPE_INT | CTLFLAG_RW, + dev, sizeof(dev), + hdaa_sysctl_reconfig, "I", "Reprocess configuration"); + bus_generic_attach(dev); + return (0); +} + +static int +hdaa_detach(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int error; + + if ((error = device_delete_children(dev)) != 0) + return (error); + + hdaa_lock(devinfo); + hdaa_unconfigure(dev); + devinfo->poll_ival = 0; + callout_stop(&devinfo->poll_jack); + hdaa_unlock(devinfo); + callout_drain(&devinfo->poll_jack); + + free(devinfo->widget, M_HDAA); + return (0); +} + +static int +hdaa_print_child(device_t dev, device_t child) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(child); + struct hdaa_audio_as *as; + int retval, first = 1, i; + + retval = bus_print_child_header(dev, child); + retval += printf(" at nid "); + if (pdevinfo->playas >= 0) { + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + retval += printf("%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + if (pdevinfo->recas >= 0) { + if (pdevinfo->playas >= 0) { + retval += printf(" and "); + first = 1; + } + as = &devinfo->as[pdevinfo->recas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + retval += printf("%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static int +hdaa_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(child); + struct hdaa_audio_as *as; + int first = 1, i, len = 0; + + len += snprintf(buf + len, buflen - len, "nid="); + if (pdevinfo->playas >= 0) { + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + len += snprintf(buf + len, buflen - len, + "%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + if (pdevinfo->recas >= 0) { + as = &devinfo->as[pdevinfo->recas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + len += snprintf(buf + len, buflen - len, + "%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + return (0); +} + +static void +hdaa_stream_intr(device_t dev, int dir, int stream) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_chan *ch; + int i; + + for (i = 0; i < devinfo->num_chans; i++) { + ch = &devinfo->chans[i]; + if (!(ch->flags & HDAA_CHN_RUNNING)) + continue; + if (ch->dir == ((dir == 1) ? PCMDIR_PLAY : PCMDIR_REC) && + ch->sid == stream) { + hdaa_unlock(devinfo); + chn_intr(ch->c); + hdaa_lock(devinfo); + } + } +} + +static void +hdaa_unsol_intr(device_t dev, uint32_t resp) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int i, tag; + + tag = resp >> 26; + for (i = 0; i < devinfo->ascnt; i++) { + if (devinfo->as[i].unsol == tag) + hdaa_hp_switch_handler(devinfo, i); + } +} + +static device_method_t hdaa_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdaa_probe), + DEVMETHOD(device_attach, hdaa_attach), + DEVMETHOD(device_detach, hdaa_detach), + DEVMETHOD(device_suspend, hdaa_suspend), + DEVMETHOD(device_resume, hdaa_resume), + /* Bus interface */ + DEVMETHOD(bus_print_child, hdaa_print_child), + DEVMETHOD(bus_child_location_str, hdaa_child_location_str), + DEVMETHOD(hdac_stream_intr, hdaa_stream_intr), + DEVMETHOD(hdac_unsol_intr, hdaa_unsol_intr), + DEVMETHOD(hdac_pindump, hdaa_pindump), + { 0, 0 } +}; + +static driver_t hdaa_driver = { + "hdaa", + hdaa_methods, + sizeof(struct hdaa_devinfo), +}; + +static devclass_t hdaa_devclass; + +DRIVER_MODULE(snd_hda, hdacc, hdaa_driver, hdaa_devclass, 0, 0); + +static void +hdaa_chan_formula(struct hdaa_devinfo *devinfo, int asid, + char *buf, int buflen) +{ + struct hdaa_audio_as *as; + int c; + + as = &devinfo->as[asid]; + c = devinfo->chans[as->chans[0]].channels; + if (c == 1) + snprintf(buf, buflen, "mono"); + else if (c == 2) + buf[0] = 0; + else if (as->pinset == 0x0003) + snprintf(buf, buflen, "3.1"); + else if (as->pinset == 0x0005 || as->pinset == 0x0011) + snprintf(buf, buflen, "4.0"); + else if (as->pinset == 0x0007 || as->pinset == 0x0013) + snprintf(buf, buflen, "5.1"); + else if (as->pinset == 0x0017) + snprintf(buf, buflen, "7.1"); + else + snprintf(buf, buflen, "%dch", c); +} + +static int +hdaa_pcm_probe(device_t dev) +{ + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(dev); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + char chans1[8], chans2[8]; + char buf[128]; + int loc1, loc2; + + if (pdevinfo->playas >= 0) + loc1 = devinfo->as[pdevinfo->playas].location; + else + loc1 = devinfo->as[pdevinfo->recas].location; + if (pdevinfo->recas >= 0) + loc2 = devinfo->as[pdevinfo->recas].location; + else + loc2 = loc1; + if (loc1 != loc2) + loc1 = -2; + if (loc1 >= 0 && HDA_LOCS[loc1][0] == '0') + loc1 = -2; + chans1[0] = 0; + chans2[0] = 0; + if (pdevinfo->playas >= 0) + hdaa_chan_formula(devinfo, pdevinfo->playas, + chans1, sizeof(chans1)); + if (pdevinfo->recas >= 0) + hdaa_chan_formula(devinfo, pdevinfo->recas, + chans2, sizeof(chans2)); + if (chans1[0] != 0 || chans2[0] != 0) { + if (chans1[0] == 0 && pdevinfo->playas >= 0) + snprintf(chans1, sizeof(chans1), "2.0"); + else if (chans2[0] == 0 && pdevinfo->recas >= 0) + snprintf(chans2, sizeof(chans2), "2.0"); + if (strcmp(chans1, chans2) == 0) + chans2[0] = 0; + } + snprintf(buf, sizeof(buf), "%s PCM (%s%s%s%s%s%s%s)", + device_get_desc(device_get_parent(device_get_parent(dev))), + loc1 >= 0 ? HDA_LOCS[loc1] : "", loc1 >= 0 ? " " : "", + (pdevinfo->digital == 3)?"DisplayPort": + ((pdevinfo->digital == 2)?"HDMI": + ((pdevinfo->digital)?"Digital":"Analog")), + chans1[0] ? " " : "", chans1, + chans2[0] ? "/" : "", chans2); + device_set_desc_copy(dev, buf); + return (BUS_PROBE_SPECIFIC); +} + +static int +hdaa_pcm_attach(device_t dev) +{ + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(dev); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_audio_as *as; + char status[SND_STATUSLEN]; + int i; + + pdevinfo->chan_size = pcm_getbuffersize(dev, + HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); + + HDA_BOOTVERBOSE( + device_printf(dev, "+--------------------------------------+\n"); + device_printf(dev, "| DUMPING PCM Playback/Record Channels |\n"); + device_printf(dev, "+--------------------------------------+\n"); + hdaa_dump_pcmchannels(pdevinfo); + device_printf(dev, "\n"); + device_printf(dev, "+-------------------------------+\n"); + device_printf(dev, "| DUMPING Playback/Record Paths |\n"); + device_printf(dev, "+-------------------------------+\n"); + hdaa_dump_dac(pdevinfo); + hdaa_dump_adc(pdevinfo); + hdaa_dump_mix(pdevinfo); + device_printf(dev, "\n"); + device_printf(dev, "+-------------------------+\n"); + device_printf(dev, "| DUMPING Volume Controls |\n"); + device_printf(dev, "+-------------------------+\n"); + hdaa_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME); + hdaa_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM); + hdaa_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD); + hdaa_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC); + hdaa_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR); + hdaa_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE); + hdaa_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER); + hdaa_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV); + hdaa_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX); + hdaa_dump_ctls(pdevinfo, "Input Monitoring Level", SOUND_MASK_IGAIN); + hdaa_dump_ctls(pdevinfo, NULL, 0); + device_printf(dev, "\n"); + ); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { + i &= HDA_BLK_ALIGN; + if (i < HDA_BLK_MIN) + i = HDA_BLK_MIN; + pdevinfo->chan_blkcnt = pdevinfo->chan_size / i; + i = 0; + while (pdevinfo->chan_blkcnt >> i) + i++; + pdevinfo->chan_blkcnt = 1 << (i - 1); + if (pdevinfo->chan_blkcnt < HDA_BDL_MIN) + pdevinfo->chan_blkcnt = HDA_BDL_MIN; + else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX) + pdevinfo->chan_blkcnt = HDA_BDL_MAX; + } else + pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT; + + /* + * We don't register interrupt handler with snd_setup_intr + * in pcm device. Mark pcm device as MPSAFE manually. + */ + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + + HDA_BOOTHVERBOSE( + device_printf(dev, "OSS mixer initialization...\n"); + ); + if (mixer_init(dev, &hdaa_audio_ctl_ossmixer_class, pdevinfo) != 0) + device_printf(dev, "Can't register mixer\n"); + + HDA_BOOTHVERBOSE( + device_printf(dev, "Registering PCM channels...\n"); + ); + if (pcm_register(dev, pdevinfo, (pdevinfo->playas >= 0)?1:0, + (pdevinfo->recas >= 0)?1:0) != 0) + device_printf(dev, "Can't register PCM\n"); + + pdevinfo->registered++; + + if (pdevinfo->playas >= 0) { + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < as->num_chans; i++) + pcm_addchan(dev, PCMDIR_PLAY, &hdaa_channel_class, + &devinfo->chans[as->chans[i]]); + } + if (pdevinfo->recas >= 0) { + as = &devinfo->as[pdevinfo->recas]; + for (i = 0; i < as->num_chans; i++) + pcm_addchan(dev, PCMDIR_REC, &hdaa_channel_class, + &devinfo->chans[as->chans[i]]); + } + + snprintf(status, SND_STATUSLEN, "on %s %s", + device_get_nameunit(device_get_parent(dev)), + PCM_KLDSTRING(snd_hda)); + pcm_setstatus(dev, status); + + return (0); +} + +static int +hdaa_pcm_detach(device_t dev) +{ + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(dev); + int err; + + if (pdevinfo->registered > 0) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + } + + return (0); +} + +static device_method_t hdaa_pcm_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdaa_pcm_probe), + DEVMETHOD(device_attach, hdaa_pcm_attach), + DEVMETHOD(device_detach, hdaa_pcm_detach), + { 0, 0 } +}; + +static driver_t hdaa_pcm_driver = { + "pcm", + hdaa_pcm_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_hda_pcm, hdaa, hdaa_pcm_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_hda, 1); diff --git a/sys/dev/sound/pci/hda/hdaa.h b/sys/dev/sound/pci/hda/hdaa.h new file mode 100644 index 0000000..b56b6d7 --- /dev/null +++ b/sys/dev/sound/pci/hda/hdaa.h @@ -0,0 +1,230 @@ +/*-7 + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * 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$ + */ + +/* + * Intel High Definition Audio (Audio function quirks) driver for FreeBSD. + */ + +#ifndef _HDAA_QUIRKS_H_ +#define _HDAA_QUIRKS_H_ + +#define HDAA_GPIO_SHIFT(n) (n * 3) +#define HDAA_GPIO_MASK(n) (0x7 << (n * 3)) +#define HDAA_GPIO_KEEP(n) (0x0 << (n * 3)) +#define HDAA_GPIO_SET(n) (0x1 << (n * 3)) +#define HDAA_GPIO_CLEAR(n) (0x2 << (n * 3)) +#define HDAA_GPIO_DISABLE(n) (0x3 << (n * 3)) +#define HDAA_GPIO_INPUT(n) (0x4 << (n * 3)) + +/* 9 - 25 = anything else */ +#define HDAA_QUIRK_SOFTPCMVOL (1 << 9) +#define HDAA_QUIRK_FIXEDRATE (1 << 10) +#define HDAA_QUIRK_FORCESTEREO (1 << 11) +#define HDAA_QUIRK_EAPDINV (1 << 12) +#define HDAA_QUIRK_SENSEINV (1 << 14) + +/* 26 - 31 = vrefs */ +#define HDAA_QUIRK_IVREF50 (1 << 26) +#define HDAA_QUIRK_IVREF80 (1 << 27) +#define HDAA_QUIRK_IVREF100 (1 << 28) +#define HDAA_QUIRK_OVREF50 (1 << 29) +#define HDAA_QUIRK_OVREF80 (1 << 30) +#define HDAA_QUIRK_OVREF100 (1 << 31) + +#define HDAA_QUIRK_IVREF (HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF80 | \ + HDAA_QUIRK_IVREF100) +#define HDAA_QUIRK_OVREF (HDAA_QUIRK_OVREF50 | HDAA_QUIRK_OVREF80 | \ + HDAA_QUIRK_OVREF100) +#define HDAA_QUIRK_VREF (HDAA_QUIRK_IVREF | HDAA_QUIRK_OVREF) + +#define HDAA_AMP_VOL_DEFAULT (-1) +#define HDAA_AMP_MUTE_DEFAULT (0xffffffff) +#define HDAA_AMP_MUTE_NONE (0) +#define HDAA_AMP_MUTE_LEFT (1 << 0) +#define HDAA_AMP_MUTE_RIGHT (1 << 1) +#define HDAA_AMP_MUTE_ALL (HDAA_AMP_MUTE_LEFT | HDAA_AMP_MUTE_RIGHT) + +#define HDAA_AMP_LEFT_MUTED(v) ((v) & (HDAA_AMP_MUTE_LEFT)) +#define HDAA_AMP_RIGHT_MUTED(v) (((v) & HDAA_AMP_MUTE_RIGHT) >> 1) + +#define HDAA_ADC_MONITOR (1 << 0) + +#define HDAA_CTL_OUT 1 +#define HDAA_CTL_IN 2 + +#define HDA_MAX_CONNS 32 +#define HDA_MAX_NAMELEN 32 + +struct hdaa_widget { + nid_t nid; + int type; + int enable; + int nconns, selconn; + int waspin; + uint32_t pflags; + int bindas; + int bindseqmask; + int ossdev; + uint32_t ossmask; + nid_t conns[HDA_MAX_CONNS]; + u_char connsenable[HDA_MAX_CONNS]; + char name[HDA_MAX_NAMELEN]; + struct hdaa_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; + } param; + union { + struct { + uint32_t config; + uint32_t original; + uint32_t newconf; + uint32_t cap; + uint32_t ctrl; + } pin; + } wclass; +}; + +struct hdaa_audio_ctl { + struct hdaa_widget *widget, *childwidget; + int enable; + int index, dir, ndir; + int mute, step, size, offset; + int left, right, forcemute; + uint32_t muted; + uint32_t ossmask, possmask; +}; + +/* Association is a group of pins bound for some special function. */ +struct hdaa_audio_as { + u_char enable; + u_char index; + u_char dir; + u_char pincnt; + u_char fakeredir; + u_char digital; + uint16_t pinset; + nid_t hpredir; + nid_t pins[16]; + nid_t dacs[2][16]; + int num_chans; + int chans[2]; + int unsol; + int location; /* Pins location, if all have the same */ + int mixed; /* Mixed/multiplexed recording, not multichannel. */ +}; + +struct hdaa_pcm_devinfo { + device_t dev; + struct hdaa_devinfo *devinfo; + int index; + int registered; + int playas, recas; + u_char left[SOUND_MIXER_NRDEVICES]; + u_char right[SOUND_MIXER_NRDEVICES]; + int chan_size; + int chan_blkcnt; + u_char digital; +}; + +struct hdaa_devinfo { + device_t dev; + struct mtx *lock; + nid_t nid; + nid_t startnode, endnode; + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + uint32_t gpio_cap; + uint32_t quirks; + uint32_t newquirks; + uint32_t gpio; + uint32_t newgpio; + uint32_t gpo; + uint32_t newgpo; + int nodecnt; + int ctlcnt; + int ascnt; + int num_devs; + int num_chans; + struct hdaa_widget *widget; + struct hdaa_audio_ctl *ctl; + struct hdaa_audio_as *as; + struct hdaa_pcm_devinfo *devs; + struct hdaa_chan *chans; + struct callout poll_jack; + int poll_ival; +}; + +#define HDAA_CHN_RUNNING 0x00000001 +#define HDAA_CHN_SUSPEND 0x00000002 + +struct hdaa_chan { + struct snd_dbuf *b; + struct pcm_channel *c; + struct pcmchan_caps caps; + struct hdaa_devinfo *devinfo; + struct hdaa_pcm_devinfo *pdevinfo; + uint32_t spd, fmt, fmtlist[16], pcmrates[16]; + uint32_t supp_stream_formats, supp_pcm_size_rate; + uint32_t ptr, prevptr, blkcnt, blksz; + uint32_t *dmapos; + uint32_t flags; + int dir; + int off; + int sid; + int bit16, bit32; + int channels; /* Number of audio channels. */ + int as; /* Number of association. */ + int asindex; /* Index within association. */ + nid_t io[16]; +}; + +#define hdaa_codec_id(devinfo) \ + (((uint32_t)hda_get_vendor_id(devinfo->dev) << 16) + \ + hda_get_device_id(devinfo->dev)) + +#define hdaa_subvendor_id(devinfo) \ + (((uint32_t)hda_get_subvendor_id(devinfo->dev) << 16) + \ + hda_get_subdevice_id(devinfo->dev)) + +struct hdaa_widget *hdaa_widget_get(struct hdaa_devinfo *, nid_t); +uint32_t hdaa_widget_pin_patch(uint32_t config, const char *str); +uint32_t hdaa_gpio_patch(uint32_t gpio, const char *str); + +void hdaa_patch(struct hdaa_devinfo *devinfo); +void hdaa_patch_direct(struct hdaa_devinfo *devinfo); + +#endif diff --git a/sys/dev/sound/pci/hda/hdaa_patches.c b/sys/dev/sound/pci/hda/hdaa_patches.c new file mode 100644 index 0000000..b20c339 --- /dev/null +++ b/sys/dev/sound/pci/hda/hdaa_patches.c @@ -0,0 +1,638 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * 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 (Audio function quirks) driver for FreeBSD. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include <dev/sound/pcm/sound.h> + +#include <sys/ctype.h> + +#include <dev/sound/pci/hda/hdac.h> +#include <dev/sound/pci/hda/hdaa.h> +#include <dev/sound/pci/hda/hda_reg.h> + +SND_DECLARE_FILE("$FreeBSD$"); + +static const struct { + uint32_t model; + uint32_t id; + uint32_t set, unset; + uint32_t gpio; +} hdac_quirks[] = { + /* + * XXX Force stereo quirk. Monoural recording / playback + * on few codecs (especially ALC880) seems broken or + * perhaps unsupported. + */ + { HDA_MATCH_ALL, HDA_MATCH_ALL, + HDAA_QUIRK_FORCESTEREO | HDAA_QUIRK_IVREF, 0, + 0 }, + { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_G2K_SUBVENDOR, HDA_CODEC_ALC660, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV, 0, + 0 }, + { ASUS_A8X_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV, 0, + 0 }, + { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, + HDAA_QUIRK_OVREF, 0, + 0 }, + { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, + HDAA_QUIRK_OVREF, 0, + 0 }, + /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, + HDAA_QUIRK_IVREF80, HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF100, + 0 },*/ + { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, + 0, 0, + HDAA_GPIO_SET(1) }, + { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV | HDAA_QUIRK_SENSEINV, 0, + 0 }, + { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV, 0, + 0 }, + { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, + HDAA_QUIRK_OVREF50, 0, + HDAA_GPIO_SET(0) }, + { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, + 0, 0, + HDAA_GPIO_SET(0) | HDAA_GPIO_SET(1) }, + { APPLE_MACBOOKPRO55, HDA_CODEC_CS4206, + 0, 0, + HDAA_GPIO_SET(1) | HDAA_GPIO_SET(3) }, + { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205X, + 0, 0, + HDAA_GPIO_SET(0) }, + { DELL_V1400_SUBVENDOR, HDA_CODEC_STAC9228X, + 0, 0, + HDAA_GPIO_SET(2) }, + { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205X, + 0, 0, + HDAA_GPIO_SET(0) }, + { HDA_MATCH_ALL, HDA_CODEC_AD1988, + HDAA_QUIRK_IVREF80, HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF100, + 0 }, + { HDA_MATCH_ALL, HDA_CODEC_AD1988B, + HDAA_QUIRK_IVREF80, HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF100, + 0 }, + { HDA_MATCH_ALL, HDA_CODEC_CX20549, + 0, HDAA_QUIRK_FORCESTEREO, + 0 } +}; +#define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) + +static void +hdac_pin_patch(struct hdaa_widget *w) +{ + const char *patch = NULL; + uint32_t config, orig, id, subid; + nid_t nid = w->nid; + + config = orig = w->wclass.pin.config; + id = hdaa_codec_id(w->devinfo); + subid = hdaa_subvendor_id(w->devinfo); + + /* XXX: Old patches require complete review. + * Now they may create more problem then solve due to + * incorrect associations. + */ + if (id == HDA_CODEC_ALC880 && subid == LG_LW20_SUBVENDOR) { + switch (nid) { + case 26: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC880 && + (subid == CLEVO_D900T_SUBVENDOR || + subid == ASUS_M5200_SUBVENDOR)) { + /* + * Super broken BIOS + */ + switch (nid) { + 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; + } + } else if (id == HDA_CODEC_ALC883 && + (subid == MSI_MS034A_SUBVENDOR || + HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, subid))) { + switch (nid) { + case 25: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 28: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + } + } else if (id == HDA_CODEC_CX20549 && subid == + HP_V3000_SUBVENDOR) { + switch (nid) { + case 18: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + case 20: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 21: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + } + } else if (id == HDA_CODEC_CX20551 && subid == + HP_DV5000_SUBVENDOR) { + switch (nid) { + case 20: + case 21: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + } + } else if (id == HDA_CODEC_ALC861 && subid == + ASUS_W6F_SUBVENDOR) { + switch (nid) { + case 11: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 12: + case 14: + case 16: + case 31: + case 32: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + } + } else if (id == HDA_CODEC_ALC861 && subid == + UNIWILL_9075_SUBVENDOR) { + switch (nid) { + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + } + } + + /* New patches */ + if (id == HDA_CODEC_AD1986A && + (subid == ASUS_M2NPVMX_SUBVENDOR || + subid == ASUS_A8NVMCSM_SUBVENDOR || + subid == ASUS_P5PL2_SUBVENDOR)) { + switch (nid) { + case 26: /* Headphones with redirection */ + patch = "as=1 seq=15"; + break; + case 28: /* 5.1 out => 2.0 out + 1 input */ + patch = "device=Line-in as=8 seq=1"; + break; + case 29: /* Can't use this as input, as the only available mic + * preamplifier is busy by front panel mic (nid 31). + * If you want to use this rear connector as mic input, + * you have to disable the front panel one. */ + patch = "as=0"; + break; + case 31: /* Lot of inputs configured with as=15 and unusable */ + patch = "as=8 seq=3"; + break; + case 32: + patch = "as=8 seq=4"; + break; + case 34: + patch = "as=8 seq=5"; + break; + case 36: + patch = "as=8 seq=6"; + break; + } + } else if (id == HDA_CODEC_ALC260 && + HDA_DEV_MATCH(SONY_S5_SUBVENDOR, subid)) { + switch (nid) { + case 16: + patch = "seq=15 device=Headphones"; + break; + } + } else if (id == HDA_CODEC_ALC268) { + if (subid == ACER_T5320_SUBVENDOR) { + switch (nid) { + case 20: /* Headphones Jack */ + patch = "as=1 seq=15"; + break; + } + } + } else if (id == HDA_CODEC_CX20561 && + subid == LENOVO_B450_SUBVENDOR) { + switch (nid) { + case 22: + patch = "as=1 seq=15"; + break; + } + } + + if (patch != NULL) + config = hdaa_widget_pin_patch(config, patch); + HDA_BOOTVERBOSE( + if (config != orig) + device_printf(w->devinfo->dev, + "Patching pin config nid=%u 0x%08x -> 0x%08x\n", + nid, orig, config); + ); + w->wclass.pin.config = config; +} + +static void +hdaa_widget_patch(struct hdaa_widget *w) +{ + struct hdaa_devinfo *devinfo = w->devinfo; + uint32_t orig; + nid_t beeper = -1; + + orig = w->param.widget_cap; + /* On some codecs beeper is an input pin, but it is not recordable + alone. Also most of BIOSes does not declare beeper pin. + Change beeper pin node type to beeper to help parser. */ + switch (hdaa_codec_id(devinfo)) { + case HDA_CODEC_AD1882: + case HDA_CODEC_AD1883: + case HDA_CODEC_AD1984: + case HDA_CODEC_AD1984A: + case HDA_CODEC_AD1984B: + case HDA_CODEC_AD1987: + case HDA_CODEC_AD1988: + case HDA_CODEC_AD1988B: + case HDA_CODEC_AD1989B: + beeper = 26; + break; + case HDA_CODEC_ALC260: + beeper = 23; + break; + } + if (hda_get_vendor_id(devinfo->dev) == REALTEK_VENDORID && + hdaa_codec_id(devinfo) != HDA_CODEC_ALC260) + beeper = 29; + if (w->nid == beeper) { + 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; + w->waspin = 1; + } + HDA_BOOTVERBOSE( + if (w->param.widget_cap != orig) { + device_printf(w->devinfo->dev, + "Patching widget caps nid=%u 0x%08x -> 0x%08x\n", + w->nid, orig, w->param.widget_cap); + } + ); + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_pin_patch(w); +} + +void +hdaa_patch(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + uint32_t id, subid; + int i; + + id = hdaa_codec_id(devinfo); + subid = hdaa_subvendor_id(devinfo); + + /* + * Quirks + */ + for (i = 0; i < HDAC_QUIRKS_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subid) && + HDA_DEV_MATCH(hdac_quirks[i].id, id))) + continue; + if (hdac_quirks[i].set != 0) + devinfo->quirks |= + hdac_quirks[i].set; + if (hdac_quirks[i].unset != 0) + devinfo->quirks &= + ~(hdac_quirks[i].unset); + } + + /* Apply per-widget patch. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + hdaa_widget_patch(w); + } + + switch (id) { + case HDA_CODEC_AD1983: + /* + * This CODEC has several possible usages, but none + * fit the parser best. Help parser to choose better. + */ + /* Disable direct unmixed playback to get pcm volume. */ + w = hdaa_widget_get(devinfo, 5); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 6); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 11); + if (w != NULL) + w->connsenable[0] = 0; + /* Disable mic and line selectors. */ + w = hdaa_widget_get(devinfo, 12); + if (w != NULL) + w->connsenable[1] = 0; + w = hdaa_widget_get(devinfo, 13); + if (w != NULL) + w->connsenable[1] = 0; + /* Disable recording from mono playback mix. */ + w = hdaa_widget_get(devinfo, 20); + if (w != NULL) + w->connsenable[3] = 0; + break; + case HDA_CODEC_AD1986A: + /* + * This CODEC has overcomplicated input mixing. + * Make some cleaning there. + */ + /* Disable input mono mixer. Not needed and not supported. */ + w = hdaa_widget_get(devinfo, 43); + if (w != NULL) + w->enable = 0; + /* Disable any with any input mixing mesh. Use separately. */ + w = hdaa_widget_get(devinfo, 39); + if (w != NULL) + w->enable = 0; + w = hdaa_widget_get(devinfo, 40); + if (w != NULL) + w->enable = 0; + w = hdaa_widget_get(devinfo, 41); + if (w != NULL) + w->enable = 0; + w = hdaa_widget_get(devinfo, 42); + if (w != NULL) + w->enable = 0; + /* Disable duplicate mixer node connector. */ + w = hdaa_widget_get(devinfo, 15); + if (w != NULL) + w->connsenable[3] = 0; + /* There is only one mic preamplifier, use it effectively. */ + w = hdaa_widget_get(devinfo, 31); + if (w != NULL) { + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == + HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { + w = hdaa_widget_get(devinfo, 16); + if (w != NULL) + w->connsenable[2] = 0; + } else { + w = hdaa_widget_get(devinfo, 15); + if (w != NULL) + w->connsenable[0] = 0; + } + } + w = hdaa_widget_get(devinfo, 32); + if (w != NULL) { + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == + HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { + w = hdaa_widget_get(devinfo, 16); + if (w != NULL) + w->connsenable[0] = 0; + } else { + w = hdaa_widget_get(devinfo, 15); + if (w != NULL) + w->connsenable[1] = 0; + } + } + + if (subid == ASUS_A8X_SUBVENDOR) { + /* + * This is just plain ridiculous.. There + * are several A8 series that share the same + * pci id but works differently (EAPD). + */ + w = hdaa_widget_get(devinfo, 26); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) != + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) + devinfo->quirks &= + ~HDAA_QUIRK_EAPDINV; + } + break; + case HDA_CODEC_AD1981HD: + /* + * This CODEC has very unusual design with several + * points inappropriate for the present parser. + */ + /* Disable recording from mono playback mix. */ + w = hdaa_widget_get(devinfo, 21); + if (w != NULL) + w->connsenable[3] = 0; + /* Disable rear to front mic mixer, use separately. */ + w = hdaa_widget_get(devinfo, 31); + if (w != NULL) + w->enable = 0; + /* Disable direct playback, use mixer. */ + w = hdaa_widget_get(devinfo, 5); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 6); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 9); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 24); + if (w != NULL) + w->connsenable[0] = 0; + break; + case HDA_CODEC_CX20582: + case HDA_CODEC_CX20583: + case HDA_CODEC_CX20584: + case HDA_CODEC_CX20585: + case HDA_CODEC_CX20590: + /* + * These codecs have extra connectivity on record side + * too reach for the present parser. + */ + w = hdaa_widget_get(devinfo, 20); + if (w != NULL) + w->connsenable[1] = 0; + w = hdaa_widget_get(devinfo, 21); + if (w != NULL) + w->connsenable[1] = 0; + w = hdaa_widget_get(devinfo, 22); + if (w != NULL) + w->connsenable[0] = 0; + break; + case HDA_CODEC_VT1708S_0: + case HDA_CODEC_VT1708S_1: + case HDA_CODEC_VT1708S_2: + case HDA_CODEC_VT1708S_3: + case HDA_CODEC_VT1708S_4: + case HDA_CODEC_VT1708S_5: + case HDA_CODEC_VT1708S_6: + case HDA_CODEC_VT1708S_7: + /* + * These codecs have hidden mic boost controls. + */ + w = hdaa_widget_get(devinfo, 26); + if (w != NULL) + w->param.inamp_cap = + (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | + (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | + (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); + w = hdaa_widget_get(devinfo, 30); + if (w != NULL) + w->param.inamp_cap = + (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | + (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | + (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); + break; + } +} + +void +hdaa_patch_direct(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + uint32_t id, subid, val; + + id = hdaa_codec_id(devinfo); + subid = hdaa_subvendor_id(devinfo); + + switch (id) { + case HDA_CODEC_VT1708S_0: + case HDA_CODEC_VT1708S_1: + case HDA_CODEC_VT1708S_2: + case HDA_CODEC_VT1708S_3: + case HDA_CODEC_VT1708S_4: + case HDA_CODEC_VT1708S_5: + case HDA_CODEC_VT1708S_6: + case HDA_CODEC_VT1708S_7: + /* Enable Mic Boost Volume controls. */ + hda_command(dev, HDA_CMD_12BIT(0, devinfo->nid, + 0xf98, 0x01)); + /* Don't bypass mixer. */ + hda_command(dev, HDA_CMD_12BIT(0, devinfo->nid, + 0xf88, 0xc0)); + break; + } + if (subid == APPLE_INTEL_MAC) + hda_command(dev, HDA_CMD_12BIT(0, devinfo->nid, + 0x7e7, 0)); + if (id == HDA_CODEC_ALC269) { + if (subid == 0x104316e3 || subid == 0x1043831a || + subid == 0x1043834a || subid == 0x10438398 || + subid == 0x104383ce) { + /* + * The ditital mics on some Asus laptops produce + * differential signals instead of expected stereo. + * That results in silence if downmix it to mono. + * To workaround, make codec to handle signal as mono. + */ + hda_command(dev, HDA_CMD_SET_COEFF_INDEX(0, 0x20, 0x07)); + val = hda_command(dev, HDA_CMD_GET_PROCESSING_COEFF(0, 0x20)); + hda_command(dev, HDA_CMD_SET_COEFF_INDEX(0, 0x20, 0x07)); + hda_command(dev, HDA_CMD_SET_PROCESSING_COEFF(0, 0x20, val|0x80)); + } + } +} diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index a190e0b..0f11b29 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> - * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,45 +27,7 @@ */ /* - * 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 - * 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, especially 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 <sepotvin@videotron.ca> * - * * Andrea Bittau <a.bittau@cs.ucl.ac.uk> * - * * Wesley Morgan <morganw@chemikals.org> * - * * Daniel Eischen <deischen@FreeBSD.org> * - * * Maxime Guillaud <bsd-ports@mguillaud.net> * - * * Ariff Abdullah <ariff@FreeBSD.org> * - * * Alexander Motin <mav@FreeBSD.org> * - * * * - * * ....and various people from freebsd-multimedia@FreeBSD.org * - * * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Intel High Definition Audio (Controller) driver for FreeBSD. */ #ifdef HAVE_KERNEL_OPTION_HEADERS @@ -84,370 +46,26 @@ #include <dev/sound/pci/hda/hda_reg.h> #include <dev/sound/pci/hda/hdac.h> -#include "mixer_if.h" - -#define HDA_DRV_TEST_REV "20100226_0142" +#define HDA_DRV_TEST_REV "20120111_0001" SND_DECLARE_FILE("$FreeBSD$"); -#define HDA_BOOTVERBOSE(stmt) do { \ - if (bootverbose != 0 || snd_verbose > 3) { \ - stmt \ - } \ -} while (0) - -#define HDA_BOOTHVERBOSE(stmt) do { \ - if (snd_verbose > 3) { \ - 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 - -/* Default controller / jack sense poll: 250ms */ -#define HDAC_POLL_INTERVAL max(hz >> 2, 1) - -/* - * Make room for possible 4096 playback/record channels, in 100 years to come. - */ -#define HDAC_TRIGGER_NONE 0x00000000 -#define HDAC_TRIGGER_PLAY 0x00000fff -#define HDAC_TRIGGER_REC 0x00fff000 -#define HDAC_TRIGGER_UNSOL 0x80000000 - -#define HDA_MODEL_CONSTRUCT(vendor, model) \ - (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) - -/* Controller models */ - -/* Intel */ -#define INTEL_VENDORID 0x8086 -#define HDA_INTEL_CPT HDA_MODEL_CONSTRUCT(INTEL, 0x1c20) -#define HDA_INTEL_PATSBURG HDA_MODEL_CONSTRUCT(INTEL, 0x1d20) -#define HDA_INTEL_PPT1 HDA_MODEL_CONSTRUCT(INTEL, 0x1e20) -#define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) -#define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) -#define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) -#define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) -#define HDA_INTEL_82801I HDA_MODEL_CONSTRUCT(INTEL, 0x293e) -#define HDA_INTEL_82801JI HDA_MODEL_CONSTRUCT(INTEL, 0x3a3e) -#define HDA_INTEL_82801JD HDA_MODEL_CONSTRUCT(INTEL, 0x3a6e) -#define HDA_INTEL_PCH HDA_MODEL_CONSTRUCT(INTEL, 0x3b56) -#define HDA_INTEL_PCH2 HDA_MODEL_CONSTRUCT(INTEL, 0x3b57) -#define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b) -#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_MCP61_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) -#define HDA_NVIDIA_MCP61_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) -#define HDA_NVIDIA_MCP65_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) -#define HDA_NVIDIA_MCP65_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) -#define HDA_NVIDIA_MCP67_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055c) -#define HDA_NVIDIA_MCP67_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055d) -#define HDA_NVIDIA_MCP78_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0774) -#define HDA_NVIDIA_MCP78_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0775) -#define HDA_NVIDIA_MCP78_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0776) -#define HDA_NVIDIA_MCP78_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0777) -#define HDA_NVIDIA_MCP73_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fc) -#define HDA_NVIDIA_MCP73_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fd) -#define HDA_NVIDIA_MCP79_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac0) -#define HDA_NVIDIA_MCP79_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac1) -#define HDA_NVIDIA_MCP79_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac2) -#define HDA_NVIDIA_MCP79_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac3) -#define HDA_NVIDIA_MCP89_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d94) -#define HDA_NVIDIA_MCP89_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d95) -#define HDA_NVIDIA_MCP89_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d96) -#define HDA_NVIDIA_MCP89_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d97) -#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_RS600 HDA_MODEL_CONSTRUCT(ATI, 0x793b) -#define HDA_ATI_RS690 HDA_MODEL_CONSTRUCT(ATI, 0x7919) -#define HDA_ATI_RS780 HDA_MODEL_CONSTRUCT(ATI, 0x960f) -#define HDA_ATI_R600 HDA_MODEL_CONSTRUCT(ATI, 0xaa00) -#define HDA_ATI_RV630 HDA_MODEL_CONSTRUCT(ATI, 0xaa08) -#define HDA_ATI_RV610 HDA_MODEL_CONSTRUCT(ATI, 0xaa10) -#define HDA_ATI_RV670 HDA_MODEL_CONSTRUCT(ATI, 0xaa18) -#define HDA_ATI_RV635 HDA_MODEL_CONSTRUCT(ATI, 0xaa20) -#define HDA_ATI_RV620 HDA_MODEL_CONSTRUCT(ATI, 0xaa28) -#define HDA_ATI_RV770 HDA_MODEL_CONSTRUCT(ATI, 0xaa30) -#define HDA_ATI_RV730 HDA_MODEL_CONSTRUCT(ATI, 0xaa38) -#define HDA_ATI_RV710 HDA_MODEL_CONSTRUCT(ATI, 0xaa40) -#define HDA_ATI_RV740 HDA_MODEL_CONSTRUCT(ATI, 0xaa48) -#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) - -/* RDC */ -#define RDC_VENDORID 0x17f3 -#define HDA_RDC_M3010 HDA_MODEL_CONSTRUCT(RDC, 0x3010) - -/* 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) - -/* ULI */ -#define ULI_VENDORID 0x10b9 -#define HDA_ULI_M5461 HDA_MODEL_CONSTRUCT(ULI, 0x5461) -#define HDA_ULI_ALL HDA_MODEL_CONSTRUCT(ULI, 0xffff) - -/* OEM/subvendors */ - -/* Intel */ -#define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) - -/* 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_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) -#define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) -#define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) -#define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) -#define HP_DC7700S_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2801) -#define HP_DC7700_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2802) -#define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) -/* What is wrong with XN 2563 anyway? (Got the picture ?) */ -#define HP_NX6325_SUBVENDORX 0x103c30b0 - -/* Dell */ -#define DELL_VENDORID 0x1028 -#define DELL_D630_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01f9) -#define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) -#define DELL_V1400_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0227) -#define DELL_V1500_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0228) -#define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) -#define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) -#define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) -#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_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) -#define ACER_A4520_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0127) -#define ACER_A4710_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x012f) -#define ACER_A4715_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0133) -#define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) -#define ACER_T6292_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011b) -#define ACER_T5320_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011f) -#define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) - -/* Asus */ -#define ASUS_VENDORID 0x1043 -#define ASUS_A8X_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) -#define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) -#define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) -#define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) -#define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) -#define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339) -#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) -#define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) -#define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) -#define ASUS_P5PL2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x817f) -#define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) -#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) -#define ASUS_M2V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81e7) -#define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec) -#define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) -#define ASUS_A8NVMCSM_SUBVENDOR HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84) -#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) - -/* Lenovo */ -#define LENOVO_VENDORID 0x17aa -#define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) -#define LENOVO_3KN200_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e) -#define LENOVO_B450_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3a0d) -#define LENOVO_TCA55_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x1015) -#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) - -/* Samsung */ -#define SAMSUNG_VENDORID 0x144d -#define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) -#define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) - -/* Medion ? */ -#define MEDION_VENDORID 0x161f -#define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) -#define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) - -/* Apple Computer Inc. */ -#define APPLE_VENDORID 0x106b -#define APPLE_MB3_SUBVENDOR HDA_MODEL_CONSTRUCT(APPLE, 0x00a1) - -/* Sony */ -#define SONY_VENDORID 0x104d -#define SONY_S5_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0x81cc) -#define SONY_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0xffff) - -/* - * Apple Intel MacXXXX seems using Sigmatel codec/vendor id - * instead of their own, which is beyond my comprehension - * (see HDA_CODEC_STAC9221 below). - */ -#define APPLE_INTEL_MAC 0x76808384 -#define APPLE_MACBOOKPRO55 0xcb7910de - -/* LG Electronics */ -#define LG_VENDORID 0x1854 -#define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) -#define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) - -/* Fujitsu Siemens */ -#define FS_VENDORID 0x1734 -#define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) -#define FS_SI1848_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10cd) -#define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) - -/* Fujitsu Limited */ -#define FL_VENDORID 0x10cf -#define FL_S7020D_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x1326) -#define FL_U1010_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x142d) -#define FL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0xffff) - -/* Toshiba */ -#define TOSHIBA_VENDORID 0x1179 -#define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) -#define TOSHIBA_A135_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xff01) -#define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) - -/* Micro-Star International (MSI) */ -#define MSI_VENDORID 0x1462 -#define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) -#define MSI_MS034A_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x034a) -#define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) - -/* Giga-Byte Technology */ -#define GB_VENDORID 0x1458 -#define GB_G33S2H_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xa022) -#define GP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xffff) - -/* Uniwill ? */ -#define UNIWILL_VENDORID 0x1584 -#define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) -#define UNIWILL_9080_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9080) - - -/* Misc constants.. */ -#define HDA_AMP_VOL_DEFAULT (-1) -#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_ADC_MONITOR (1 << 0) - -#define HDA_CTL_OUT 1 -#define HDA_CTL_IN 2 - -#define HDA_GPIO_MAX 8 -/* 0 - 7 = GPIO , 8 = Flush */ -#define HDA_QUIRK_GPIO0 (1 << 0) -#define HDA_QUIRK_GPIO1 (1 << 1) -#define HDA_QUIRK_GPIO2 (1 << 2) -#define HDA_QUIRK_GPIO3 (1 << 3) -#define HDA_QUIRK_GPIO4 (1 << 4) -#define HDA_QUIRK_GPIO5 (1 << 5) -#define HDA_QUIRK_GPIO6 (1 << 6) -#define HDA_QUIRK_GPIO7 (1 << 7) -#define HDA_QUIRK_GPIOFLUSH (1 << 8) - -/* 9 - 25 = anything else */ -#define HDA_QUIRK_SOFTPCMVOL (1 << 9) -#define HDA_QUIRK_FIXEDRATE (1 << 10) -#define HDA_QUIRK_FORCESTEREO (1 << 11) -#define HDA_QUIRK_EAPDINV (1 << 12) -#define HDA_QUIRK_DMAPOS (1 << 13) -#define HDA_QUIRK_SENSEINV (1 << 14) - -/* 26 - 31 = vrefs */ -#define HDA_QUIRK_IVREF50 (1 << 26) -#define HDA_QUIRK_IVREF80 (1 << 27) -#define HDA_QUIRK_IVREF100 (1 << 28) -#define HDA_QUIRK_OVREF50 (1 << 29) -#define HDA_QUIRK_OVREF80 (1 << 30) -#define HDA_QUIRK_OVREF100 (1 << 31) - -#define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 | \ - HDA_QUIRK_IVREF100) -#define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 | \ - HDA_QUIRK_OVREF100) -#define HDA_QUIRK_VREF (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF) - -#if __FreeBSD_version < 600000 -#define taskqueue_drain(...) -#endif +#define HDAC_QUIRK_64BIT (1 << 0) +#define HDAC_QUIRK_DMAPOS (1 << 1) +#define HDAC_QUIRK_MSI (1 << 2) static const struct { char *key; uint32_t value; } hdac_quirks_tab[] = { - { "gpio0", HDA_QUIRK_GPIO0 }, - { "gpio1", HDA_QUIRK_GPIO1 }, - { "gpio2", HDA_QUIRK_GPIO2 }, - { "gpio3", HDA_QUIRK_GPIO3 }, - { "gpio4", HDA_QUIRK_GPIO4 }, - { "gpio5", HDA_QUIRK_GPIO5 }, - { "gpio6", HDA_QUIRK_GPIO6 }, - { "gpio7", HDA_QUIRK_GPIO7 }, - { "gpioflush", HDA_QUIRK_GPIOFLUSH }, - { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, - { "fixedrate", HDA_QUIRK_FIXEDRATE }, - { "forcestereo", HDA_QUIRK_FORCESTEREO }, - { "eapdinv", HDA_QUIRK_EAPDINV }, - { "dmapos", HDA_QUIRK_DMAPOS }, - { "senseinv", HDA_QUIRK_SENSEINV }, - { "ivref50", HDA_QUIRK_IVREF50 }, - { "ivref80", HDA_QUIRK_IVREF80 }, - { "ivref100", HDA_QUIRK_IVREF100 }, - { "ovref50", HDA_QUIRK_OVREF50 }, - { "ovref80", HDA_QUIRK_OVREF80 }, - { "ovref100", HDA_QUIRK_OVREF100 }, - { "ivref", HDA_QUIRK_IVREF }, - { "ovref", HDA_QUIRK_OVREF }, - { "vref", HDA_QUIRK_VREF }, + { "64bit", HDAC_QUIRK_DMAPOS }, + { "dmapos", HDAC_QUIRK_DMAPOS }, + { "msi", HDAC_QUIRK_MSI }, }; #define HDAC_QUIRKS_TAB_LEN \ (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) @@ -456,105 +74,81 @@ static const struct { #define HDA_BDL_MAX 256 #define HDA_BDL_DEFAULT HDA_BDL_MIN -#define HDA_BLK_MIN HDAC_DMA_ALIGNMENT +#define HDA_BLK_MIN HDA_DMA_ALIGNMENT #define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) #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 - -MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); - -const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue", "Green", "Red", - "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B", "Res.C", "Res.D", - "White", "Other"}; - -const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD", - "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in", - "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"}; - -const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; - -/* Default */ -static uint32_t hdac_fmt[] = { - SND_FORMAT(AFMT_S16_LE, 2, 0), - 0 -}; - -static struct pcmchan_caps hdac_caps = {48000, 48000, hdac_fmt, 0}; - -#define HDAC_NO_MSI 1 -#define HDAC_NO_64BIT 2 +MALLOC_DEFINE(M_HDAC, "hdac", "HDA Controller"); static const struct { uint32_t model; char *desc; - char flags; + char quirks_on; + char quirks_off; } hdac_devices[] = { - { HDA_INTEL_CPT, "Intel Cougar Point", 0 }, - { HDA_INTEL_PATSBURG,"Intel Patsburg", 0 }, - { HDA_INTEL_PPT1, "Intel Panther Point", 0 }, - { HDA_INTEL_82801F, "Intel 82801F", 0 }, - { HDA_INTEL_63XXESB, "Intel 631x/632xESB", 0 }, - { HDA_INTEL_82801G, "Intel 82801G", 0 }, - { HDA_INTEL_82801H, "Intel 82801H", 0 }, - { HDA_INTEL_82801I, "Intel 82801I", 0 }, - { HDA_INTEL_82801JI, "Intel 82801JI", 0 }, - { HDA_INTEL_82801JD, "Intel 82801JD", 0 }, - { HDA_INTEL_PCH, "Intel 5 Series/3400 Series", 0 }, - { HDA_INTEL_PCH2, "Intel 5 Series/3400 Series", 0 }, - { HDA_INTEL_SCH, "Intel SCH", 0 }, - { HDA_NVIDIA_MCP51, "NVidia MCP51", HDAC_NO_MSI }, - { HDA_NVIDIA_MCP55, "NVidia MCP55", HDAC_NO_MSI }, - { HDA_NVIDIA_MCP61_1, "NVidia MCP61", 0 }, - { HDA_NVIDIA_MCP61_2, "NVidia MCP61", 0 }, - { HDA_NVIDIA_MCP65_1, "NVidia MCP65", 0 }, - { HDA_NVIDIA_MCP65_2, "NVidia MCP65", 0 }, - { HDA_NVIDIA_MCP67_1, "NVidia MCP67", 0 }, - { HDA_NVIDIA_MCP67_2, "NVidia MCP67", 0 }, - { HDA_NVIDIA_MCP73_1, "NVidia MCP73", 0 }, - { HDA_NVIDIA_MCP73_2, "NVidia MCP73", 0 }, - { HDA_NVIDIA_MCP78_1, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP78_2, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP78_3, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP78_4, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP79_1, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP79_2, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP79_3, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP79_4, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP89_1, "NVidia MCP89", 0 }, - { HDA_NVIDIA_MCP89_2, "NVidia MCP89", 0 }, - { HDA_NVIDIA_MCP89_3, "NVidia MCP89", 0 }, - { HDA_NVIDIA_MCP89_4, "NVidia MCP89", 0 }, - { HDA_ATI_SB450, "ATI SB450", 0 }, - { HDA_ATI_SB600, "ATI SB600", 0 }, - { HDA_ATI_RS600, "ATI RS600", 0 }, - { HDA_ATI_RS690, "ATI RS690", 0 }, - { HDA_ATI_RS780, "ATI RS780", 0 }, - { HDA_ATI_R600, "ATI R600", 0 }, - { HDA_ATI_RV610, "ATI RV610", 0 }, - { HDA_ATI_RV620, "ATI RV620", 0 }, - { HDA_ATI_RV630, "ATI RV630", 0 }, - { HDA_ATI_RV635, "ATI RV635", 0 }, - { HDA_ATI_RV710, "ATI RV710", 0 }, - { HDA_ATI_RV730, "ATI RV730", 0 }, - { HDA_ATI_RV740, "ATI RV740", 0 }, - { HDA_ATI_RV770, "ATI RV770", 0 }, - { HDA_RDC_M3010, "RDC M3010", 0 }, - { HDA_VIA_VT82XX, "VIA VT8251/8237A",0 }, - { HDA_SIS_966, "SiS 966", 0 }, - { HDA_ULI_M5461, "ULI M5461", 0 }, + { HDA_INTEL_CPT, "Intel Cougar Point", 0, 0 }, + { HDA_INTEL_PATSBURG,"Intel Patsburg", 0, 0 }, + { HDA_INTEL_PPT1, "Intel Panther Point", 0, 0 }, + { HDA_INTEL_82801F, "Intel 82801F", 0, 0 }, + { HDA_INTEL_63XXESB, "Intel 631x/632xESB", 0, 0 }, + { HDA_INTEL_82801G, "Intel 82801G", 0, 0 }, + { HDA_INTEL_82801H, "Intel 82801H", 0, 0 }, + { HDA_INTEL_82801I, "Intel 82801I", 0, 0 }, + { HDA_INTEL_82801JI, "Intel 82801JI", 0, 0 }, + { HDA_INTEL_82801JD, "Intel 82801JD", 0, 0 }, + { HDA_INTEL_PCH, "Intel 5 Series/3400 Series", 0, 0 }, + { HDA_INTEL_PCH2, "Intel 5 Series/3400 Series", 0, 0 }, + { HDA_INTEL_SCH, "Intel SCH", 0, 0 }, + { HDA_NVIDIA_MCP51, "NVIDIA MCP51", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_MCP55, "NVIDIA MCP55", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_MCP61_1, "NVIDIA MCP61", 0, 0 }, + { HDA_NVIDIA_MCP61_2, "NVIDIA MCP61", 0, 0 }, + { HDA_NVIDIA_MCP65_1, "NVIDIA MCP65", 0, 0 }, + { HDA_NVIDIA_MCP65_2, "NVIDIA MCP65", 0, 0 }, + { HDA_NVIDIA_MCP67_1, "NVIDIA MCP67", 0, 0 }, + { HDA_NVIDIA_MCP67_2, "NVIDIA MCP67", 0, 0 }, + { HDA_NVIDIA_MCP73_1, "NVIDIA MCP73", 0, 0 }, + { HDA_NVIDIA_MCP73_2, "NVIDIA MCP73", 0, 0 }, + { HDA_NVIDIA_MCP78_1, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP78_2, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP78_3, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP78_4, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP79_1, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP79_2, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP79_3, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP79_4, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP89_1, "NVIDIA MCP89", 0, 0 }, + { HDA_NVIDIA_MCP89_2, "NVIDIA MCP89", 0, 0 }, + { HDA_NVIDIA_MCP89_3, "NVIDIA MCP89", 0, 0 }, + { HDA_NVIDIA_MCP89_4, "NVIDIA MCP89", 0, 0 }, + { HDA_ATI_SB450, "ATI SB450", 0, 0 }, + { HDA_ATI_SB600, "ATI SB600", 0, 0 }, + { HDA_ATI_RS600, "ATI RS600", 0, 0 }, + { HDA_ATI_RS690, "ATI RS690", 0, 0 }, + { HDA_ATI_RS780, "ATI RS780", 0, 0 }, + { HDA_ATI_R600, "ATI R600", 0, 0 }, + { HDA_ATI_RV610, "ATI RV610", 0, 0 }, + { HDA_ATI_RV620, "ATI RV620", 0, 0 }, + { HDA_ATI_RV630, "ATI RV630", 0, 0 }, + { HDA_ATI_RV635, "ATI RV635", 0, 0 }, + { HDA_ATI_RV710, "ATI RV710", 0, 0 }, + { HDA_ATI_RV730, "ATI RV730", 0, 0 }, + { HDA_ATI_RV740, "ATI RV740", 0, 0 }, + { HDA_ATI_RV770, "ATI RV770", 0, 0 }, + { HDA_RDC_M3010, "RDC M3010", 0, 0 }, + { HDA_VIA_VT82XX, "VIA VT8251/8237A",0, 0 }, + { HDA_SIS_966, "SiS 966", 0, 0 }, + { HDA_ULI_M5461, "ULI M5461", 0, 0 }, /* 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)" }, - { HDA_ULI_ALL, "ULI (Unknown)" }, + { HDA_INTEL_ALL, "Intel (Unknown)", 0, 0 }, + { HDA_NVIDIA_ALL, "NVIDIA (Unknown)", 0, 0 }, + { HDA_ATI_ALL, "ATI (Unknown)", 0, 0 }, + { HDA_VIA_ALL, "VIA (Unknown)", 0, 0 }, + { HDA_SIS_ALL, "SiS (Unknown)", 0, 0 }, + { HDA_ULI_ALL, "ULI (Unknown)", 0, 0 }, }; #define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) @@ -571,482 +165,6 @@ static const struct { #define HDAC_PCIESNOOP_LEN \ (sizeof(hdac_pcie_snoop) / sizeof(hdac_pcie_snoop[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)) - -/* Cirrus Logic */ -#define CIRRUSLOGIC_VENDORID 0x1013 -#define HDA_CODEC_CS4206 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4206) -#define HDA_CODEC_CS4207 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4207) -#define HDA_CODEC_CSXXXX HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0xffff) - -/* Realtek */ -#define REALTEK_VENDORID 0x10ec -#define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) -#define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) -#define HDA_CODEC_ALC267 HDA_CODEC_CONSTRUCT(REALTEK, 0x0267) -#define HDA_CODEC_ALC268 HDA_CODEC_CONSTRUCT(REALTEK, 0x0268) -#define HDA_CODEC_ALC269 HDA_CODEC_CONSTRUCT(REALTEK, 0x0269) -#define HDA_CODEC_ALC270 HDA_CODEC_CONSTRUCT(REALTEK, 0x0270) -#define HDA_CODEC_ALC272 HDA_CODEC_CONSTRUCT(REALTEK, 0x0272) -#define HDA_CODEC_ALC273 HDA_CODEC_CONSTRUCT(REALTEK, 0x0273) -#define HDA_CODEC_ALC275 HDA_CODEC_CONSTRUCT(REALTEK, 0x0275) -#define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660) -#define HDA_CODEC_ALC662 HDA_CODEC_CONSTRUCT(REALTEK, 0x0662) -#define HDA_CODEC_ALC663 HDA_CODEC_CONSTRUCT(REALTEK, 0x0663) -#define HDA_CODEC_ALC665 HDA_CODEC_CONSTRUCT(REALTEK, 0x0665) -#define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) -#define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) -#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_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) -#define HDA_CODEC_ALC887 HDA_CODEC_CONSTRUCT(REALTEK, 0x0887) -#define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) -#define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889) -#define HDA_CODEC_ALC892 HDA_CODEC_CONSTRUCT(REALTEK, 0x0892) -#define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) - -/* Analog Devices */ -#define ANALOGDEVICES_VENDORID 0x11d4 -#define HDA_CODEC_AD1884A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x184a) -#define HDA_CODEC_AD1882 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1882) -#define HDA_CODEC_AD1883 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1883) -#define HDA_CODEC_AD1884 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1884) -#define HDA_CODEC_AD1984A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194a) -#define HDA_CODEC_AD1984B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194b) -#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) -#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) -#define HDA_CODEC_AD1984 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1984) -#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) -#define HDA_CODEC_AD1987 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1987) -#define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) -#define HDA_CODEC_AD1988B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b) -#define HDA_CODEC_AD1882A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x882a) -#define HDA_CODEC_AD1989B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989b) -#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 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_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) -#define HDA_CODEC_STAC9230D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7613) -#define HDA_CODEC_STAC9229X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7614) -#define HDA_CODEC_STAC9229D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7615) -#define HDA_CODEC_STAC9228X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7616) -#define HDA_CODEC_STAC9228D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7617) -#define HDA_CODEC_STAC9227X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) -#define HDA_CODEC_STAC9227D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7619) -#define HDA_CODEC_STAC9274 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7620) -#define HDA_CODEC_STAC9274D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7621) -#define HDA_CODEC_STAC9273X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7622) -#define HDA_CODEC_STAC9273D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7623) -#define HDA_CODEC_STAC9272X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7624) -#define HDA_CODEC_STAC9272D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7625) -#define HDA_CODEC_STAC9271X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7626) -#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) -#define HDA_CODEC_STAC9274X5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7628) -#define HDA_CODEC_STAC9274D5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7629) -#define HDA_CODEC_STAC9250 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7634) -#define HDA_CODEC_STAC9251 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7636) -#define HDA_CODEC_IDT92HD700X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7638) -#define HDA_CODEC_IDT92HD700D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7639) -#define HDA_CODEC_IDT92HD206X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7645) -#define HDA_CODEC_IDT92HD206D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7646) -#define HDA_CODEC_CXD9872RDK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7661) -#define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) -#define HDA_CODEC_CXD9872AKD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7664) -#define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) -#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) -#define HDA_CODEC_STAC9221_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7682) -#define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) -#define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) -#define HDA_CODEC_STAC9200D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7691) -#define HDA_CODEC_IDT92HD005 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7698) -#define HDA_CODEC_IDT92HD005D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7699) -#define HDA_CODEC_STAC9205X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a0) -#define HDA_CODEC_STAC9205D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a1) -#define HDA_CODEC_STAC9204X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a2) -#define HDA_CODEC_STAC9204D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a3) -#define HDA_CODEC_STAC9220_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7880) -#define HDA_CODEC_STAC9220_A1 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7882) -#define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) - -/* IDT */ -#define IDT_VENDORID 0x111d -#define HDA_CODEC_IDT92HD75BX HDA_CODEC_CONSTRUCT(IDT, 0x7603) -#define HDA_CODEC_IDT92HD83C1X HDA_CODEC_CONSTRUCT(IDT, 0x7604) -#define HDA_CODEC_IDT92HD81B1X HDA_CODEC_CONSTRUCT(IDT, 0x7605) -#define HDA_CODEC_IDT92HD75B3 HDA_CODEC_CONSTRUCT(IDT, 0x7608) -#define HDA_CODEC_IDT92HD73D1 HDA_CODEC_CONSTRUCT(IDT, 0x7674) -#define HDA_CODEC_IDT92HD73C1 HDA_CODEC_CONSTRUCT(IDT, 0x7675) -#define HDA_CODEC_IDT92HD73E1 HDA_CODEC_CONSTRUCT(IDT, 0x7676) -#define HDA_CODEC_IDT92HD71B8 HDA_CODEC_CONSTRUCT(IDT, 0x76b0) -#define HDA_CODEC_IDT92HD71B7 HDA_CODEC_CONSTRUCT(IDT, 0x76b2) -#define HDA_CODEC_IDT92HD71B5 HDA_CODEC_CONSTRUCT(IDT, 0x76b6) -#define HDA_CODEC_IDT92HD83C1C HDA_CODEC_CONSTRUCT(IDT, 0x76d4) -#define HDA_CODEC_IDT92HD81B1C HDA_CODEC_CONSTRUCT(IDT, 0x76d5) -#define HDA_CODEC_IDTXXXX HDA_CODEC_CONSTRUCT(IDT, 0xffff) - -/* Silicon Image */ -#define SII_VENDORID 0x1095 -#define HDA_CODEC_SII1390 HDA_CODEC_CONSTRUCT(SII, 0x1390) -#define HDA_CODEC_SII1392 HDA_CODEC_CONSTRUCT(SII, 0x1392) -#define HDA_CODEC_SIIXXXX HDA_CODEC_CONSTRUCT(SII, 0xffff) - -/* Lucent/Agere */ -#define AGERE_VENDORID 0x11c1 -#define HDA_CODEC_AGEREXXXX HDA_CODEC_CONSTRUCT(AGERE, 0xffff) - -/* Conexant */ -#define CONEXANT_VENDORID 0x14f1 -#define HDA_CODEC_CX20549 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) -#define HDA_CODEC_CX20551 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) -#define HDA_CODEC_CX20561 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5051) -#define HDA_CODEC_CX20582 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5066) -#define HDA_CODEC_CX20583 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5067) -#define HDA_CODEC_CX20584 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5068) -#define HDA_CODEC_CX20585 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5069) -#define HDA_CODEC_CX20590 HDA_CODEC_CONSTRUCT(CONEXANT, 0x506e) -#define HDA_CODEC_CX20631 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5097) -#define HDA_CODEC_CX20632 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5098) -#define HDA_CODEC_CX20641 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a1) -#define HDA_CODEC_CX20642 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a2) -#define HDA_CODEC_CX20651 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ab) -#define HDA_CODEC_CX20652 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ac) -#define HDA_CODEC_CX20664 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b8) -#define HDA_CODEC_CX20665 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b9) -#define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) - -/* VIA */ -#define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) -#define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) -#define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) -#define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) -#define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) -#define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) -#define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) -#define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) -#define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) -#define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) -#define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) -#define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) -#define HDA_CODEC_VT1708B_0 HDA_CODEC_CONSTRUCT(VIA, 0xe720) -#define HDA_CODEC_VT1708B_1 HDA_CODEC_CONSTRUCT(VIA, 0xe721) -#define HDA_CODEC_VT1708B_2 HDA_CODEC_CONSTRUCT(VIA, 0xe722) -#define HDA_CODEC_VT1708B_3 HDA_CODEC_CONSTRUCT(VIA, 0xe723) -#define HDA_CODEC_VT1708B_4 HDA_CODEC_CONSTRUCT(VIA, 0xe724) -#define HDA_CODEC_VT1708B_5 HDA_CODEC_CONSTRUCT(VIA, 0xe725) -#define HDA_CODEC_VT1708B_6 HDA_CODEC_CONSTRUCT(VIA, 0xe726) -#define HDA_CODEC_VT1708B_7 HDA_CODEC_CONSTRUCT(VIA, 0xe727) -#define HDA_CODEC_VT1708S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0397) -#define HDA_CODEC_VT1708S_1 HDA_CODEC_CONSTRUCT(VIA, 0x1397) -#define HDA_CODEC_VT1708S_2 HDA_CODEC_CONSTRUCT(VIA, 0x2397) -#define HDA_CODEC_VT1708S_3 HDA_CODEC_CONSTRUCT(VIA, 0x3397) -#define HDA_CODEC_VT1708S_4 HDA_CODEC_CONSTRUCT(VIA, 0x4397) -#define HDA_CODEC_VT1708S_5 HDA_CODEC_CONSTRUCT(VIA, 0x5397) -#define HDA_CODEC_VT1708S_6 HDA_CODEC_CONSTRUCT(VIA, 0x6397) -#define HDA_CODEC_VT1708S_7 HDA_CODEC_CONSTRUCT(VIA, 0x7397) -#define HDA_CODEC_VT1702_0 HDA_CODEC_CONSTRUCT(VIA, 0x0398) -#define HDA_CODEC_VT1702_1 HDA_CODEC_CONSTRUCT(VIA, 0x1398) -#define HDA_CODEC_VT1702_2 HDA_CODEC_CONSTRUCT(VIA, 0x2398) -#define HDA_CODEC_VT1702_3 HDA_CODEC_CONSTRUCT(VIA, 0x3398) -#define HDA_CODEC_VT1702_4 HDA_CODEC_CONSTRUCT(VIA, 0x4398) -#define HDA_CODEC_VT1702_5 HDA_CODEC_CONSTRUCT(VIA, 0x5398) -#define HDA_CODEC_VT1702_6 HDA_CODEC_CONSTRUCT(VIA, 0x6398) -#define HDA_CODEC_VT1702_7 HDA_CODEC_CONSTRUCT(VIA, 0x7398) -#define HDA_CODEC_VT1716S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0433) -#define HDA_CODEC_VT1716S_1 HDA_CODEC_CONSTRUCT(VIA, 0xa721) -#define HDA_CODEC_VT1718S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0428) -#define HDA_CODEC_VT1718S_1 HDA_CODEC_CONSTRUCT(VIA, 0x4428) -#define HDA_CODEC_VT1812 HDA_CODEC_CONSTRUCT(VIA, 0x0448) -#define HDA_CODEC_VT1818S HDA_CODEC_CONSTRUCT(VIA, 0x0440) -#define HDA_CODEC_VT1828S HDA_CODEC_CONSTRUCT(VIA, 0x4441) -#define HDA_CODEC_VT2002P_0 HDA_CODEC_CONSTRUCT(VIA, 0x0438) -#define HDA_CODEC_VT2002P_1 HDA_CODEC_CONSTRUCT(VIA, 0x4438) -#define HDA_CODEC_VT2020 HDA_CODEC_CONSTRUCT(VIA, 0x0441) -#define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) - -/* ATI */ -#define HDA_CODEC_ATIRS600_1 HDA_CODEC_CONSTRUCT(ATI, 0x793c) -#define HDA_CODEC_ATIRS600_2 HDA_CODEC_CONSTRUCT(ATI, 0x7919) -#define HDA_CODEC_ATIRS690 HDA_CODEC_CONSTRUCT(ATI, 0x791a) -#define HDA_CODEC_ATIR6XX HDA_CODEC_CONSTRUCT(ATI, 0xaa01) -#define HDA_CODEC_ATIXXXX HDA_CODEC_CONSTRUCT(ATI, 0xffff) - -/* NVIDIA */ -#define HDA_CODEC_NVIDIAMCP78 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0002) -#define HDA_CODEC_NVIDIAMCP78_2 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0006) -#define HDA_CODEC_NVIDIAMCP7A HDA_CODEC_CONSTRUCT(NVIDIA, 0x0007) -#define HDA_CODEC_NVIDIAGT220 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000a) -#define HDA_CODEC_NVIDIAGT21X HDA_CODEC_CONSTRUCT(NVIDIA, 0x000b) -#define HDA_CODEC_NVIDIAMCP89 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000c) -#define HDA_CODEC_NVIDIAGT240 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000d) -#define HDA_CODEC_NVIDIAMCP67 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0067) -#define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001) -#define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) - -/* INTEL */ -#define HDA_CODEC_INTELIP HDA_CODEC_CONSTRUCT(INTEL, 0x0054) -#define HDA_CODEC_INTELBL HDA_CODEC_CONSTRUCT(INTEL, 0x2801) -#define HDA_CODEC_INTELCA HDA_CODEC_CONSTRUCT(INTEL, 0x2802) -#define HDA_CODEC_INTELEL HDA_CODEC_CONSTRUCT(INTEL, 0x2803) -#define HDA_CODEC_INTELIP2 HDA_CODEC_CONSTRUCT(INTEL, 0x2804) -#define HDA_CODEC_INTELCPT HDA_CODEC_CONSTRUCT(INTEL, 0x2805) -#define HDA_CODEC_INTELCL HDA_CODEC_CONSTRUCT(INTEL, 0x29fb) -#define HDA_CODEC_INTELXXXX HDA_CODEC_CONSTRUCT(INTEL, 0xffff) - -/* Codecs */ -static const struct { - uint32_t id; - char *name; -} hdac_codecs[] = { - { HDA_CODEC_CS4206, "Cirrus Logic CS4206" }, - { HDA_CODEC_CS4207, "Cirrus Logic CS4207" }, - { HDA_CODEC_ALC260, "Realtek ALC260" }, - { HDA_CODEC_ALC262, "Realtek ALC262" }, - { HDA_CODEC_ALC267, "Realtek ALC267" }, - { HDA_CODEC_ALC268, "Realtek ALC268" }, - { HDA_CODEC_ALC269, "Realtek ALC269" }, - { HDA_CODEC_ALC270, "Realtek ALC270" }, - { HDA_CODEC_ALC272, "Realtek ALC272" }, - { HDA_CODEC_ALC273, "Realtek ALC273" }, - { HDA_CODEC_ALC275, "Realtek ALC275" }, - { HDA_CODEC_ALC660, "Realtek ALC660" }, - { HDA_CODEC_ALC662, "Realtek ALC662" }, - { HDA_CODEC_ALC663, "Realtek ALC663" }, - { HDA_CODEC_ALC665, "Realtek ALC665" }, - { HDA_CODEC_ALC861, "Realtek ALC861" }, - { HDA_CODEC_ALC861VD, "Realtek ALC861-VD" }, - { HDA_CODEC_ALC880, "Realtek ALC880" }, - { HDA_CODEC_ALC882, "Realtek ALC882" }, - { HDA_CODEC_ALC883, "Realtek ALC883" }, - { HDA_CODEC_ALC885, "Realtek ALC885" }, - { HDA_CODEC_ALC887, "Realtek ALC887" }, - { HDA_CODEC_ALC888, "Realtek ALC888" }, - { HDA_CODEC_ALC889, "Realtek ALC889" }, - { HDA_CODEC_ALC892, "Realtek ALC892" }, - { HDA_CODEC_AD1882, "Analog Devices AD1882" }, - { HDA_CODEC_AD1882A, "Analog Devices AD1882A" }, - { HDA_CODEC_AD1883, "Analog Devices AD1883" }, - { HDA_CODEC_AD1884, "Analog Devices AD1884" }, - { HDA_CODEC_AD1884A, "Analog Devices AD1884A" }, - { HDA_CODEC_AD1981HD, "Analog Devices AD1981HD" }, - { HDA_CODEC_AD1983, "Analog Devices AD1983" }, - { HDA_CODEC_AD1984, "Analog Devices AD1984" }, - { HDA_CODEC_AD1984A, "Analog Devices AD1984A" }, - { HDA_CODEC_AD1984B, "Analog Devices AD1984B" }, - { HDA_CODEC_AD1986A, "Analog Devices AD1986A" }, - { HDA_CODEC_AD1987, "Analog Devices AD1987" }, - { HDA_CODEC_AD1988, "Analog Devices AD1988A" }, - { HDA_CODEC_AD1988B, "Analog Devices AD1988B" }, - { HDA_CODEC_AD1989B, "Analog Devices AD1989B" }, - { HDA_CODEC_CMI9880, "CMedia CMI9880" }, - { HDA_CODEC_CXD9872RDK, "Sigmatel CXD9872RD/K" }, - { HDA_CODEC_CXD9872AKD, "Sigmatel CXD9872AKD" }, - { HDA_CODEC_STAC9200D, "Sigmatel STAC9200D" }, - { HDA_CODEC_STAC9204X, "Sigmatel STAC9204X" }, - { HDA_CODEC_STAC9204D, "Sigmatel STAC9204D" }, - { HDA_CODEC_STAC9205X, "Sigmatel STAC9205X" }, - { HDA_CODEC_STAC9205D, "Sigmatel STAC9205D" }, - { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, - { HDA_CODEC_STAC9220_A1, "Sigmatel STAC9220_A1" }, - { HDA_CODEC_STAC9220_A2, "Sigmatel STAC9220_A2" }, - { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, - { HDA_CODEC_STAC9221_A2, "Sigmatel STAC9221_A2" }, - { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, - { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, - { HDA_CODEC_STAC9227X, "Sigmatel STAC9227X" }, - { HDA_CODEC_STAC9227D, "Sigmatel STAC9227D" }, - { HDA_CODEC_STAC9228X, "Sigmatel STAC9228X" }, - { HDA_CODEC_STAC9228D, "Sigmatel STAC9228D" }, - { HDA_CODEC_STAC9229X, "Sigmatel STAC9229X" }, - { HDA_CODEC_STAC9229D, "Sigmatel STAC9229D" }, - { HDA_CODEC_STAC9230X, "Sigmatel STAC9230X" }, - { HDA_CODEC_STAC9230D, "Sigmatel STAC9230D" }, - { HDA_CODEC_STAC9250, "Sigmatel STAC9250" }, - { HDA_CODEC_STAC9251, "Sigmatel STAC9251" }, - { HDA_CODEC_STAC9271X, "Sigmatel STAC9271X" }, - { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" }, - { HDA_CODEC_STAC9272X, "Sigmatel STAC9272X" }, - { HDA_CODEC_STAC9272D, "Sigmatel STAC9272D" }, - { HDA_CODEC_STAC9273X, "Sigmatel STAC9273X" }, - { HDA_CODEC_STAC9273D, "Sigmatel STAC9273D" }, - { HDA_CODEC_STAC9274, "Sigmatel STAC9274" }, - { HDA_CODEC_STAC9274D, "Sigmatel STAC9274D" }, - { HDA_CODEC_STAC9274X5NH, "Sigmatel STAC9274X5NH" }, - { HDA_CODEC_STAC9274D5NH, "Sigmatel STAC9274D5NH" }, - { HDA_CODEC_STAC9872AK, "Sigmatel STAC9872AK" }, - { HDA_CODEC_IDT92HD005, "IDT 92HD005" }, - { HDA_CODEC_IDT92HD005D, "IDT 92HD005D" }, - { HDA_CODEC_IDT92HD206X, "IDT 92HD206X" }, - { HDA_CODEC_IDT92HD206D, "IDT 92HD206D" }, - { HDA_CODEC_IDT92HD700X, "IDT 92HD700X" }, - { HDA_CODEC_IDT92HD700D, "IDT 92HD700D" }, - { HDA_CODEC_IDT92HD71B5, "IDT 92HD71B5" }, - { HDA_CODEC_IDT92HD71B7, "IDT 92HD71B7" }, - { HDA_CODEC_IDT92HD71B8, "IDT 92HD71B8" }, - { HDA_CODEC_IDT92HD73C1, "IDT 92HD73C1" }, - { HDA_CODEC_IDT92HD73D1, "IDT 92HD73D1" }, - { HDA_CODEC_IDT92HD73E1, "IDT 92HD73E1" }, - { HDA_CODEC_IDT92HD75B3, "IDT 92HD75B3" }, - { HDA_CODEC_IDT92HD75BX, "IDT 92HD75BX" }, - { HDA_CODEC_IDT92HD81B1C, "IDT 92HD81B1C" }, - { HDA_CODEC_IDT92HD81B1X, "IDT 92HD81B1X" }, - { HDA_CODEC_IDT92HD83C1C, "IDT 92HD83C1C" }, - { HDA_CODEC_IDT92HD83C1X, "IDT 92HD83C1X" }, - { HDA_CODEC_CX20549, "Conexant CX20549 (Venice)" }, - { HDA_CODEC_CX20551, "Conexant CX20551 (Waikiki)" }, - { HDA_CODEC_CX20561, "Conexant CX20561 (Hermosa)" }, - { HDA_CODEC_CX20582, "Conexant CX20582 (Pebble)" }, - { HDA_CODEC_CX20583, "Conexant CX20583 (Pebble HSF)" }, - { HDA_CODEC_CX20584, "Conexant CX20584" }, - { HDA_CODEC_CX20585, "Conexant CX20585" }, - { HDA_CODEC_CX20590, "Conexant CX20590" }, - { HDA_CODEC_CX20631, "Conexant CX20631" }, - { HDA_CODEC_CX20632, "Conexant CX20632" }, - { HDA_CODEC_CX20641, "Conexant CX20641" }, - { HDA_CODEC_CX20642, "Conexant CX20642" }, - { HDA_CODEC_CX20651, "Conexant CX20651" }, - { HDA_CODEC_CX20652, "Conexant CX20652" }, - { HDA_CODEC_CX20664, "Conexant CX20664" }, - { HDA_CODEC_CX20665, "Conexant CX20665" }, - { HDA_CODEC_VT1708_8, "VIA VT1708_8" }, - { HDA_CODEC_VT1708_9, "VIA VT1708_9" }, - { HDA_CODEC_VT1708_A, "VIA VT1708_A" }, - { HDA_CODEC_VT1708_B, "VIA VT1708_B" }, - { HDA_CODEC_VT1709_0, "VIA VT1709_0" }, - { HDA_CODEC_VT1709_1, "VIA VT1709_1" }, - { HDA_CODEC_VT1709_2, "VIA VT1709_2" }, - { HDA_CODEC_VT1709_3, "VIA VT1709_3" }, - { HDA_CODEC_VT1709_4, "VIA VT1709_4" }, - { HDA_CODEC_VT1709_5, "VIA VT1709_5" }, - { HDA_CODEC_VT1709_6, "VIA VT1709_6" }, - { HDA_CODEC_VT1709_7, "VIA VT1709_7" }, - { HDA_CODEC_VT1708B_0, "VIA VT1708B_0" }, - { HDA_CODEC_VT1708B_1, "VIA VT1708B_1" }, - { HDA_CODEC_VT1708B_2, "VIA VT1708B_2" }, - { HDA_CODEC_VT1708B_3, "VIA VT1708B_3" }, - { HDA_CODEC_VT1708B_4, "VIA VT1708B_4" }, - { HDA_CODEC_VT1708B_5, "VIA VT1708B_5" }, - { HDA_CODEC_VT1708B_6, "VIA VT1708B_6" }, - { HDA_CODEC_VT1708B_7, "VIA VT1708B_7" }, - { HDA_CODEC_VT1708S_0, "VIA VT1708S_0" }, - { HDA_CODEC_VT1708S_1, "VIA VT1708S_1" }, - { HDA_CODEC_VT1708S_2, "VIA VT1708S_2" }, - { HDA_CODEC_VT1708S_3, "VIA VT1708S_3" }, - { HDA_CODEC_VT1708S_4, "VIA VT1708S_4" }, - { HDA_CODEC_VT1708S_5, "VIA VT1708S_5" }, - { HDA_CODEC_VT1708S_6, "VIA VT1708S_6" }, - { HDA_CODEC_VT1708S_7, "VIA VT1708S_7" }, - { HDA_CODEC_VT1702_0, "VIA VT1702_0" }, - { HDA_CODEC_VT1702_1, "VIA VT1702_1" }, - { HDA_CODEC_VT1702_2, "VIA VT1702_2" }, - { HDA_CODEC_VT1702_3, "VIA VT1702_3" }, - { HDA_CODEC_VT1702_4, "VIA VT1702_4" }, - { HDA_CODEC_VT1702_5, "VIA VT1702_5" }, - { HDA_CODEC_VT1702_6, "VIA VT1702_6" }, - { HDA_CODEC_VT1702_7, "VIA VT1702_7" }, - { HDA_CODEC_VT1716S_0, "VIA VT1716S_0" }, - { HDA_CODEC_VT1716S_1, "VIA VT1716S_1" }, - { HDA_CODEC_VT1718S_0, "VIA VT1718S_0" }, - { HDA_CODEC_VT1718S_1, "VIA VT1718S_1" }, - { HDA_CODEC_VT1812, "VIA VT1812" }, - { HDA_CODEC_VT1818S, "VIA VT1818S" }, - { HDA_CODEC_VT1828S, "VIA VT1828S" }, - { HDA_CODEC_VT2002P_0, "VIA VT2002P_0" }, - { HDA_CODEC_VT2002P_1, "VIA VT2002P_1" }, - { HDA_CODEC_VT2020, "VIA VT2020" }, - { HDA_CODEC_ATIRS600_1,"ATI RS600 HDMI" }, - { HDA_CODEC_ATIRS600_2,"ATI RS600 HDMI" }, - { HDA_CODEC_ATIRS690, "ATI RS690/780 HDMI" }, - { HDA_CODEC_ATIR6XX, "ATI R6xx HDMI" }, - { HDA_CODEC_NVIDIAMCP67, "NVidia MCP67 HDMI" }, - { HDA_CODEC_NVIDIAMCP73, "NVidia MCP73 HDMI" }, - { HDA_CODEC_NVIDIAMCP78, "NVidia MCP78 HDMI" }, - { HDA_CODEC_NVIDIAMCP78_2, "NVidia MCP78 HDMI" }, - { HDA_CODEC_NVIDIAMCP7A, "NVidia MCP7A HDMI" }, - { HDA_CODEC_NVIDIAGT220, "NVidia GT220 HDMI" }, - { HDA_CODEC_NVIDIAGT21X, "NVidia GT21x HDMI" }, - { HDA_CODEC_NVIDIAMCP89, "NVidia MCP89 HDMI" }, - { HDA_CODEC_NVIDIAGT240, "NVidia GT240 HDMI" }, - { HDA_CODEC_INTELIP, "Intel Ibex Peak HDMI" }, - { HDA_CODEC_INTELBL, "Intel Bearlake HDMI" }, - { HDA_CODEC_INTELCA, "Intel Cantiga HDMI" }, - { HDA_CODEC_INTELEL, "Intel Eaglelake HDMI" }, - { HDA_CODEC_INTELIP2, "Intel Ibex Peak HDMI" }, - { HDA_CODEC_INTELCPT, "Intel Cougar Point HDMI" }, - { HDA_CODEC_INTELCL, "Intel Crestline HDMI" }, - { HDA_CODEC_SII1390, "Silicon Image SiI1390 HDMI" }, - { HDA_CODEC_SII1392, "Silicon Image SiI1392 HDMI" }, - /* Unknown codec */ - { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, - { HDA_CODEC_ADXXXX, "Analog Devices (Unknown)" }, - { HDA_CODEC_CSXXXX, "Cirrus Logic (Unknown)" }, - { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, - { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, - { HDA_CODEC_SIIXXXX, "Silicon Image (Unknown)" }, - { HDA_CODEC_AGEREXXXX, "Lucent/Agere Systems (Unknown)" }, - { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, - { HDA_CODEC_VTXXXX, "VIA (Unknown)" }, - { HDA_CODEC_ATIXXXX, "ATI (Unknown)" }, - { HDA_CODEC_NVIDIAXXXX,"NVidia (Unknown)" }, - { HDA_CODEC_INTELXXXX, "Intel (Unknown)" }, - { HDA_CODEC_IDTXXXX, "IDT (Unknown)" }, -}; -#define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) - - /**************************************************************************** * Function prototypes ****************************************************************************/ @@ -1065,392 +183,78 @@ 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 void hdac_probe_codec(struct hdac_codec *); -static void hdac_probe_function(struct hdac_codec *, nid_t); -static int hdac_pcmchannel_setup(struct hdac_chan *); 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 uint32_t hdac_send_command(struct hdac_softc *, nid_t, uint32_t); static int hdac_probe(device_t); static int hdac_attach(device_t); static int hdac_detach(device_t); static int hdac_suspend(device_t); static int hdac_resume(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, int); -static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, - nid_t, nid_t, int, int, int, int, int, int); -static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); static int hdac_rirb_flush(struct hdac_softc *sc); static int hdac_unsolq_flush(struct hdac_softc *sc); -static void hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf); - #define hdac_command(a1, a2, a3) \ - hdac_command_sendone_internal(a1, a2, a3) - -#define hdac_codec_id(c) \ - ((uint32_t)((c == NULL) ? 0x00000000 : \ - ((((uint32_t)(c)->vendor_id & 0x0000ffff) << 16) | \ - ((uint32_t)(c)->device_id & 0x0000ffff)))) - -static char * -hdac_codec_name(struct hdac_codec *codec) -{ - uint32_t id; - int i; + hdac_send_command(a1, a3, a2) - id = hdac_codec_id(codec); - - 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_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; - } - } - return (buf); -} - -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 dir, - int index, int cnt) -{ - struct hdac_audio_ctl *ctl; - int i, found = 0; - - if (devinfo == NULL || devinfo->function.audio.ctl == NULL) - return (NULL); - - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if (ctl->widget->nid != nid) - continue; - if (dir && ctl->ndir != dir) - continue; - if (index >= 0 && ctl->ndir == HDA_CTL_IN && - ctl->dir == ctl->ndir && ctl->index != index) - continue; - found++; - if (found == cnt || cnt <= 0) - return (ctl); - } - - return (NULL); -} - -/* - * Jack detection (Speaker/HP redirection) event handler. - */ +/* This function surely going to make its way into upper level someday. */ static void -hdac_hp_switch_handler(struct hdac_devinfo *devinfo) +hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) { - struct hdac_audio_as *as; - struct hdac_softc *sc; - struct hdac_widget *w; - struct hdac_audio_ctl *ctl; - uint32_t val, res; - int i, j; - nid_t cad; + const char *res = NULL; + int i = 0, j, k, len, inv; - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) + if (resource_string_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "config", &res) != 0) return; - - sc = devinfo->codec->sc; - cad = devinfo->codec->cad; - as = devinfo->function.audio.as; - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].hpredir < 0) - continue; - - w = hdac_widget_get(devinfo, as[i].pins[15]); - if (w == NULL || w->enable == 0 || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - - res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, as[i].pins[15]), cad); - - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "Pin sense: nid=%d res=0x%08x\n", - as[i].pins[15], res); - ); - - res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); - if (devinfo->function.audio.quirks & HDA_QUIRK_SENSEINV) - res ^= 1; - - /* (Un)Mute headphone pin. */ - ctl = hdac_audio_ctl_amp_get(devinfo, - as[i].pins[15], HDA_CTL_IN, -1, 1); - if (ctl != NULL && ctl->mute) { - /* If pin has muter - use it. */ - val = (res != 0) ? 0 : 1; - if (val != ctl->forcemute) { - ctl->forcemute = val; - hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, - HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); - } - } else { - /* If there is no muter - disable pin output. */ - w = hdac_widget_get(devinfo, as[i].pins[15]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if (res != 0) - val = w->wclass.pin.ctrl | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - else - val = w->wclass.pin.ctrl & - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (val != w->wclass.pin.ctrl) { - w->wclass.pin.ctrl = val; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, - w->nid, w->wclass.pin.ctrl), cad); - } - } + if (!(res != NULL && strlen(res) > 0)) + return; + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Config options:"); + ); + for (;;) { + while (res[i] != '\0' && + (res[i] == ',' || isspace(res[i]) != 0)) + i++; + if (res[i] == '\0') { + HDA_BOOTVERBOSE( + printf("\n"); + ); + return; } - /* (Un)Mute other pins. */ - for (j = 0; j < 15; j++) { - if (as[i].pins[j] <= 0) + 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; - ctl = hdac_audio_ctl_amp_get(devinfo, - as[i].pins[j], HDA_CTL_IN, -1, 1); - if (ctl != NULL && ctl->mute) { - /* If pin has muter - use it. */ - val = (res != 0) ? 1 : 0; - if (val == ctl->forcemute) - continue; - ctl->forcemute = val; - hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, - HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); + if (len - inv != strlen(hdac_quirks_tab[k].key)) continue; + HDA_BOOTVERBOSE( + printf(" %s%s", (inv != 0) ? "no" : "", + hdac_quirks_tab[k].key); + ); + if (inv == 0) { + *on |= hdac_quirks_tab[k].value; + *on &= ~hdac_quirks_tab[k].value; + } else if (inv != 0) { + *off |= hdac_quirks_tab[k].value; + *off &= ~hdac_quirks_tab[k].value; } - /* If there is no muter - disable pin output. */ - w = hdac_widget_get(devinfo, as[i].pins[j]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if (res != 0) - val = w->wclass.pin.ctrl & - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - else - val = w->wclass.pin.ctrl | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (val != w->wclass.pin.ctrl) { - w->wclass.pin.ctrl = val; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, - w->nid, w->wclass.pin.ctrl), cad); - } - } - } - } -} - -/* - * Callback for poll based jack detection. - */ -static void -hdac_jack_poll_callback(void *arg) -{ - struct hdac_devinfo *devinfo = arg; - struct hdac_softc *sc; - - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) - return; - sc = devinfo->codec->sc; - hdac_lock(sc); - if (sc->poll_ival == 0) { - hdac_unlock(sc); - return; - } - hdac_hp_switch_handler(devinfo); - callout_reset(&sc->poll_jack, sc->poll_ival, - hdac_jack_poll_callback, devinfo); - hdac_unlock(sc); -} - -/* - * Jack detection initializer. - */ -static void -hdac_hp_switch_init(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - uint32_t id; - int i, enable = 0, poll = 0; - nid_t cad; - - id = hdac_codec_id(devinfo->codec); - cad = devinfo->codec->cad; - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].hpredir < 0) - continue; - - w = hdac_widget_get(devinfo, as[i].pins[15]); - if (w == NULL || w->enable == 0 || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || - (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) { - device_printf(sc->dev, - "No jack detection support at pin %d\n", - as[i].pins[15]); - continue; - } - enable = 1; - if (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); - } else - poll = 1; - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "Enabling headphone/speaker " - "audio routing switching:\n"); - device_printf(sc->dev, "\tas=%d sense nid=%d [%s]\n", - i, w->nid, (poll != 0) ? "POLL" : "UNSOL"); - ); - } - if (enable) { - hdac_hp_switch_handler(devinfo); - if (poll) { - callout_reset(&sc->poll_jack, 1, - hdac_jack_poll_callback, devinfo); - } - } -} - -/* - * Unsolicited messages handler. - */ -static void -hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) -{ - struct hdac_softc *sc; - struct hdac_devinfo *devinfo = NULL; - int i; - - if (codec == NULL || codec->sc == NULL) - return; - - sc = codec->sc; - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "Unsol Tag: 0x%08x\n", tag); - ); - - for (i = 0; i < codec->num_fgs; i++) { - if (codec->fgs[i].node_type == - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - devinfo = &codec->fgs[i]; break; } + i = j; } - - if (devinfo == NULL) - return; - - switch (tag) { - case HDAC_UNSOLTAG_EVENT_HP: - hdac_hp_switch_handler(devinfo); - break; - default: - device_printf(sc->dev, "Unknown unsol tag: 0x%08x!\n", tag); - break; - } -} - -static int -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->flags & HDAC_CHN_RUNNING)) - return (0); - - /* 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(ch->pdevinfo->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 - return (1); - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - } -#endif - - return (0); } /**************************************************************************** @@ -1462,69 +266,70 @@ static void hdac_intr_handler(void *context) { struct hdac_softc *sc; + device_t dev; uint32_t intsts; uint8_t rirbsts; - struct hdac_rirb *rirb_base; - uint32_t trigger; int i; sc = (struct hdac_softc *)context; - hdac_lock(sc); - if (sc->polling != 0) { - hdac_unlock(sc); - return; - } /* Do we have anything to do? */ intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); - if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { + if ((intsts & HDAC_INTSTS_GIS) == 0) { hdac_unlock(sc); return; } - trigger = 0; - /* Was this a controller interrupt? */ - if (HDA_FLAG_MATCH(intsts, HDAC_INTSTS_CIS)) { - rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + if (intsts & HDAC_INTSTS_CIS) { rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); /* Get as many responses that we can */ - while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { + while (rirbsts & HDAC_RIRBSTS_RINTFL) { HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); - if (hdac_rirb_flush(sc) != 0) - trigger |= HDAC_TRIGGER_UNSOL; + hdac_rirb_flush(sc); 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 (sc->unsolq_rp != sc->unsolq_wp) + taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); } if (intsts & HDAC_INTSTS_SIS_MASK) { - for (i = 0; i < sc->num_chans; i++) { - if ((intsts & (1 << (sc->chans[i].off >> 5))) && - hdac_stream_intr(sc, &sc->chans[i]) != 0) - trigger |= (1 << i); + for (i = 0; i < sc->num_ss; i++) { + if ((intsts & (1 << i)) == 0) + continue; + HDAC_WRITE_1(&sc->mem, (i << 5) + HDAC_SDSTS, + HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); + if ((dev = sc->streams[i].dev) != NULL) { + HDAC_STREAM_INTR(dev, + sc->streams[i].dir, sc->streams[i].stream); + } } - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & - HDAC_INTSTS_SIS_MASK); -#endif } + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts); hdac_unlock(sc); +} - for (i = 0; i < sc->num_chans; i++) { - if (trigger & (1 << i)) - chn_intr(sc->chans[i].c); +static void +hdac_poll_callback(void *arg) +{ + struct hdac_softc *sc = arg; + + if (sc == NULL) + return; + + hdac_lock(sc); + if (sc->polling == 0) { + hdac_unlock(sc); + return; } - if (trigger & HDAC_TRIGGER_UNSOL) - taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); + callout_reset(&sc->poll_callout, sc->poll_ival, + hdac_poll_callback, sc); + hdac_unlock(sc); + + hdac_intr_handler(sc); } /**************************************************************************** @@ -1577,11 +382,11 @@ hdac_reset(struct hdac_softc *sc, int wakeup) device_printf(sc->dev, "Unable to put hdac in reset\n"); return (ENXIO); } - + /* If wakeup is not requested - leave the controller in reset state. */ if (!wakeup) return (0); - + DELAY(100); gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); @@ -1629,8 +434,13 @@ hdac_get_capabilities(struct hdac_softc *sc) sc->num_iss = HDAC_GCAP_ISS(gcap); sc->num_oss = HDAC_GCAP_OSS(gcap); sc->num_bss = HDAC_GCAP_BSS(gcap); + sc->num_ss = sc->num_iss + sc->num_oss + sc->num_bss; sc->num_sdo = HDAC_GCAP_NSDO(gcap); - sc->support_64bit = HDA_FLAG_MATCH(gcap, HDAC_GCAP_64OK); + sc->support_64bit = (gcap & HDAC_GCAP_64OK) != 0; + if (sc->quirks_on & HDAC_QUIRK_64BIT) + sc->support_64bit = 1; + else if (sc->quirks_off & HDAC_QUIRK_64BIT) + sc->support_64bit = 0; corbsize = HDAC_READ_1(&sc->mem, HDAC_CORBSIZE); if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_256) == @@ -1707,7 +517,7 @@ hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) bus_size_t roundsz; int result; - roundsz = roundup2(size, HDAC_DMA_ALIGNMENT); + roundsz = roundup2(size, HDA_DMA_ALIGNMENT); bzero(dma, sizeof(*dma)); /* @@ -1715,7 +525,7 @@ hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) */ result = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ - HDAC_DMA_ALIGNMENT, /* alignment */ + HDA_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ @@ -1863,12 +673,10 @@ hdac_irq_alloc(struct hdac_softc *sc) irq = &sc->irq; irq->irq_rid = 0x0; - if ((sc->flags & HDAC_F_MSI) && + if ((sc->quirks_off & HDAC_QUIRK_MSI) == 0 && (result = pci_msi_count(sc->dev)) == 1 && pci_alloc_msi(sc->dev, &result) == 0) irq->irq_rid = 0x1; - else - sc->flags &= ~HDAC_F_MSI; irq->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &irq->irq_rid, RF_SHAREABLE | RF_ACTIVE); @@ -2058,884 +866,10 @@ hdac_rirb_start(struct hdac_softc *sc) HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, rirbctl); } - -/**************************************************************************** - * void hdac_scan_codecs(struct hdac_softc *, int) - * - * Scan the bus for available codecs, starting with num. - ****************************************************************************/ -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. */ - codec = (struct hdac_codec *)malloc(sizeof(*codec), - M_HDAC, M_ZERO | M_NOWAIT); - if (codec == NULL) { - device_printf(sc->dev, - "Unable to allocate memory for codec\n"); - continue; - } - codec->commands = NULL; - codec->responses_received = 0; - codec->verbs_sent = 0; - codec->sc = sc; - codec->cad = i; - sc->codecs[i] = codec; - hdac_probe_codec(codec); - } - } - /* 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 void -hdac_probe_codec(struct hdac_codec *codec) -{ - struct hdac_softc *sc = codec->sc; - uint32_t vendorid, revisionid, subnode; - int startnode; - int endnode; - int i; - nid_t cad = codec->cad; - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "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); - codec->vendor_id = HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); - codec->device_id = HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); - codec->revision_id = HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); - codec->stepping_id = HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); - - if (vendorid == HDAC_INVALID && revisionid == HDAC_INVALID) { - device_printf(sc->dev, "Codec #%d is not responding!" - " Probing aborted.\n", cad); - return; - } - - device_printf(sc->dev, "HDA Codec #%d: %s\n", - cad, hdac_codec_name(codec)); - HDA_BOOTVERBOSE( - device_printf(sc->dev, " HDA Codec ID: 0x%08x\n", - hdac_codec_id(codec)); - device_printf(sc->dev, " Vendor: 0x%04x\n", - codec->vendor_id); - device_printf(sc->dev, " Device: 0x%04x\n", - codec->device_id); - device_printf(sc->dev, " Revision: 0x%02x\n", - codec->revision_id); - device_printf(sc->dev, " Stepping: 0x%02x\n", - codec->stepping_id); - device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", - sc->pci_subvendor); - ); - 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_BOOTHVERBOSE( - device_printf(sc->dev, "\tstartnode=%d endnode=%d\n", - startnode, endnode); - ); - - codec->fgs = (struct hdac_devinfo *)malloc(sizeof(struct hdac_devinfo) * - (endnode - startnode), M_HDAC, M_NOWAIT | M_ZERO); - if (codec->fgs == NULL) { - device_printf(sc->dev, "%s: Unable to allocate function groups\n", - __func__); - return; - } - - for (i = startnode; i < endnode; i++) - hdac_probe_function(codec, i); - return; -} - -/* - * Probe codec function and add it to the list. - */ -static void -hdac_probe_function(struct hdac_codec *codec, nid_t nid) -{ - struct hdac_softc *sc = codec->sc; - struct hdac_devinfo *devinfo = &codec->fgs[codec->num_fgs]; - uint32_t fctgrptype; - uint32_t res; - 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)); - - devinfo->nid = nid; - devinfo->node_type = fctgrptype; - devinfo->codec = codec; - - 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, - "\tFound %s FG nid=%d startnode=%d endnode=%d total=%d\n", - (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": - (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": - "unknown", nid, devinfo->startnode, devinfo->endnode, - devinfo->nodecnt); - ); - - if (devinfo->nodecnt > 0) - devinfo->widget = (struct hdac_widget *)malloc( - sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, - M_NOWAIT | M_ZERO); - 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; - } - - codec->num_fgs++; -} - -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, ents, entnum; - nid_t cad = w->devinfo->codec->cad; - nid_t nid = w->nid; - nid_t cnid, addcnid, prevcnid; - - w->nconns = 0; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); - - ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); - - if (ents < 1) - return; - - entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; - max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; - prevcnid = 0; - -#define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) -#define CONN_NMASK(e) (CONN_RMASK(e) - 1) -#define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) -#define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) -#define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) - - for (i = 0; i < ents; i += entnum) { - res = hdac_command(sc, - HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); - for (j = 0; j < entnum; j++) { - cnid = CONN_CNID(res, entnum, j); - if (cnid == 0) { - if (w->nconns < ents) - device_printf(sc->dev, - "%s: nid=%d WARNING: zero cnid " - "entnum=%d j=%d index=%d " - "entries=%d found=%d res=0x%08x\n", - __func__, nid, entnum, j, i, - ents, w->nconns, res); - else - goto getconns_out; - } - if (cnid < w->devinfo->startnode || - cnid >= w->devinfo->endnode) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "GHOST: nid=%d j=%d " - "entnum=%d index=%d res=0x%08x\n", - nid, j, entnum, i, res); - ); - } - if (CONN_RANGE(res, entnum, j) == 0) - addcnid = cnid; - else if (prevcnid == 0 || prevcnid >= cnid) { - device_printf(sc->dev, - "%s: WARNING: Invalid child range " - "nid=%d index=%d j=%d entnum=%d " - "prevcnid=%d cnid=%d res=0x%08x\n", - __func__, nid, i, j, entnum, prevcnid, - cnid, res); - addcnid = cnid; - } else - addcnid = prevcnid + 1; - while (addcnid <= cnid) { - if (w->nconns > max) { - device_printf(sc->dev, - "Adding %d (nid=%d): " - "Max connection reached! max=%d\n", - addcnid, nid, max + 1); - goto getconns_out; - } - w->connsenable[w->nconns] = 1; - w->conns[w->nconns++] = addcnid++; - } - prevcnid = cnid; - } - } - -getconns_out: - return; -} - -static uint32_t -hdac_widget_pin_patch(uint32_t config, const char *str) -{ - char buf[256]; - char *key, *value, *rest, *bad; - int ival, i; - - strlcpy(buf, str, sizeof(buf)); - rest = buf; - while ((key = strsep(&rest, "=")) != NULL) { - value = strsep(&rest, " \t"); - if (value == NULL) - break; - ival = strtol(value, &bad, 10); - if (strcmp(key, "seq") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) & - HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK); - } else if (strcmp(key, "as") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) & - HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK); - } else if (strcmp(key, "misc") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) & - HDA_CONFIG_DEFAULTCONF_MISC_MASK); - } else if (strcmp(key, "color") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK; - if (bad[0] == 0) { - config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) & - HDA_CONFIG_DEFAULTCONF_COLOR_MASK); - }; - for (i = 0; i < 16; i++) { - if (strcasecmp(HDA_COLORS[i], value) == 0) { - config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT); - break; - } - } - } else if (strcmp(key, "ctype") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) & - HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK); - } else if (strcmp(key, "device") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - if (bad[0] == 0) { - config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK); - continue; - }; - for (i = 0; i < 16; i++) { - if (strcasecmp(HDA_DEVS[i], value) == 0) { - config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT); - break; - } - } - } else if (strcmp(key, "loc") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) & - HDA_CONFIG_DEFAULTCONF_LOCATION_MASK); - } else if (strcmp(key, "conn") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; - if (bad[0] == 0) { - config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) & - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - continue; - }; - for (i = 0; i < 4; i++) { - if (strcasecmp(HDA_CONNS[i], value) == 0) { - config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT); - break; - } - } - } - } - return (config); -} - -static uint32_t -hdac_widget_pin_getconfig(struct hdac_widget *w) -{ - struct hdac_softc *sc; - uint32_t config, orig, id; - nid_t cad, nid; - char buf[32]; - const char *res = NULL, *patch = NULL; - - sc = w->devinfo->codec->sc; - cad = w->devinfo->codec->cad; - nid = w->nid; - id = hdac_codec_id(w->devinfo->codec); - - config = hdac_command(sc, - HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), - cad); - orig = config; - - HDA_BOOTVERBOSE( - hdac_dump_pin_config(w, orig); - ); - - /* XXX: Old patches require complete review. - * Now they may create more problem then solve due to - * incorrect associations. - */ - if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) { - switch (nid) { - case 26: - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; - break; - case 27: - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; - break; - default: - break; - } - } else if (id == HDA_CODEC_ALC880 && - (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || - sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { - /* - * Super broken BIOS - */ - switch (nid) { - 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; - } - } else if (id == HDA_CODEC_ALC883 && - (sc->pci_subvendor == MSI_MS034A_SUBVENDOR || - HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor))) { - switch (nid) { - case 25: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 28: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - } - } else if (id == HDA_CODEC_CX20549 && sc->pci_subvendor == - HP_V3000_SUBVENDOR) { - switch (nid) { - case 18: - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; - config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; - break; - case 20: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 21: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - } - } else if (id == HDA_CODEC_CX20551 && sc->pci_subvendor == - HP_DV5000_SUBVENDOR) { - switch (nid) { - case 20: - case 21: - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; - config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; - break; - } - } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == - ASUS_W6F_SUBVENDOR) { - switch (nid) { - case 11: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 12: - case 14: - case 16: - case 31: - case 32: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 15: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); - break; - } - } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == - UNIWILL_9075_SUBVENDOR) { - switch (nid) { - case 15: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); - break; - } - } - - /* New patches */ - if (id == HDA_CODEC_AD1986A && - (sc->pci_subvendor == ASUS_M2NPVMX_SUBVENDOR || - sc->pci_subvendor == ASUS_A8NVMCSM_SUBVENDOR || - sc->pci_subvendor == ASUS_P5PL2_SUBVENDOR)) { - switch (nid) { - case 26: /* Headphones with redirection */ - patch = "as=1 seq=15"; - break; - case 28: /* 5.1 out => 2.0 out + 1 input */ - patch = "device=Line-in as=8 seq=1"; - break; - case 29: /* Can't use this as input, as the only available mic - * preamplifier is busy by front panel mic (nid 31). - * If you want to use this rear connector as mic input, - * you have to disable the front panel one. */ - patch = "as=0"; - break; - case 31: /* Lot of inputs configured with as=15 and unusable */ - patch = "as=8 seq=3"; - break; - case 32: - patch = "as=8 seq=4"; - break; - case 34: - patch = "as=8 seq=5"; - break; - case 36: - patch = "as=8 seq=6"; - break; - } - } else if (id == HDA_CODEC_ALC260 && - HDA_DEV_MATCH(SONY_S5_SUBVENDOR, sc->pci_subvendor)) { - switch (nid) { - case 16: - patch = "seq=15 device=Headphones"; - break; - } - } else if (id == HDA_CODEC_ALC268) { - if (sc->pci_subvendor == ACER_T5320_SUBVENDOR) { - switch (nid) { - case 20: /* Headphones Jack */ - patch = "as=1 seq=15"; - break; - } - } - } else if (id == HDA_CODEC_CX20561 && - sc->pci_subvendor == LENOVO_B450_SUBVENDOR) { - switch (nid) { - case 22: - patch = "as=1 seq=15"; - break; - } - } - - if (patch != NULL) - config = hdac_widget_pin_patch(config, patch); - - snprintf(buf, sizeof(buf), "cad%u.nid%u.config", cad, nid); - if (resource_string_value(device_get_name(sc->dev), - device_get_unit(sc->dev), buf, &res) == 0) { - if (strncmp(res, "0x", 2) == 0) { - config = strtol(res + 2, NULL, 16); - } else { - config = hdac_widget_pin_patch(config, res); - } - } - - HDA_BOOTVERBOSE( - if (config != orig) - device_printf(sc->dev, - "Patching pin config nid=%u 0x%08x -> 0x%08x\n", - nid, orig, config); - ); - - return (config); -} - -static uint32_t -hdac_widget_pin_getcaps(struct hdac_widget *w) -{ - struct hdac_softc *sc; - uint32_t caps, orig, id; - nid_t cad, nid; - - sc = w->devinfo->codec->sc; - cad = w->devinfo->codec->cad; - nid = w->nid; - id = hdac_codec_id(w->devinfo->codec); - - caps = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); - orig = caps; - - HDA_BOOTVERBOSE( - if (caps != orig) - device_printf(sc->dev, - "Patching pin caps nid=%u 0x%08x -> 0x%08x\n", - nid, orig, caps); - ); - - return (caps); -} - -static void -hdac_widget_pin_parse(struct hdac_widget *w) -{ - struct hdac_softc *sc = w->devinfo->codec->sc; - uint32_t config, pincap; - const char *devstr; - nid_t cad = w->devinfo->codec->cad; - nid_t nid = w->nid; - int conn, color; - - config = hdac_widget_pin_getconfig(w); - w->wclass.pin.config = config; - - pincap = hdac_widget_pin_getcaps(w); - w->wclass.pin.cap = pincap; - - w->wclass.pin.ctrl = hdac_command(sc, - HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad); - - 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; - - devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> - HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT]; - - conn = (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT; - color = (config & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> - HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT; - - strlcat(w->name, ": ", sizeof(w->name)); - strlcat(w->name, devstr, sizeof(w->name)); - strlcat(w->name, " (", sizeof(w->name)); - if (conn == 0 && color != 0 && color != 15) { - strlcat(w->name, HDA_COLORS[color], sizeof(w->name)); - strlcat(w->name, " ", sizeof(w->name)); - } - strlcat(w->name, HDA_CONNS[conn], sizeof(w->name)); - strlcat(w->name, ")", sizeof(w->name)); -} - -static uint32_t -hdac_widget_getcaps(struct hdac_widget *w, int *waspin) -{ - struct hdac_softc *sc; - uint32_t caps, orig, id; - nid_t cad, nid, beeper = -1; - - sc = w->devinfo->codec->sc; - cad = w->devinfo->codec->cad; - nid = w->nid; - id = hdac_codec_id(w->devinfo->codec); - - caps = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), - cad); - orig = caps; - - /* On some codecs beeper is an input pin, but it is not recordable - alone. Also most of BIOSes does not declare beeper pin. - Change beeper pin node type to beeper to help parser. */ - *waspin = 0; - switch (id) { - case HDA_CODEC_AD1882: - case HDA_CODEC_AD1883: - case HDA_CODEC_AD1984: - case HDA_CODEC_AD1984A: - case HDA_CODEC_AD1984B: - case HDA_CODEC_AD1987: - case HDA_CODEC_AD1988: - case HDA_CODEC_AD1988B: - case HDA_CODEC_AD1989B: - beeper = 26; - break; - case HDA_CODEC_ALC260: - beeper = 23; - break; - case HDA_CODEC_ALC262: - case HDA_CODEC_ALC268: - case HDA_CODEC_ALC880: - case HDA_CODEC_ALC882: - case HDA_CODEC_ALC883: - case HDA_CODEC_ALC885: - case HDA_CODEC_ALC888: - case HDA_CODEC_ALC889: - beeper = 29; - break; - } - if (nid == beeper) { - caps &= ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; - caps |= HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; - *waspin = 1; - } - - HDA_BOOTVERBOSE( - if (caps != orig) { - device_printf(sc->dev, - "Patching widget caps nid=%u 0x%08x -> 0x%08x\n", - nid, orig, caps); - } - ); - - return (caps); -} - -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_widget_getcaps(w, &w->waspin); - - 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)); - - 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 __inline int -hda_poll_channel(struct hdac_chan *ch) -{ - uint32_t sz, delta; - volatile uint32_t ptr; - - if (!(ch->flags & HDAC_CHN_RUNNING)) - return (0); - - sz = ch->blksz * ch->blkcnt; - if (ch->dmapos != NULL) - ptr = *(ch->dmapos); - else - ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, - ch->off + HDAC_SDLPIB); - ch->ptr = ptr; - ptr %= sz; - ptr &= ~(ch->blksz - 1); - delta = (sz + ptr - ch->prevptr) % sz; - - if (delta < ch->blksz) - return (0); - - ch->prevptr = ptr; - - return (1); -} - -static void -hda_poll_callback(void *arg) -{ - struct hdac_softc *sc = arg; - uint32_t trigger; - int i, active = 0; - - if (sc == NULL) - return; - - hdac_lock(sc); - if (sc->polling == 0) { - hdac_unlock(sc); - return; - } - - trigger = 0; - for (i = 0; i < sc->num_chans; i++) { - if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) - continue; - active = 1; - if (hda_poll_channel(&sc->chans[i])) - trigger |= (1 << i); - } - - /* XXX */ - if (active) - callout_reset(&sc->poll_hda, sc->poll_ticks, - hda_poll_callback, sc); - - hdac_unlock(sc); - - for (i = 0; i < sc->num_chans; i++) { - if (trigger & (1 << i)) - chn_intr(sc->chans[i].c); - } -} - static int hdac_rirb_flush(struct hdac_softc *sc) { struct hdac_rirb *rirb_base, *rirb; - struct hdac_codec *codec; - struct hdac_command_list *commands; nid_t cad; uint32_t resp; uint8_t rirbwp; @@ -2949,46 +883,46 @@ hdac_rirb_flush(struct hdac_softc *sc) #endif ret = 0; - while (sc->rirb_rp != rirbwp) { sc->rirb_rp++; sc->rirb_rp %= sc->rirb_size; rirb = &rirb_base[sc->rirb_rp]; cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); - if (cad < 0 || cad >= HDAC_CODEC_MAX || - sc->codecs[cad] == NULL) - continue; resp = rirb->response; - codec = sc->codecs[cad]; - commands = codec->commands; if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { - sc->unsolq[sc->unsolq_wp++] = (cad << 16) | - ((resp >> 26) & 0xffff); + sc->unsolq[sc->unsolq_wp++] = resp; sc->unsolq_wp %= HDAC_UNSOLQ_MAX; - } else if (commands != NULL && commands->num_commands > 0 && - codec->responses_received < commands->num_commands) - commands->responses[codec->responses_received++] = - resp; + sc->unsolq[sc->unsolq_wp++] = cad; + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } else if (sc->codecs[cad].pending <= 0) { + device_printf(sc->dev, "Unexpected unsolicited " + "response from address %d: %08x\n", cad, resp); + } else { + sc->codecs[cad].response = resp; + sc->codecs[cad].pending--; + } ret++; } - return (ret); } static int hdac_unsolq_flush(struct hdac_softc *sc) { + device_t child; nid_t cad; - uint32_t tag; + uint32_t resp; int ret = 0; if (sc->unsolq_st == HDAC_UNSOLQ_READY) { sc->unsolq_st = HDAC_UNSOLQ_BUSY; while (sc->unsolq_rp != sc->unsolq_wp) { - cad = sc->unsolq[sc->unsolq_rp] >> 16; - tag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + resp = sc->unsolq[sc->unsolq_rp++]; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + cad = sc->unsolq[sc->unsolq_rp++]; sc->unsolq_rp %= HDAC_UNSOLQ_MAX; - hdac_unsolicited_handler(sc->codecs[cad], tag); + if ((child = sc->codecs[cad].dev) != NULL) + HDAC_UNSOL_INTR(child, resp); ret++; } sc->unsolq_st = HDAC_UNSOLQ_READY; @@ -2997,375 +931,54 @@ hdac_unsolq_flush(struct hdac_softc *sc) return (ret); } -static void -hdac_poll_callback(void *arg) -{ - struct hdac_softc *sc = arg; - if (sc == NULL) - return; - - hdac_lock(sc); - if (sc->polling == 0 || sc->poll_ival == 0) { - hdac_unlock(sc); - return; - } - if (hdac_rirb_flush(sc) != 0) - hdac_unsolq_flush(sc); - callout_reset(&sc->poll_hdac, sc->poll_ival, hdac_poll_callback, sc); - hdac_unlock(sc); -} - -static void -hdac_poll_reinit(struct hdac_softc *sc) -{ - int i, pollticks, min = 1000000; - struct hdac_chan *ch; - - for (i = 0; i < sc->num_chans; i++) { - if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) - continue; - ch = &sc->chans[i]; - pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getalign(ch->b) * sndbuf_getspd(ch->b)); - pollticks >>= 1; - if (pollticks > hz) - pollticks = hz; - if (pollticks < 1) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%s: pollticks=%d < 1 !\n", - __func__, pollticks); - ); - pollticks = 1; - } - if (min > pollticks) - min = pollticks; - } - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%s: pollticks %d -> %d\n", - __func__, sc->poll_ticks, min); - ); - sc->poll_ticks = min; - if (min == 1000000) - callout_stop(&sc->poll_hda); - else - callout_reset(&sc->poll_hda, 1, hda_poll_callback, sc); -} - -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); - - ch->flags &= ~HDAC_CHN_RUNNING; - - if (sc->polling != 0) - hdac_poll_reinit(sc); - - 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; - - ch->flags |= HDAC_CHN_RUNNING; - - if (sc->polling != 0) - hdac_poll_reinit(sc); - - 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; - struct hdac_bdle *bdle; - uint64_t addr; - uint32_t blksz, blkcnt; - int i; - - addr = (uint64_t)sndbuf_getbufaddr(ch->b); - bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr; - - blksz = ch->blksz; - blkcnt = ch->blkcnt; - - for (i = 0; i < blkcnt; i++, bdle++) { - bdle->addrl = (uint32_t)addr; - bdle->addrh = (uint32_t)(addr >> 32); - bdle->len = blksz; - bdle->ioc = 1; - addr += blksz; - } - - HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt); - HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 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)); - if (ch->dmapos != NULL && - !(HDAC_READ_4(&sc->mem, HDAC_DPIBLBASE) & 0x00000001)) { - addr = sc->pos_dma.dma_paddr; - HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, - ((uint32_t)addr & HDAC_DPLBASE_DPLBASE_MASK) | 0x00000001); - HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, (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); - } - - 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; - - sc = ctl->widget->devinfo->codec->sc; - cad = ctl->widget->devinfo->codec->cad; - nid = ctl->widget->nid; - - /* Save new values if valid. */ - if (mute != HDA_AMP_MUTE_DEFAULT) - ctl->muted = mute; - if (left != HDA_AMP_VOL_DEFAULT) - ctl->left = left; - if (right != HDA_AMP_VOL_DEFAULT) - ctl->right = right; - /* Prepare effective values */ - if (ctl->forcemute) { - lmute = 1; - rmute = 1; - left = 0; - right = 0; - } else { - lmute = HDA_AMP_LEFT_MUTED(ctl->muted); - rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); - left = ctl->left; - right = ctl->right; - } - /* Apply effective values */ - 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); -} - -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) +hdac_send_command(struct hdac_softc *sc, nid_t cad, uint32_t verb) { - struct hdac_command_list cl; - uint32_t response = HDAC_INVALID; + int timeout; + uint32_t *corb; 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; - int timeout; - int retry = 10; - struct hdac_rirb *rirb_base; + verb &= ~HDA_CMD_CAD_MASK; + verb |= ((uint32_t)cad) << HDA_CMD_CAD_SHIFT; + sc->codecs[cad].response = HDA_INVALID; - if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL || - commands->num_commands < 1) - return; - - codec = sc->codecs[cad]; - codec->commands = commands; - codec->responses_received = 0; - codec->verbs_sent = 0; + sc->codecs[cad].pending++; + sc->corb_wp++; + sc->corb_wp %= sc->corb_size; 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); #if 0 - bus_dmamap_sync(sc->corb_dma.dma_tag, - sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); #endif - 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 */ + corb[sc->corb_wp] = verb; #if 0 - bus_dmamap_sync(sc->corb_dma.dma_tag, - sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); #endif - HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); - } + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); - timeout = 1000; - while (hdac_rirb_flush(sc) == 0 && --timeout) + timeout = 10000; + do { + if (hdac_rirb_flush(sc) == 0) DELAY(10); - } 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); + } while (sc->codecs[cad].pending != 0 && --timeout); - codec->commands = NULL; - codec->responses_received = 0; - codec->verbs_sent = 0; + if (sc->codecs[cad].pending != 0) { + device_printf(sc->dev, "Command timeout on address %d\n", cad); + sc->codecs[cad].pending = 0; + } - hdac_unsolq_flush(sc); + if (sc->unsolq_rp != sc->unsolq_wp) + taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); + return (sc->codecs[cad].response); } - /**************************************************************************** * Device Methods ****************************************************************************/ @@ -3411,7 +1024,7 @@ hdac_probe(device_t dev) result = BUS_PROBE_GENERIC; } if (result != ENXIO) { - strlcat(desc, " High Definition Audio Controller", + strlcat(desc, " HDA Controller", sizeof(desc)); device_set_desc_copy(dev, desc); } @@ -3419,711 +1032,6 @@ hdac_probe(device_t dev) return (result); } -static void * -hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, - struct pcm_channel *c, int dir) -{ - struct hdac_pcm_devinfo *pdevinfo = data; - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_chan *ch; - int i, ord = 0, chid; - - hdac_lock(sc); - - chid = (dir == PCMDIR_PLAY)?pdevinfo->play:pdevinfo->rec; - ch = &sc->chans[chid]; - for (i = 0; i < sc->num_chans && i < chid; i++) { - if (ch->dir == sc->chans[i].dir) - ord++; - } - if (dir == PCMDIR_PLAY) { - ch->off = (sc->num_iss + ord) << 5; - } else { - ch->off = ord << 5; - } - - if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { - ch->caps.minspeed = ch->caps.maxspeed = 48000; - ch->pcmrates[0] = 48000; - ch->pcmrates[1] = 0; - } - if (sc->pos_dma.dma_vaddr != NULL) - ch->dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + - (sc->streamcnt * 8)); - else - ch->dmapos = NULL; - ch->sid = ++sc->streamcnt; - ch->dir = dir; - ch->b = b; - ch->c = c; - ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt; - ch->blkcnt = pdevinfo->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->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0, - pdevinfo->chan_size) != 0) - return (NULL); - - 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 uint32_t -hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) -{ - struct hdac_chan *ch = data; - uint32_t spd = 0, threshold; - int i; - - for (i = 0; ch->pcmrates[i] != 0; i++) { - spd = ch->pcmrates[i]; - threshold = spd + ((ch->pcmrates[i + 1] != 0) ? - ((ch->pcmrates[i + 1] - spd) >> 1) : 0); - if (speed < threshold) - break; - } - - if (spd == 0) /* impossible */ - 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; - struct hdac_audio_as *as = &ch->devinfo->function.audio.as[ch->as]; - struct hdac_widget *w; - int i, chn, totalchn, c; - nid_t cad = ch->devinfo->codec->cad; - uint16_t fmt, dfmt; - uint16_t chmap[2][5] = {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x0231 }, /* 5.1 */ - { 0x0010, 0x0001, 0x2001, 0x2031, 0x2431 }};/* 7.1 */ - int map = -1; - - totalchn = AFMT_CHANNEL(ch->fmt); - HDA_BOOTHVERBOSE( - device_printf(ch->pdevinfo->dev, - "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->fmt, ch->spd); - ); - 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; - } - } - fmt |= (totalchn - 1); - - /* Set channel mapping for known speaker setups. */ - if (as->pinset == 0x0007 || as->pinset == 0x0013) /* Standard 5.1 */ - map = 0; - else if (as->pinset == 0x0017) /* Standard 7.1 */ - map = 1; - - HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); - - dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; - if (ch->fmt & AFMT_AC3) - dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO; - - chn = 0; - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(ch->devinfo, ch->io[i]); - if (w == NULL) - continue; - - /* If HP redirection is enabled, but failed to use same - DAC, make last DAC to duplicate first one. */ - if (as->fakeredir && i == (as->pincnt - 1)) { - c = (ch->sid << 4); - } else { - if (map >= 0) /* Map known speaker setups. */ - chn = (((chmap[map][totalchn / 2] >> i * 4) & - 0xf) - 1) * 2; - if (chn < 0 || chn >= totalchn) { - c = 0; - } else { - c = (ch->sid << 4) | chn; - } - } - HDA_BOOTHVERBOSE( - device_printf(ch->pdevinfo->dev, - "PCMDIR_%s: Stream setup nid=%d: " - "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x\n", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->io[i], fmt, dfmt, c); - ); - hdac_command(sc, - HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { - hdac_command(sc, - HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], dfmt), - cad); - } - hdac_command(sc, - HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], c), cad); -#if 0 - hdac_command(sc, - HDA_CMD_SET_CONV_CHAN_COUNT(cad, ch->io[i], 1), cad); - hdac_command(sc, - HDA_CMD_SET_HDMI_CHAN_SLOT(cad, ch->io[i], 0x00), cad); - hdac_command(sc, - HDA_CMD_SET_HDMI_CHAN_SLOT(cad, ch->io[i], 0x11), cad); -#endif - chn += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; - } -} - -/* - * Greatest Common Divisor. - */ -static unsigned -gcd(unsigned a, unsigned b) -{ - u_int c; - - while (b != 0) { - c = a; - a = b; - b = (c % b); - } - return (a); -} - -/* - * Least Common Multiple. - */ -static unsigned -lcm(unsigned a, unsigned b) -{ - - return ((a * b) / gcd(a, b)); -} - -static int -hdac_channel_setfragments(kobj_t obj, void *data, - uint32_t blksz, uint32_t blkcnt) -{ - struct hdac_chan *ch = data; - struct hdac_softc *sc = ch->devinfo->codec->sc; - - blksz -= blksz % lcm(HDAC_DMA_ALIGNMENT, sndbuf_getalign(ch->b)); - - if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) - blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; - if (blksz < HDA_BLK_MIN) - blksz = HDA_BLK_MIN; - if (blkcnt > HDA_BDL_MAX) - blkcnt = HDA_BDL_MAX; - if (blkcnt < HDA_BDL_MIN) - blkcnt = HDA_BDL_MIN; - - while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { - if ((blkcnt >> 1) >= HDA_BDL_MIN) - blkcnt >>= 1; - else if ((blksz >> 1) >= HDA_BLK_MIN) - blksz >>= 1; - else - break; - } - - if ((sndbuf_getblksz(ch->b) != blksz || - sndbuf_getblkcnt(ch->b) != blkcnt) && - sndbuf_resize(ch->b, blkcnt, blksz) != 0) - device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", - __func__, blksz, blkcnt); - - ch->blksz = sndbuf_getblksz(ch->b); - ch->blkcnt = sndbuf_getblkcnt(ch->b); - - return (0); -} - -static uint32_t -hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) -{ - struct hdac_chan *ch = data; - - hdac_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt); - - return (ch->blksz); -} - -static void -hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) -{ - struct hdac_devinfo *devinfo = ch->devinfo; - struct hdac_widget *w; - nid_t cad = devinfo->codec->cad; - int i; - - hdac_stream_stop(ch); - - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(ch->devinfo, ch->io[i]); - if (w == NULL) - continue; - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { - hdac_command(sc, - HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], 0), - cad); - } - 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; - - if (!PCMTRIG_COMMON(go)) - return (0); - - 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; - default: - break; - } - hdac_unlock(sc); - - return (0); -} - -static uint32_t -hdac_channel_getptr(kobj_t obj, void *data) -{ - struct hdac_chan *ch = data; - struct hdac_softc *sc = ch->devinfo->codec->sc; - uint32_t ptr; - - hdac_lock(sc); - if (sc->polling != 0) - ptr = ch->ptr; - else if (ch->dmapos != NULL) - ptr = *(ch->dmapos); - else - ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); - hdac_unlock(sc); - - /* - * Round to available space and force 128 bytes aligment. - */ - ptr %= ch->blksz * ch->blkcnt; - ptr &= HDA_BLK_ALIGN; - - 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_setfragments, hdac_channel_setfragments), - KOBJMETHOD(channel_trigger, hdac_channel_trigger), - KOBJMETHOD(channel_getptr, hdac_channel_getptr), - KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), - KOBJMETHOD_END -}; -CHANNEL_DECLARE(hdac_channel); - -static int -hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) -{ - struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - 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; - - hdac_lock(sc); - - /* Make sure that in case of soft volume it won't stay muted. */ - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - pdevinfo->left[i] = 100; - pdevinfo->right[i] = 100; - } - - mask = 0; - recmask = 0; - id = hdac_codec_id(devinfo->codec); - - /* Declate EAPD as ogain control. */ - if (pdevinfo->play >= 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->param.eapdbtl == HDAC_INVALID || - w->bindas != sc->chans[pdevinfo->play].as) - continue; - mask |= SOUND_MASK_OGAIN; - break; - } - } - - /* Declare volume controls assigned to this association. */ - i = 0; - ctl = NULL; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if ((pdevinfo->play >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->play].as) || - (pdevinfo->rec >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || - (ctl->widget->bindas == -2 && pdevinfo->index == 0)) - mask |= ctl->ossmask; - } - - /* Declare record sources available to this association. */ - if (pdevinfo->rec >= 0) { - struct hdac_chan *ch = &sc->chans[pdevinfo->rec]; - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(devinfo, ch->io[i]); - if (w == NULL || w->enable == 0) - continue; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - if (cw->bindas != sc->chans[pdevinfo->rec].as && - cw->bindas != -2) - continue; - recmask |= cw->ossmask; - } - } - } - - /* Declare soft PCM volume if needed. */ - if (pdevinfo->play >= 0) { - ctl = NULL; - if ((mask & SOUND_MASK_PCM) == 0 || - (devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) { - softpcmvol = 1; - mask |= SOUND_MASK_PCM; - } else { - softpcmvol = 0; - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if (ctl->widget->bindas != sc->chans[pdevinfo->play].as && - (ctl->widget->bindas != -2 || pdevinfo->index != 0)) - continue; - if (!(ctl->ossmask & SOUND_MASK_PCM)) - continue; - if (ctl->step > 0) - break; - } - } - - if (softpcmvol == 1 || ctl == NULL) { - pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); - HDA_BOOTVERBOSE( - device_printf(pdevinfo->dev, - "%s Soft PCM volume\n", - (softpcmvol == 1) ? "Forcing" : "Enabling"); - ); - } - } - - /* Declare master volume if needed. */ - if (pdevinfo->play >= 0) { - if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) == - SOUND_MASK_PCM) { - mask |= SOUND_MASK_VOLUME; - mix_setparentchild(m, SOUND_MIXER_VOLUME, - SOUND_MASK_PCM); - mix_setrealdev(m, SOUND_MIXER_VOLUME, - SOUND_MIXER_NONE); - HDA_BOOTVERBOSE( - device_printf(pdevinfo->dev, - "Forcing master volume with PCM\n"); - ); - } - } - - recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; - mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; - - 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_pcm_devinfo *pdevinfo = mix_getdevinfo(m); - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_widget *w; - struct hdac_audio_ctl *ctl; - uint32_t mute; - int lvol, rvol; - int i, j; - - hdac_lock(sc); - /* Save new values. */ - pdevinfo->left[dev] = left; - pdevinfo->right[dev] = right; - - /* 'ogain' is the special case implemented with EAPD. */ - if (dev == SOUND_MIXER_OGAIN) { - uint32_t orig; - w = NULL; - 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->param.eapdbtl == HDAC_INVALID) - continue; - break; - } - if (i >= devinfo->endnode) { - 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; - - 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)); - } - - /* Recalculate all controls related to this OSS device. */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || - !(ctl->ossmask & (1 << dev))) - continue; - if (!((pdevinfo->play >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->play].as) || - (pdevinfo->rec >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || - ctl->widget->bindas == -2)) - continue; - - lvol = 100; - rvol = 100; - for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { - if (ctl->ossmask & (1 << j)) { - lvol = lvol * pdevinfo->left[j] / 100; - rvol = rvol * pdevinfo->right[j] / 100; - } - } - mute = (lvol == 0) ? HDA_AMP_MUTE_LEFT : 0; - mute |= (rvol == 0) ? HDA_AMP_MUTE_RIGHT : 0; - lvol = (lvol * ctl->step + 50) / 100; - rvol = (rvol * ctl->step + 50) / 100; - hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); - } - hdac_unlock(sc); - - return (left | (right << 8)); -} - -/* - * Commutate specified record source. - */ -static uint32_t -hdac_audio_ctl_recsel_comm(struct hdac_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - char buf[64]; - int i, muted; - uint32_t res = 0; - - if (depth > HDA_PARSE_MAXDEPTH) - return (0); - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - - for (i = 0; i < w->nconns; i++) { - if (w->connsenable[i] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[i]); - if (cw == NULL || cw->enable == 0 || cw->bindas == -1) - continue; - /* Call recursively to trace signal to it's source if needed. */ - if ((src & cw->ossmask) != 0) { - if (cw->ossdev < 0) { - res |= hdac_audio_ctl_recsel_comm(pdevinfo, src, - w->conns[i], depth + 1); - } else { - res |= cw->ossmask; - } - } - /* We have two special cases: mixers and others (selectors). */ - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { - ctl = hdac_audio_ctl_amp_get(devinfo, - w->nid, HDA_CTL_IN, i, 1); - if (ctl == NULL) - continue; - /* If we have input control on this node mute them - * according to requested sources. */ - muted = (src & cw->ossmask) ? 0 : 1; - if (muted != ctl->forcemute) { - ctl->forcemute = muted; - hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, - HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); - } - HDA_BOOTHVERBOSE( - device_printf(pdevinfo->dev, - "Recsel (%s): nid %d source %d %s\n", - hdac_audio_ctl_ossmixer_mask2allname( - src, buf, sizeof(buf)), - nid, i, muted?"mute":"unmute"); - ); - } else { - if (w->nconns == 1) - break; - if ((src & cw->ossmask) == 0) - continue; - /* If we found requested source - select it and exit. */ - hdac_widget_connection_select(w, i); - HDA_BOOTHVERBOSE( - device_printf(pdevinfo->dev, - "Recsel (%s): nid %d source %d select\n", - hdac_audio_ctl_ossmixer_mask2allname( - src, buf, sizeof(buf)), - nid, i); - ); - break; - } - } - return (res); -} - -static uint32_t -hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) -{ - struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_chan *ch; - int i; - uint32_t ret = 0xffffffff; - - hdac_lock(sc); - - /* Commutate requested recsrc for each ADC. */ - ch = &sc->chans[pdevinfo->rec]; - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(devinfo, ch->io[i]); - if (w == NULL || w->enable == 0) - continue; - ret &= hdac_audio_ctl_recsel_comm(pdevinfo, src, ch->io[i], 0); - } - - hdac_unlock(sc); - - return ((ret == 0xffffffff)? 0 : 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), - KOBJMETHOD_END -}; -MIXER_DECLARE(hdac_audio_ctl_ossmixer); - static void hdac_unsolq_task(void *context, int pending) { @@ -4154,6 +1062,7 @@ hdac_attach(device_t dev) uint16_t vendor; uint8_t v; + sc = device_get_softc(dev); HDA_BOOTVERBOSE( device_printf(dev, "HDA Driver Revision: %s\n", HDA_DRV_TEST_REV); @@ -4177,38 +1086,44 @@ hdac_attach(device_t dev) } } - sc = device_get_softc(dev); - sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "HDA driver mutex"); 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; - vendor = pci_get_vendor(dev); - - if (sc->pci_subvendor == HP_NX6325_SUBVENDORX) { - /* Screw nx6325 - subdevice/subvendor swapped */ - sc->pci_subvendor = HP_NX6325_SUBVENDOR; - } - - callout_init(&sc->poll_hda, CALLOUT_MPSAFE); - callout_init(&sc->poll_hdac, CALLOUT_MPSAFE); - callout_init(&sc->poll_jack, CALLOUT_MPSAFE); - TASK_INIT(&sc->unsolq_task, 0, hdac_unsolq_task, sc); - - sc->poll_ticks = 1000000; - sc->poll_ival = HDAC_POLL_INTERVAL; + callout_init(&sc->poll_callout, CALLOUT_MPSAFE); + for (i = 0; i < HDAC_CODEC_MAX; i++) + sc->codecs[i].dev = NULL; + if (devid >= 0) { + sc->quirks_on = hdac_devices[devid].quirks_on; + sc->quirks_off = hdac_devices[devid].quirks_off; + } else { + sc->quirks_on = 0; + sc->quirks_off = 0; + } + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "msi", &i) == 0) { + if (i == 0) + sc->quirks_off |= HDAC_QUIRK_MSI; + else { + sc->quirks_on |= HDAC_QUIRK_MSI; + sc->quirks_off |= ~HDAC_QUIRK_MSI; + } + } + hdac_config_fetch(sc, &sc->quirks_on, &sc->quirks_off); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "Config options: on=0x%08x off=0x%08x\n", + sc->quirks_on, sc->quirks_off); + ); + sc->poll_ival = hz; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "polling", &i) == 0 && i != 0) sc->polling = 1; else sc->polling = 0; - sc->hdabus = NULL; - for (i = 0; i < HDAC_CODEC_MAX; i++) - sc->codecs[i] = NULL; - pci_enable_busmaster(dev); + vendor = pci_get_vendor(dev); if (vendor == INTEL_VENDORID) { /* TCSEL -> TC0 */ v = pci_read_config(dev, 0x44, 1); @@ -4219,18 +1134,6 @@ hdac_attach(device_t dev) ); } - if (devid >= 0 && (hdac_devices[devid].flags & HDAC_NO_MSI)) - sc->flags &= ~HDAC_F_MSI; - else - sc->flags |= HDAC_F_MSI; - if (resource_int_value(device_get_name(dev), - device_get_unit(dev), "msi", &i) == 0) { - if (i == 0) - sc->flags &= ~HDAC_F_MSI; - else - sc->flags |= HDAC_F_MSI; - } - #if defined(__i386__) || defined(__amd64__) sc->flags |= HDAC_F_DMA_NOCACHE; @@ -4299,10 +1202,7 @@ hdac_attach(device_t dev) if (result != 0) goto hdac_attach_fail; - if (devid >= 0 && (hdac_devices[devid].flags & HDAC_NO_64BIT)) - sc->support_64bit = 0; - - /* Allocate CORB and RIRB dma memory */ + /* Allocate CORB, RIRB, POS and BDLs dma memory */ result = hdac_dma_alloc(sc, &sc->corb_dma, sc->corb_size * sizeof(uint32_t)); if (result != 0) @@ -4311,10 +1211,34 @@ hdac_attach(device_t dev) sc->rirb_size * sizeof(struct hdac_rirb)); if (result != 0) goto hdac_attach_fail; + sc->streams = malloc(sizeof(struct hdac_stream) * sc->num_ss, + M_HDAC, M_ZERO | M_WAITOK); + for (i = 0; i < sc->num_ss; i++) { + result = hdac_dma_alloc(sc, &sc->streams[i].bdl, + sizeof(struct hdac_bdle) * HDA_BDL_MAX); + if (result != 0) + goto hdac_attach_fail; + } + if (sc->quirks_on & HDAC_QUIRK_DMAPOS) { + if (hdac_dma_alloc(sc, &sc->pos_dma, (sc->num_ss) * 8) != 0) { + HDA_BOOTVERBOSE( + device_printf(dev, "Failed to " + "allocate DMA pos buffer " + "(non-fatal)\n"); + ); + } else { + uint64_t addr = sc->pos_dma.dma_paddr; + + HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, addr >> 32); + HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, + (addr & HDAC_DPLBASE_DPLBASE_MASK) | + HDAC_DPLBASE_DPLBASE_DMAPBE); + } + } result = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ - HDAC_DMA_ALIGNMENT, /* alignment */ + HDA_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ @@ -4356,6 +1280,9 @@ hdac_attach(device_t dev) hdac_attach_fail: hdac_irq_free(sc); + for (i = 0; i < sc->num_ss; i++) + hdac_dma_free(sc, &sc->streams[i].bdl); + free(sc->streams, M_HDAC); hdac_dma_free(sc, &sc->rirb_dma); hdac_dma_free(sc, &sc->corb_dma); hdac_mem_free(sc); @@ -4364,3127 +1291,97 @@ hdac_attach_fail: return (ENXIO); } -static void -hdac_audio_parse(struct hdac_devinfo *devinfo) -{ - struct hdac_codec *codec = devinfo->codec; - struct hdac_softc *sc = codec->sc; - struct hdac_widget *w; - uint32_t res; - int i; - nid_t cad, nid; - - cad = devinfo->codec->cad; - nid = devinfo->nid; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_GPIO_COUNT), cad); - devinfo->function.audio.gpio = res; - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "GPIO: 0x%08x " - "NumGPIO=%d NumGPO=%d " - "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", - devinfo->function.audio.gpio, - HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); - ); - - 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; - - 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->ossdev = -1; - w->bindas = -1; - 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; - - ctls = (struct hdac_audio_ctl *)malloc( - sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); - - 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, - "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; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || - w->waspin) - ctls[cnt].ndir = HDA_CTL_IN; - else - ctls[cnt].ndir = HDA_CTL_OUT; - 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, - "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].ndir = HDA_CTL_IN; - 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; - if (w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - ctls[cnt].ndir = HDA_CTL_OUT; - else - ctls[cnt].ndir = HDA_CTL_IN; - ctls[cnt++].dir = HDA_CTL_IN; - break; - } - } - } - - devinfo->function.audio.ctl = ctls; -} - -static void -hdac_audio_as_parse(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as; - struct hdac_widget *w; - int i, j, cnt, max, type, dir, assoc, seq, first, hpredir; - - /* Count present associations */ - max = 0; - for (j = 1; j < 16; j++) { - 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_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config) - != j) - continue; - max++; - if (j != 15) /* There could be many 1-pin assocs #15 */ - break; - } - } - - devinfo->function.audio.ascnt = max; - - if (max < 1) - return; - - as = (struct hdac_audio_as *)malloc( - sizeof(*as) * max, M_HDAC, M_ZERO | M_NOWAIT); - - if (as == NULL) { - /* Blekh! */ - device_printf(sc->dev, "unable to allocate assocs!\n"); - devinfo->function.audio.ascnt = 0; - return; - } - - for (i = 0; i < max; i++) { - as[i].hpredir = -1; - as[i].chan = -1; - as[i].digital = 0; - } - - /* Scan associations skipping as=0. */ - cnt = 0; - for (j = 1; j < 16; j++) { - first = 16; - hpredir = 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) - continue; - assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config); - seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config); - if (assoc != j) { - continue; - } - KASSERT(cnt < max, - ("%s: Associations owerflow (%d of %d)", - __func__, cnt, max)); - type = w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - /* Get pin direction. */ - if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT) - dir = HDA_CTL_OUT; - else - dir = HDA_CTL_IN; - /* If this is a first pin - create new association. */ - if (as[cnt].pincnt == 0) { - as[cnt].enable = 1; - as[cnt].index = j; - as[cnt].dir = dir; - } - if (seq < first) - first = seq; - /* Check association correctness. */ - if (as[cnt].pins[seq] != 0) { - device_printf(sc->dev, "%s: Duplicate pin %d (%d) " - "in association %d! Disabling association.\n", - __func__, seq, w->nid, j); - as[cnt].enable = 0; - } - if (dir != as[cnt].dir) { - device_printf(sc->dev, "%s: Pin %d has wrong " - "direction for association %d! Disabling " - "association.\n", - __func__, w->nid, j); - as[cnt].enable = 0; - } - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { - if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap)) - as[cnt].digital = 3; - else if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) - as[cnt].digital = 2; - else - as[cnt].digital = 1; - } - /* Headphones with seq=15 may mean redirection. */ - if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT && - seq == 15) - hpredir = 1; - as[cnt].pins[seq] = w->nid; - as[cnt].pincnt++; - /* Association 15 is a multiple unassociated pins. */ - if (j == 15) - cnt++; - } - if (j != 15 && as[cnt].pincnt > 0) { - if (hpredir && as[cnt].pincnt > 1) - as[cnt].hpredir = first; - cnt++; - } - } - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%d associations found:\n", max); - for (i = 0; i < max; i++) { - device_printf(sc->dev, - "Association %d (%d) %s%s:\n", - i, as[i].index, (as[i].dir == HDA_CTL_IN)?"in":"out", - as[i].enable?"":" (disabled)"); - for (j = 0; j < 16; j++) { - if (as[i].pins[j] == 0) - continue; - device_printf(sc->dev, - " Pin nid=%d seq=%d\n", - as[i].pins[j], j); - } - } - ); - - devinfo->function.audio.as = as; -} - -static const struct { - uint32_t model; - uint32_t id; - uint32_t set, unset; -} hdac_quirks[] = { - /* - * 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_FORCESTEREO | HDA_QUIRK_IVREF, 0 }, - { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_G2K_SUBVENDOR, HDA_CODEC_ALC660, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV, 0 }, - { ASUS_A8X_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV, 0 }, - { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, - HDA_QUIRK_OVREF, 0 }, - { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, - HDA_QUIRK_OVREF, 0 }, - /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, - HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 },*/ - { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, - HDA_QUIRK_GPIO1, 0 }, - { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV | HDA_QUIRK_SENSEINV, 0 }, - { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV, 0 }, - { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, - HDA_QUIRK_GPIO0 | HDA_QUIRK_OVREF50, 0}, - { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, - HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0 }, - { APPLE_MACBOOKPRO55, HDA_CODEC_CS4206, - HDA_QUIRK_GPIO1 | HDA_QUIRK_GPIO3, 0 }, - { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205X, - HDA_QUIRK_GPIO0, 0 }, - { DELL_V1400_SUBVENDOR, HDA_CODEC_STAC9228X, - HDA_QUIRK_GPIO2, 0 }, - { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205X, - HDA_QUIRK_GPIO0, 0 }, - { HDA_MATCH_ALL, HDA_CODEC_AD1988, - HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, - { HDA_MATCH_ALL, HDA_CODEC_AD1988B, - HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, - { HDA_MATCH_ALL, HDA_CODEC_CX20549, - 0, HDA_QUIRK_FORCESTEREO } -}; -#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->codec); - 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_AD1983: - /* - * This codec has several possible usages, but none - * fit the parser best. Help parser to choose better. - */ - /* Disable direct unmixed playback to get pcm volume. */ - w = hdac_widget_get(devinfo, 5); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 6); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 11); - if (w != NULL) - w->connsenable[0] = 0; - /* Disable mic and line selectors. */ - w = hdac_widget_get(devinfo, 12); - if (w != NULL) - w->connsenable[1] = 0; - w = hdac_widget_get(devinfo, 13); - if (w != NULL) - w->connsenable[1] = 0; - /* Disable recording from mono playback mix. */ - w = hdac_widget_get(devinfo, 20); - if (w != NULL) - w->connsenable[3] = 0; - break; - case HDA_CODEC_AD1986A: - /* - * This codec has overcomplicated input mixing. - * Make some cleaning there. - */ - /* Disable input mono mixer. Not needed and not supported. */ - w = hdac_widget_get(devinfo, 43); - if (w != NULL) - w->enable = 0; - /* Disable any with any input mixing mesh. Use separately. */ - w = hdac_widget_get(devinfo, 39); - if (w != NULL) - w->enable = 0; - w = hdac_widget_get(devinfo, 40); - if (w != NULL) - w->enable = 0; - w = hdac_widget_get(devinfo, 41); - if (w != NULL) - w->enable = 0; - w = hdac_widget_get(devinfo, 42); - if (w != NULL) - w->enable = 0; - /* Disable duplicate mixer node connector. */ - w = hdac_widget_get(devinfo, 15); - if (w != NULL) - w->connsenable[3] = 0; - /* There is only one mic preamplifier, use it effectively. */ - w = hdac_widget_get(devinfo, 31); - if (w != NULL) { - if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == - HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { - w = hdac_widget_get(devinfo, 16); - if (w != NULL) - w->connsenable[2] = 0; - } else { - w = hdac_widget_get(devinfo, 15); - if (w != NULL) - w->connsenable[0] = 0; - } - } - w = hdac_widget_get(devinfo, 32); - if (w != NULL) { - if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == - HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { - w = hdac_widget_get(devinfo, 16); - if (w != NULL) - w->connsenable[0] = 0; - } else { - w = hdac_widget_get(devinfo, 15); - if (w != NULL) - w->connsenable[1] = 0; - } - } - - if (subvendor == ASUS_A8X_SUBVENDOR) { - /* - * This is just plain ridiculous.. There - * are several A8 series that share the same - * pci id but works differently (EAPD). - */ - w = hdac_widget_get(devinfo, 26); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - (w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) != - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) - devinfo->function.audio.quirks &= - ~HDA_QUIRK_EAPDINV; - } - break; - case HDA_CODEC_AD1981HD: - /* - * This codec has very unusual design with several - * points inappropriate for the present parser. - */ - /* Disable recording from mono playback mix. */ - w = hdac_widget_get(devinfo, 21); - if (w != NULL) - w->connsenable[3] = 0; - /* Disable rear to front mic mixer, use separately. */ - w = hdac_widget_get(devinfo, 31); - if (w != NULL) - w->enable = 0; - /* Disable direct playback, use mixer. */ - w = hdac_widget_get(devinfo, 5); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 6); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 9); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 24); - if (w != NULL) - w->connsenable[0] = 0; - break; - case HDA_CODEC_CX20582: - case HDA_CODEC_CX20583: - case HDA_CODEC_CX20584: - case HDA_CODEC_CX20585: - case HDA_CODEC_CX20590: - /* - * These codecs have extra connectivity on record side - * too reach for the present parser. - */ - w = hdac_widget_get(devinfo, 20); - if (w != NULL) - w->connsenable[1] = 0; - w = hdac_widget_get(devinfo, 21); - if (w != NULL) - w->connsenable[1] = 0; - w = hdac_widget_get(devinfo, 22); - if (w != NULL) - w->connsenable[0] = 0; - break; - case HDA_CODEC_VT1708S_0: - case HDA_CODEC_VT1708S_1: - case HDA_CODEC_VT1708S_2: - case HDA_CODEC_VT1708S_3: - case HDA_CODEC_VT1708S_4: - case HDA_CODEC_VT1708S_5: - case HDA_CODEC_VT1708S_6: - case HDA_CODEC_VT1708S_7: - /* - * These codecs have hidden mic boost controls. - */ - w = hdac_widget_get(devinfo, 26); - if (w != NULL) - w->param.inamp_cap = - (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | - (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | - (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); - w = hdac_widget_get(devinfo, 30); - if (w != NULL) - w->param.inamp_cap = - (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | - (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | - (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); - break; - } -} - -/* - * Trace path from DAC to pin. - */ -static nid_t -hdac_audio_trace_dac(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, - int dupseq, int min, int only, int depth) -{ - struct hdac_widget *w; - int i, im = -1; - nid_t m = 0, ret; - - if (depth > HDA_PARSE_MAXDEPTH) - return (0); - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*stracing via nid %d\n", - depth + 1, "", w->nid); - } - ); - /* Use only unused widgets */ - if (w->bindas >= 0 && w->bindas != as) { - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by association %d\n", - depth + 1, "", w->nid, w->bindas); - } - ); - return (0); - } - if (dupseq < 0) { - if (w->bindseqmask != 0) { - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by seqmask %x\n", - depth + 1, "", w->nid, w->bindseqmask); - } - ); - return (0); - } - } else { - /* If this is headphones - allow duplicate first pin. */ - if (w->bindseqmask != 0 && - (w->bindseqmask & (1 << dupseq)) == 0) { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by seqmask %x\n", - depth + 1, "", w->nid, w->bindseqmask); - ); - return (0); - } - } - - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - /* Do not traverse input. AD1988 has digital monitor - for which we are not ready. */ - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: - /* If we are tracing HP take only dac of first pin. */ - if ((only == 0 || only == w->nid) && - (w->nid >= min) && (dupseq < 0 || w->nid == - devinfo->function.audio.as[as].dacs[dupseq])) - m = w->nid; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (depth > 0) - break; - /* Fall */ - default: - /* Find reachable DACs with smallest nid respecting constraints. */ - for (i = 0; i < w->nconns; i++) { - if (w->connsenable[i] == 0) - continue; - if (w->selconn != -1 && w->selconn != i) - continue; - if ((ret = hdac_audio_trace_dac(devinfo, as, seq, - w->conns[i], dupseq, min, only, depth + 1)) != 0) { - if (m == 0 || ret < m) { - m = ret; - im = i; - } - if (only || dupseq >= 0) - break; - } - } - if (m && only && ((w->nconns > 1 && - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) - w->selconn = im; - break; - } - if (m && only) { - w->bindas = as; - w->bindseqmask |= (1 << seq); - } - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*snid %d returned %d\n", - depth + 1, "", w->nid, m); - } - ); - return (m); -} - -/* - * Trace path from widget to ADC. - */ -static nid_t -hdac_audio_trace_adc(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, - int only, int depth) -{ - struct hdac_widget *w, *wc; - int i, j; - nid_t res = 0; - - if (depth > HDA_PARSE_MAXDEPTH) - return (0); - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*stracing via nid %d\n", - depth + 1, "", w->nid); - ); - /* Use only unused widgets */ - if (w->bindas >= 0 && w->bindas != as) { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by association %d\n", - depth + 1, "", w->nid, w->bindas); - ); - return (0); - } - - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - /* If we are tracing HP take only dac of first pin. */ - if (only == w->nid) - res = 1; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (depth > 0) - break; - /* Fall */ - default: - /* Try to find reachable ADCs with specified nid. */ - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - wc = hdac_widget_get(devinfo, j); - if (wc == NULL || wc->enable == 0) - continue; - for (i = 0; i < wc->nconns; i++) { - if (wc->connsenable[i] == 0) - continue; - if (wc->conns[i] != nid) - continue; - if (hdac_audio_trace_adc(devinfo, as, seq, - j, only, depth + 1) != 0) { - res = 1; - if (((wc->nconns > 1 && - wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || - wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) && - wc->selconn == -1) - wc->selconn = i; - } - } - } - break; - } - if (res) { - w->bindas = as; - w->bindseqmask |= (1 << seq); - } - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d returned %d\n", - depth + 1, "", w->nid, res); - ); - return (res); -} - -/* - * Erase trace path of the specified association. - */ -static void -hdac_audio_undo_trace(struct hdac_devinfo *devinfo, int as, int seq) -{ - struct hdac_widget *w; - int i; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->bindas == as) { - if (seq >= 0) { - w->bindseqmask &= ~(1 << seq); - if (w->bindseqmask == 0) { - w->bindas = -1; - w->selconn = -1; - } - } else { - w->bindas = -1; - w->bindseqmask = 0; - w->selconn = -1; - } - } - } -} - -/* - * Trace association path from DAC to output - */ -static int -hdac_audio_trace_as_out(struct hdac_devinfo *devinfo, int as, int seq) -{ - struct hdac_audio_as *ases = devinfo->function.audio.as; - int i, hpredir; - nid_t min, res; - - /* Find next pin */ - for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) - ; - /* Check if there is no any left. If so - we succeeded. */ - if (i == 16) - return (1); - - hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1; - min = 0; - res = 0; - do { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing pin %d with min nid %d", - ases[as].pins[i], min); - if (hpredir >= 0) - printf(" and hpredir %d", hpredir); - printf("\n"); - ); - /* Trace this pin taking min nid into account. */ - res = hdac_audio_trace_dac(devinfo, as, i, - ases[as].pins[i], hpredir, min, 0, 0); - if (res == 0) { - /* If we failed - return to previous and redo it. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Unable to trace pin %d seq %d with min " - "nid %d", - ases[as].pins[i], i, min); - if (hpredir >= 0) - printf(" and hpredir %d", hpredir); - printf("\n"); - ); - return (0); - } - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Pin %d traced to DAC %d", - ases[as].pins[i], res); - if (hpredir >= 0) - printf(" and hpredir %d", hpredir); - if (ases[as].fakeredir) - printf(" with fake redirection"); - printf("\n"); - ); - /* Trace again to mark the path */ - hdac_audio_trace_dac(devinfo, as, i, - ases[as].pins[i], hpredir, min, res, 0); - ases[as].dacs[i] = res; - /* We succeeded, so call next. */ - if (hdac_audio_trace_as_out(devinfo, as, i + 1)) - return (1); - /* If next failed, we should retry with next min */ - hdac_audio_undo_trace(devinfo, as, i); - ases[as].dacs[i] = 0; - min = res + 1; - } while (1); -} - -/* - * Trace association path from input to ADC - */ static int -hdac_audio_trace_as_in(struct hdac_devinfo *devinfo, int as) +sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) { - struct hdac_audio_as *ases = devinfo->function.audio.as; - struct hdac_widget *w; - int i, j, k; - - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) - continue; - if (w->bindas >= 0 && w->bindas != as) - continue; - - /* Find next pin */ - for (i = 0; i < 16; i++) { - if (ases[as].pins[i] == 0) - continue; - - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing pin %d to ADC %d\n", - ases[as].pins[i], j); - ); - /* Trace this pin taking goal into account. */ - if (hdac_audio_trace_adc(devinfo, as, i, - ases[as].pins[i], j, 0) == 0) { - /* If we failed - return to previous and redo it. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Unable to trace pin %d to ADC %d, undo traces\n", - ases[as].pins[i], j); - ); - hdac_audio_undo_trace(devinfo, as, -1); - for (k = 0; k < 16; k++) - ases[as].dacs[k] = 0; - break; - } - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Pin %d traced to ADC %d\n", - ases[as].pins[i], j); - ); - ases[as].dacs[i] = j; - } - if (i == 16) - return (1); - } - return (0); -} + struct hdac_softc *sc; + device_t *devlist; + device_t dev; + int devcount, i, err, val; -/* - * Trace input monitor path from mixer to output association. - */ -static int -hdac_audio_trace_to_out(struct hdac_devinfo *devinfo, nid_t nid, int depth) -{ - struct hdac_audio_as *ases = devinfo->function.audio.as; - struct hdac_widget *w, *wc; - int i, j; - nid_t res = 0; + dev = oidp->oid_arg1; + sc = device_get_softc(dev); + if (sc == NULL) + return (EINVAL); + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); - if (depth > HDA_PARSE_MAXDEPTH) + /* XXX: Temporary. For debugging. */ + if (val == 100) { + hdac_suspend(dev); return (0); - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) + } else if (val == 101) { + hdac_resume(dev); return (0); - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*stracing via nid %d\n", - depth + 1, "", w->nid); - ); - /* Use only unused widgets */ - if (depth > 0 && w->bindas != -1) { - if (w->bindas < 0 || ases[w->bindas].dir == HDA_CTL_OUT) { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d found output association %d\n", - depth + 1, "", w->nid, w->bindas); - ); - if (w->bindas >= 0) - w->pflags |= HDA_ADC_MONITOR; - return (1); - } else { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by input association %d\n", - depth + 1, "", w->nid, w->bindas); - ); - return (0); - } - } - - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - /* Do not traverse input. AD1988 has digital monitor - for which we are not ready. */ - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (depth > 0) - break; - /* Fall */ - default: - /* Try to find reachable ADCs with specified nid. */ - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - wc = hdac_widget_get(devinfo, j); - if (wc == NULL || wc->enable == 0) - continue; - for (i = 0; i < wc->nconns; i++) { - if (wc->connsenable[i] == 0) - continue; - if (wc->conns[i] != nid) - continue; - if (hdac_audio_trace_to_out(devinfo, - j, depth + 1) != 0) { - res = 1; - if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - wc->selconn == -1) - wc->selconn = i; - } - } - } - break; - } - if (res && w->bindas == -1) - w->bindas = -2; - - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d returned %d\n", - depth + 1, "", w->nid, res); - ); - return (res); -} - -/* - * Trace extra associations (beeper, monitor) - */ -static void -hdac_audio_trace_as_extra(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - int j; - - /* Input monitor */ - /* Find mixer associated with input, but supplying signal - for output associations. Hope it will be input monitor. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing input monitor\n"); - ); - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) - continue; - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing nid %d to out\n", - j); - ); - if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " nid %d is input monitor\n", - w->nid); - ); - w->ossdev = SOUND_MIXER_IMIX; - } - } - - /* Other inputs monitor */ - /* Find input pins supplying signal for output associations. - Hope it will be input monitoring. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing other input monitors\n"); - ); - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) - continue; - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing nid %d to out\n", - j); - ); - if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " nid %d is input monitor\n", - w->nid); - ); - } - } - - /* Beeper */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing beeper\n"); - ); - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) - continue; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing nid %d to out\n", - j); - ); - if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " nid %d traced to out\n", - j); - ); - } - w->bindas = -2; - } -} - -/* - * Bind assotiations to PCM channels - */ -static void -hdac_audio_bind_as(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as = devinfo->function.audio.as; - int j, cnt = 0, free; - - for (j = 0; j < devinfo->function.audio.ascnt; j++) { - if (as[j].enable) - cnt++; - } - if (sc->num_chans == 0) { - sc->chans = (struct hdac_chan *)malloc( - sizeof(struct hdac_chan) * cnt, - M_HDAC, M_ZERO | M_NOWAIT); - if (sc->chans == NULL) { - device_printf(sc->dev, - "Channels memory allocation failed!\n"); - return; - } - } else { - sc->chans = (struct hdac_chan *)realloc(sc->chans, - sizeof(struct hdac_chan) * (sc->num_chans + cnt), - M_HDAC, M_ZERO | M_NOWAIT); - if (sc->chans == NULL) { - sc->num_chans = 0; - device_printf(sc->dev, - "Channels memory allocation failed!\n"); - return; - } - /* Fixup relative pointers after realloc */ - for (j = 0; j < sc->num_chans; j++) - sc->chans[j].caps.fmtlist = sc->chans[j].fmtlist; - } - free = sc->num_chans; - sc->num_chans += cnt; - - for (j = free; j < free + cnt; j++) { - sc->chans[j].devinfo = devinfo; - sc->chans[j].as = -1; - } - - /* Assign associations in order of their numbers, */ - for (j = 0; j < devinfo->function.audio.ascnt; j++) { - if (as[j].enable == 0) - continue; - - as[j].chan = free; - sc->chans[free].as = j; - sc->chans[free].dir = - (as[j].dir == HDA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY; - hdac_pcmchannel_setup(&sc->chans[free]); - free++; - } -} - -static void -hdac_audio_disable_nonaudio(struct hdac_devinfo *devinfo) -{ - struct hdac_widget *w; - int i; - - /* Disable power and volume widgets. */ - 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_POWER_WIDGET || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d due to it's" - " non-audio type.\n", - w->nid); - ); - } - } -} - -static void -hdac_audio_disable_useless(struct hdac_devinfo *devinfo) -{ - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - int done, found, i, j, k; - - /* Disable useless pins. */ - 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) { - if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling pin nid %d due" - " to None connectivity.\n", - w->nid); - ); - } else if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling unassociated" - " pin nid %d.\n", - w->nid); - ); - } - } - } - do { - done = 1; - /* Disable and mute controls for disabled widgets. */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if (ctl->widget->enable == 0 || - (ctl->childwidget != NULL && - ctl->childwidget->enable == 0)) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - if (ctl->ndir == HDA_CTL_IN) - ctl->widget->connsenable[ctl->index] = 0; - done = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling ctl %d nid %d cnid %d due" - " to disabled widget.\n", i, - ctl->widget->nid, - (ctl->childwidget != NULL)? - ctl->childwidget->nid:-1); - ); - } - } - /* Disable useless widgets. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - /* Disable inputs with disabled child widgets. */ - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j]) { - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) { - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d connection %d due" - " to disabled child widget.\n", - i, j); - ); - } - } - } - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - /* Disable mixers and selectors without inputs. */ - found = 0; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j]) { - found = 1; - break; - } - } - if (found == 0) { - w->enable = 0; - done = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d due to all it's" - " inputs disabled.\n", w->nid); - ); - } - /* Disable nodes without consumers. */ - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - found = 0; - for (k = devinfo->startnode; k < devinfo->endnode; k++) { - cw = hdac_widget_get(devinfo, k); - if (cw == NULL || cw->enable == 0) - continue; - for (j = 0; j < cw->nconns; j++) { - if (cw->connsenable[j] && cw->conns[j] == i) { - found = 1; - break; - } - } - } - if (found == 0) { - w->enable = 0; - done = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d due to all it's" - " consumers disabled.\n", w->nid); - ); - } - } - } while (done == 0); - -} - -static void -hdac_audio_disable_unas(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - int i, j, k; - - /* Disable unassosiated widgets. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->bindas == -1) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling unassociated nid %d.\n", - w->nid); - ); - } - } - /* Disable input connections on input pin and - * output on output. */ - 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 (w->bindas < 0) - continue; - if (as[w->bindas].dir == HDA_CTL_IN) { - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling connection to input pin " - "nid %d conn %d.\n", - i, j); - ); - } - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_IN, -1, 1); - if (ctl && ctl->enable) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - } - } else { - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_OUT, -1, 1); - if (ctl && ctl->enable) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - } - for (k = devinfo->startnode; k < devinfo->endnode; k++) { - cw = hdac_widget_get(devinfo, k); - if (cw == NULL || cw->enable == 0) - continue; - for (j = 0; j < cw->nconns; j++) { - if (cw->connsenable[j] && cw->conns[j] == i) { - cw->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling connection from output pin " - "nid %d conn %d cnid %d.\n", - k, j, i); - ); - if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - cw->nconns > 1) - continue; - ctl = hdac_audio_ctl_amp_get(devinfo, k, - HDA_CTL_IN, j, 1); - if (ctl && ctl->enable) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - } - } - } - } - } - } -} - -static void -hdac_audio_disable_notselected(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - int i, j; - - /* On playback path we can safely disable all unseleted inputs. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->nconns <= 1) - continue; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - if (w->bindas < 0 || as[w->bindas].dir == HDA_CTL_IN) - continue; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - if (w->selconn < 0 || w->selconn == j) - continue; - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling unselected connection " - "nid %d conn %d.\n", - i, j); - ); - } - } -} - -static void -hdac_audio_disable_crossas(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *ases = devinfo->function.audio.as; - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - int i, j; - - /* Disable crossassociatement and unwanted crosschannel connections. */ - /* ... using selectors */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->nconns <= 1) - continue; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - if (w->bindas == -2) - continue; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || w->enable == 0) - continue; - if (cw->bindas == -2 || - ((w->pflags & HDA_ADC_MONITOR) && - cw->bindas >= 0 && - ases[cw->bindas].dir == HDA_CTL_IN)) - continue; - if (w->bindas == cw->bindas && - (w->bindseqmask & cw->bindseqmask) != 0) - continue; - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling crossassociatement connection " - "nid %d conn %d cnid %d.\n", - i, j, cw->nid); - ); - } - } - /* ... using controls */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->childwidget == NULL) - continue; - if (ctl->widget->bindas == -2) - continue; - if (ctl->childwidget->bindas == -2 || - ((ctl->widget->pflags & HDA_ADC_MONITOR) && - ctl->childwidget->bindas >= 0 && - ases[ctl->childwidget->bindas].dir == HDA_CTL_IN)) - continue; - if (ctl->widget->bindas != ctl->childwidget->bindas || - (ctl->widget->bindseqmask & ctl->childwidget->bindseqmask) == 0) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - if (ctl->ndir == HDA_CTL_IN) - ctl->widget->connsenable[ctl->index] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling crossassociatement connection " - "ctl %d nid %d cnid %d.\n", i, - ctl->widget->nid, - ctl->childwidget->nid); - ); - } } + if ((err = device_get_children(dev, &devlist, &devcount)) != 0) + return (err); + hdac_lock(sc); + for (i = 0; i < devcount; i++) + HDAC_PINDUMP(devlist[i]); + hdac_unlock(sc); + free(devlist, M_TEMP); + return (0); } -#define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) - -/* - * Find controls to control amplification for source. - */ static int -hdac_audio_ctl_source_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, - int ossdev, int ctlable, int depth, int need) +hdac_data_rate(uint16_t fmt) { - struct hdac_widget *w, *wc; - struct hdac_audio_ctl *ctl; - int i, j, conns = 0, rneed; - - if (depth > HDA_PARSE_MAXDEPTH) - return (need); - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (need); - - /* Count number of active inputs. */ - if (depth > 0) { - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j]) - conns++; - } - } + static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; + int rate; - /* If this is not a first step - use input mixer. - Pins have common input ctl so care must be taken. */ - if (depth > 0 && ctlable && (conns == 1 || - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, - index, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & need) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - need &= ~HDA_CTL_GIVE(ctl); - } - } - - /* If widget has own ossdev - not traverse it. - It will be traversed on it's own. */ - if (w->ossdev >= 0 && depth > 0) - return (need); - - /* We must not traverse pin */ - if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && - depth > 0) - return (need); - - /* record that this widget exports such signal, */ - w->ossmask |= (1 << ossdev); - - /* If signals mixed, we can't assign controls farther. - * Ignore this on depth zero. Caller must knows why. - * Ignore this for static selectors if this input selected. - */ - if (conns > 1) - ctlable = 0; - - if (ctlable) { - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & need) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - need &= ~HDA_CTL_GIVE(ctl); - } - } - - rneed = 0; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - wc = hdac_widget_get(devinfo, i); - if (wc == NULL || wc->enable == 0) - continue; - for (j = 0; j < wc->nconns; j++) { - if (wc->connsenable[j] && wc->conns[j] == nid) { - rneed |= hdac_audio_ctl_source_amp(devinfo, - wc->nid, j, ossdev, ctlable, depth + 1, need); - } - } - } - rneed &= need; - - return (rneed); + if (fmt & (1 << 14)) + rate = 44100; + else + rate = 48000; + rate *= ((fmt >> 11) & 0x07) + 1; + rate /= ((fmt >> 8) & 0x07) + 1; + rate *= ((bits[(fmt >> 4) & 0x03]) * ((fmt & 0x0f) + 1) + 7) / 8; + return (rate); } -/* - * Find controls to control amplification for destination. - */ static void -hdac_audio_ctl_dest_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, - int ossdev, int depth, int need) +hdac_poll_reinit(struct hdac_softc *sc) { - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w, *wc; - struct hdac_audio_ctl *ctl; - int i, j, consumers; - - if (depth > HDA_PARSE_MAXDEPTH) - return; - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return; - - if (depth > 0) { - /* If this node produce output for several consumers, - we can't touch it. */ - consumers = 0; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - wc = hdac_widget_get(devinfo, i); - if (wc == NULL || wc->enable == 0) - continue; - for (j = 0; j < wc->nconns; j++) { - if (wc->connsenable[j] && wc->conns[j] == nid) - consumers++; - } - } - /* The only exception is if real HP redirection is configured - and this is a duplication point. - XXX: Actually exception is not completely correct. - XXX: Duplication point check is not perfect. */ - if ((consumers == 2 && (w->bindas < 0 || - as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || - (w->bindseqmask & (1 << 15)) == 0)) || - consumers > 2) - return; + int i, pollticks, min = 1000000; + struct hdac_stream *s; - /* Else use it's output mixer. */ - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_OUT, -1, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & need) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - need &= ~HDA_CTL_GIVE(ctl); - } - } - - /* We must not traverse pin */ - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - depth > 0) + if (sc->polling == 0) return; - - for (i = 0; i < w->nconns; i++) { - int tneed = need; - if (w->connsenable[i] == 0) - continue; - if (index >= 0 && i != index) - continue; - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_IN, i, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & tneed) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - tneed &= ~HDA_CTL_GIVE(ctl); - } - hdac_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev, - depth + 1, tneed); - } -} - -/* - * Assign OSS names to sound sources - */ -static void -hdac_audio_assign_names(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - int i, j; - int type = -1, use, used = 0; - static const int types[7][13] = { - { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, - SOUND_MIXER_LINE3, -1 }, /* line */ - { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */ - { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */ - { SOUND_MIXER_CD, -1 }, /* cd */ - { SOUND_MIXER_SPEAKER, -1 }, /* speaker */ - { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, - -1 }, /* digital */ - { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, - SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT, - SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1, - SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR, - -1 } /* others */ - }; - - /* Surely known names */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->bindas == -1) - continue; - use = -1; - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (as[w->bindas].dir == HDA_CTL_OUT) - break; - type = -1; - switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { - case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: - type = 0; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: - if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) - == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) - break; - type = 1; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: - type = 3; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: - type = 4; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: - case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: - type = 5; - break; - } - if (type == -1) - break; - j = 0; - while (types[type][j] >= 0 && - (used & (1 << types[type][j])) != 0) { - j++; - } - if (types[type][j] >= 0) - use = types[type][j]; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: - use = SOUND_MIXER_PCM; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: - use = SOUND_MIXER_SPEAKER; - break; - default: - break; - } - if (use >= 0) { - w->ossdev = use; - used |= (1 << use); - } - } - /* Semi-known names */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->ossdev >= 0) - continue; - if (w->bindas == -1) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (as[w->bindas].dir == HDA_CTL_OUT) - continue; - type = -1; - switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { - case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: - case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: - case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: - type = 0; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: - type = 2; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: - case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: - type = 5; - break; - } - if (type == -1) - break; - j = 0; - while (types[type][j] >= 0 && - (used & (1 << types[type][j])) != 0) { - j++; - } - if (types[type][j] >= 0) { - w->ossdev = types[type][j]; - used |= (1 << types[type][j]); - } - } - /* Others */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->ossdev >= 0) - continue; - if (w->bindas == -1) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (as[w->bindas].dir == HDA_CTL_OUT) - continue; - j = 0; - while (types[6][j] >= 0 && - (used & (1 << types[6][j])) != 0) { - j++; - } - if (types[6][j] >= 0) { - w->ossdev = types[6][j]; - used |= (1 << types[6][j]); - } - } -} - -static void -hdac_audio_build_tree(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - int j, res; - - /* Trace all associations in order of their numbers, */ - for (j = 0; j < devinfo->function.audio.ascnt; j++) { - if (as[j].enable == 0) - continue; - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing association %d (%d)\n", j, as[j].index); - ); - if (as[j].dir == HDA_CTL_OUT) { -retry: - res = hdac_audio_trace_as_out(devinfo, j, 0); - if (res == 0 && as[j].hpredir >= 0 && - as[j].fakeredir == 0) { - /* If codec can't do analog HP redirection - try to make it using one more DAC. */ - as[j].fakeredir = 1; - goto retry; - } - } else { - res = hdac_audio_trace_as_in(devinfo, j); - } - if (res) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Association %d (%d) trace succeeded\n", - j, as[j].index); - ); - } else { + if (sc->unsol_registered > 0) + min = hz / 2; + for (i = 0; i < sc->num_ss; i++) { + s = &sc->streams[i]; + if (s->running == 0) + continue; + pollticks = ((uint64_t)hz * s->blksz) / + hdac_data_rate(s->format); + pollticks >>= 1; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) { HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Association %d (%d) trace failed\n", - j, as[j].index); + device_printf(sc->dev, + "poll interval < 1 tick !\n"); ); - as[j].enable = 0; - } - } - - /* Trace mixer and beeper pseudo associations. */ - hdac_audio_trace_as_extra(devinfo); -} - -static void -hdac_audio_assign_mixers(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_audio_ctl *ctl; - struct hdac_widget *w, *cw; - int i, j; - - /* Assign mixers to the tree. */ - 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 || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET || - (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - as[w->bindas].dir == HDA_CTL_IN)) { - if (w->ossdev < 0) - continue; - hdac_audio_ctl_source_amp(devinfo, w->nid, -1, - w->ossdev, 1, 0, 1); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { - hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, - SOUND_MIXER_RECLEV, 0, 1); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - as[w->bindas].dir == HDA_CTL_OUT) { - hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, - SOUND_MIXER_VOLUME, 0, 1); - } - if (w->ossdev == SOUND_MIXER_IMIX) { - if (hdac_audio_ctl_source_amp(devinfo, w->nid, -1, - w->ossdev, 1, 0, 1)) { - /* If we are unable to control input monitor - as source - try to control it as destination. */ - hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, - w->ossdev, 0, 1); - } - } - if (w->pflags & HDA_ADC_MONITOR) { - for (j = 0; j < w->nconns; j++) { - if (!w->connsenable[j]) - continue; - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - if (cw->bindas == -1) - continue; - if (cw->bindas >= 0 && - as[cw->bindas].dir != HDA_CTL_IN) - continue; - hdac_audio_ctl_dest_amp(devinfo, - w->nid, j, SOUND_MIXER_IGAIN, 0, 1); - } - } - } - /* Treat unrequired as possible. */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->ossmask == 0) - ctl->ossmask = ctl->possmask; - } -} - -static void -hdac_audio_prepare_pin_ctrl(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - uint32_t pincap; - int i; - - for (i = 0; i < devinfo->nodecnt; i++) { - w = &devinfo->widget[i]; - if (w == NULL) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - - pincap = w->wclass.pin.cap; - - /* Disable everything. */ - 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 | - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); - - if (w->enable == 0 || - w->bindas < 0 || as[w->bindas].enable == 0) { - /* Pin is unused so left it disabled. */ - continue; - } else if (as[w->bindas].dir == HDA_CTL_IN) { - /* Input pin, configure for input. */ - if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; - - if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && - HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && - HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && - HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); - } else { - /* Output pin, configure for output. */ - 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_HEADPHONE_CAP(pincap) && - (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; - - if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && - HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && - HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && - HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); - } - } -} - -static void -hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_ctl *ctl; - int i, z; - - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->ossmask != 0) { - /* Mute disabled and mixer controllable controls. - * Last will be initialized by mixer_init(). - * This expected to reduce click on startup. */ - hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_ALL, 0, 0); - continue; - } - /* Init fixed controls to 0dB amplification. */ - z = ctl->offset; - if (z > ctl->step) - z = ctl->step; - hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_NONE, z, z); - } -} - -static void -hdac_audio_commit(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_widget *w; - nid_t cad; - uint32_t id, gdata, gmask, gdir; - int commitgpio, numgpio; - int i; - - cad = devinfo->codec->cad; - - if (sc->pci_subvendor == APPLE_INTEL_MAC) - hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, - 0x7e7, 0), cad); - id = hdac_codec_id(devinfo->codec); - switch (id) { - case HDA_CODEC_VT1708S_0: - case HDA_CODEC_VT1708S_1: - case HDA_CODEC_VT1708S_2: - case HDA_CODEC_VT1708S_3: - case HDA_CODEC_VT1708S_4: - case HDA_CODEC_VT1708S_5: - case HDA_CODEC_VT1708S_6: - case HDA_CODEC_VT1708S_7: - /* Enable Mic Boost Volume controls. */ - hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, - 0xf98, 0x01), cad); - /* Don't bypass mixer. */ - hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, - 0xf88, 0xc0), cad); - break; - } - - /* Commit controls. */ - hdac_audio_ctl_commit(devinfo); - - /* Commit selectors, pins and EAPD. */ - for (i = 0; i < devinfo->nodecnt; i++) { - w = &devinfo->widget[i]; - if (w == NULL) - continue; - if (w->selconn == -1) - w->selconn = 0; - if (w->nconns > 0) - hdac_widget_connection_select(w, w->selconn); - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, - w->wclass.pin.ctrl), cad); - } - if (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); - } - } - - /* Commit GPIOs. */ - gdata = 0; - gmask = 0; - gdir = 0; - commitgpio = 0; - numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( - devinfo->function.audio.gpio); - - if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) - commitgpio = (numgpio > 0) ? 1 : 0; - else { - for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { - if (!(devinfo->function.audio.quirks & - (1 << i))) - continue; - if (commitgpio == 0) { - commitgpio = 1; - HDA_BOOTVERBOSE( - gdata = hdac_command(sc, - HDA_CMD_GET_GPIO_DATA(cad, - devinfo->nid), cad); - gmask = hdac_command(sc, - HDA_CMD_GET_GPIO_ENABLE_MASK(cad, - devinfo->nid), cad); - gdir = hdac_command(sc, - HDA_CMD_GET_GPIO_DIRECTION(cad, - devinfo->nid), cad); - device_printf(sc->dev, - "GPIO init: data=0x%08x " - "mask=0x%08x dir=0x%08x\n", - gdata, gmask, gdir); - gdata = 0; - gmask = 0; - gdir = 0; - ); - } - gdata |= 1 << i; - gmask |= 1 << i; - gdir |= 1 << i; - } - } - - if (commitgpio != 0) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "GPIO commit: data=0x%08x mask=0x%08x " - "dir=0x%08x\n", - gdata, gmask, gdir); - ); - hdac_command(sc, - HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, - gmask), cad); - hdac_command(sc, - HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, - gdir), cad); - hdac_command(sc, - HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, - gdata), cad); - } -} - -static void -hdac_powerup(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - nid_t cad = devinfo->codec->cad; - int i; - - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(cad, - devinfo->nid, HDA_CMD_POWER_STATE_D0), - cad); - DELAY(100); - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(cad, - i, HDA_CMD_POWER_STATE_D0), - cad); - } - DELAY(1000); -} - -static int -hdac_pcmchannel_setup(struct hdac_chan *ch) -{ - struct hdac_devinfo *devinfo = ch->devinfo; - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - uint32_t cap, fmtcap, pcmcap; - int i, j, ret, channels, onlystereo; - uint16_t pinset; - - 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; - channels = 0; - onlystereo = 1; - pinset = 0; - fmtcap = devinfo->function.audio.supp_stream_formats; - pcmcap = devinfo->function.audio.supp_pcm_size_rate; - - for (i = 0; i < 16; i++) { - /* Check as is correct */ - if (ch->as < 0) - break; - /* Cound only present DACs */ - if (as[ch->as].dacs[i] <= 0) - continue; - /* Ignore duplicates */ - for (j = 0; j < ret; j++) { - if (ch->io[j] == as[ch->as].dacs[i]) - break; - } - if (j < ret) - continue; - - w = hdac_widget_get(devinfo, as[ch->as].dacs[i]); - if (w == NULL || w->enable == 0) - continue; - cap = w->param.supp_stream_formats; - if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) && - !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) - continue; - /* Many CODECs does not declare AC3 support on SPDIF. - I don't beleave that they doesn't support it! */ - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) - cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK; - if (ret == 0) { - fmtcap = cap; - pcmcap = w->param.supp_pcm_size_rate; - } else { - fmtcap &= cap; - pcmcap &= w->param.supp_pcm_size_rate; - } - ch->io[ret++] = as[ch->as].dacs[i]; - /* Do not count redirection pin/dac channels. */ - if (i == 15 && as[ch->as].hpredir >= 0) - continue; - channels += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; - if (HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) != 1) - onlystereo = 0; - pinset |= (1 << i); - } - ch->io[ret] = -1; - - if (as[ch->as].fakeredir) - ret--; - /* Standard speaks only about stereo pins and playback, ... */ - if ((!onlystereo) || as[ch->as].dir != HDA_CTL_OUT) - pinset = 0; - /* ..., but there it gives us info about speakers layout. */ - as[ch->as].pinset = pinset; - - ch->supp_stream_formats = fmtcap; - ch->supp_pcm_size_rate = pcmcap; - - /* - * 8bit = 0 - * 16bit = 1 - * 20bit = 2 - * 24bit = 3 - * 32bit = 4 - */ - if (ret > 0) { - i = 0; - if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) { - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap)) - ch->bit16 = 1; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap)) - ch->bit16 = 0; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) - ch->bit32 = 4; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) - ch->bit32 = 3; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) - ch->bit32 = 2; - if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) { - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 1, 0); - } - if (channels >= 2) { - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0); - } - if (channels == 4 || /* Any 4-channel */ - pinset == 0x0007 || /* 5.1 */ - pinset == 0x0013 || /* 5.1 */ - pinset == 0x0017) { /* 7.1 */ - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0); - } - if (channels == 6 || /* Any 6-channel */ - pinset == 0x0017) { /* 7.1 */ - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1); - } - if (channels == 8) { /* Any 8-channel */ - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1); - } - } - if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { - ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); - } - ch->fmtlist[i] = 0; - i = 0; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap)) - ch->pcmrates[i++] = 8000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap)) - ch->pcmrates[i++] = 11025; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap)) - ch->pcmrates[i++] = 16000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap)) - ch->pcmrates[i++] = 22050; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap)) - ch->pcmrates[i++] = 32000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap)) - ch->pcmrates[i++] = 44100; - /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */ - ch->pcmrates[i++] = 48000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap)) - ch->pcmrates[i++] = 88200; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap)) - ch->pcmrates[i++] = 96000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap)) - ch->pcmrates[i++] = 176400; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap)) - ch->pcmrates[i++] = 192000; - /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */ - 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_create_pcms(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as = devinfo->function.audio.as; - int i, j, apdev = 0, ardev = 0, dpdev = 0, drdev = 0; - - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].enable == 0) - continue; - if (as[i].dir == HDA_CTL_IN) { - if (as[i].digital) - drdev++; - else - ardev++; - } else { - if (as[i].digital) - dpdev++; - else - apdev++; - } - } - devinfo->function.audio.num_devs = - max(ardev, apdev) + max(drdev, dpdev); - devinfo->function.audio.devs = - (struct hdac_pcm_devinfo *)malloc( - devinfo->function.audio.num_devs * sizeof(struct hdac_pcm_devinfo), - M_HDAC, M_ZERO | M_NOWAIT); - if (devinfo->function.audio.devs == NULL) { - device_printf(sc->dev, - "Unable to allocate memory for devices\n"); - return; - } - for (i = 0; i < devinfo->function.audio.num_devs; i++) { - devinfo->function.audio.devs[i].index = i; - devinfo->function.audio.devs[i].devinfo = devinfo; - devinfo->function.audio.devs[i].play = -1; - devinfo->function.audio.devs[i].rec = -1; - devinfo->function.audio.devs[i].digital = 255; - } - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].enable == 0) - continue; - for (j = 0; j < devinfo->function.audio.num_devs; j++) { - if (devinfo->function.audio.devs[j].digital != 255 && - (!devinfo->function.audio.devs[j].digital) != - (!as[i].digital)) - continue; - if (as[i].dir == HDA_CTL_IN) { - if (devinfo->function.audio.devs[j].rec >= 0) - continue; - devinfo->function.audio.devs[j].rec - = as[i].chan; - } else { - if (devinfo->function.audio.devs[j].play >= 0) - continue; - devinfo->function.audio.devs[j].play - = as[i].chan; - } - sc->chans[as[i].chan].pdevinfo = - &devinfo->function.audio.devs[j]; - devinfo->function.audio.devs[j].digital = - as[i].digital; - break; - } - } - for (i = 0; i < devinfo->function.audio.num_devs; i++) { - struct hdac_pcm_devinfo *pdevinfo = - &devinfo->function.audio.devs[i]; - pdevinfo->dev = - device_add_child(sc->dev, "pcm", -1); - device_set_ivars(pdevinfo->dev, - (void *)pdevinfo); - } -} - -static void -hdac_dump_ctls(struct hdac_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_audio_ctl *ctl; - struct hdac_softc *sc = devinfo->codec->sc; - char buf[64]; - int i, j, printed; - - if (flag == 0) { - flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM | - SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | - SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | - SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR); - } - - for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { - if ((flag & (1 << j)) == 0) - continue; - i = 0; - printed = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || - ctl->widget->enable == 0) - continue; - if (!((pdevinfo->play >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->play].as) || - (pdevinfo->rec >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || - (ctl->widget->bindas == -2 && pdevinfo->index == 0))) - continue; - if ((ctl->ossmask & (1 << j)) == 0) - continue; - - if (printed == 0) { - device_printf(pdevinfo->dev, "\n"); - if (banner != NULL) { - device_printf(pdevinfo->dev, "%s", banner); - } else { - device_printf(pdevinfo->dev, "Unknown Ctl"); - } - printf(" (OSS: %s)\n", - hdac_audio_ctl_ossmixer_mask2allname(1 << j, - buf, sizeof(buf))); - device_printf(pdevinfo->dev, " |\n"); - printed = 1; - } - device_printf(pdevinfo->dev, " +- ctl %2d (nid %3d %s", i, - ctl->widget->nid, - (ctl->ndir == HDA_CTL_IN)?"in ":"out"); - if (ctl->ndir == HDA_CTL_IN && ctl->ndir == ctl->dir) - printf(" %2d): ", ctl->index); - else - printf("): "); - if (ctl->step > 0) { - printf("%+d/%+ddB (%d steps)%s\n", - (0 - ctl->offset) * (ctl->size + 1) / 4, - (ctl->step - ctl->offset) * (ctl->size + 1) / 4, - ctl->step + 1, - ctl->mute?" + mute":""); - } else - printf("%s\n", ctl->mute?"mute":""); - } - } -} - -static void -hdac_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap) -{ - uint32_t cap; - - cap = fcap; - if (cap != 0) { - device_printf(dev, " Stream cap: 0x%08x\n", cap); - device_printf(dev, " "); - 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(dev, " PCM cap: 0x%08x\n", cap); - device_printf(dev, " "); - 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(" bits,"); - 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(" KHz\n"); - } -} - -static void -hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) -{ - uint32_t pincap; - - pincap = w->wclass.pin.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_HDMI(pincap)) - printf(" HDMI"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { - printf(" VREF["); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - printf(" 50"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - printf(" 80"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - printf(" 100"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) - printf(" GROUND"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) - printf(" HIZ"); - printf(" ]"); - } - if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) - printf(" EAPD"); - if (HDA_PARAM_PIN_CAP_DP(pincap)) - printf(" DP"); - if (HDA_PARAM_PIN_CAP_HBR(pincap)) - printf(" HBR"); - 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"); - if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) - printf(" VREFs"); - printf("\n"); -} - -static void -hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf) -{ - struct hdac_softc *sc = w->devinfo->codec->sc; - - device_printf(sc->dev, " nid %d 0x%08x as %2d seq %2d %13s %5s " - "jack %2d loc %2d color %7s misc %d%s\n", - w->nid, conf, - HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), - HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), - HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], - HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], - HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf), - HDA_CONFIG_DEFAULTCONF_LOCATION(conf), - HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], - HDA_CONFIG_DEFAULTCONF_MISC(conf), - (w->enable == 0)?" [DISABLED]":""); -} - -static void -hdac_dump_pin_configs(struct hdac_devinfo *devinfo) -{ - struct hdac_widget *w; - int i; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - hdac_dump_pin_config(w, w->wclass.pin.config); - } -} - -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; - static char *ossname[] = SOUND_DEVICE_NAMES; - struct hdac_widget *w, *cw; - char buf[64]; - 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->dev, - 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\n", w->nid, - (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); - if (w->param.widget_cap & 0x0ee1) { - device_printf(sc->dev, " "); - if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap)) - printf(" LRSWAP"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap)) - printf(" PWR"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) - printf(" DIGITAL"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) - printf(" UNSOL"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) - printf(" PROC"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) - printf(" STRIPE"); - j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); - if (j == 1) - printf(" STEREO"); - else if (j > 1) - printf(" %dCH", j + 1); - printf("\n"); - } - if (w->bindas != -1) { - device_printf(sc->dev, " Association: %d (0x%08x)\n", - w->bindas, w->bindseqmask); - } - if (w->ossmask != 0 || w->ossdev >= 0) { - device_printf(sc->dev, " OSS: %s", - hdac_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); - if (w->ossdev >= 0) - printf(" (%s)", ossname[w->ossdev]); - printf("\n"); - } - 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->dev, - 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"); - if (w->nconns > 0) { - device_printf(sc->dev, " connections: %d\n", w->nconns); - device_printf(sc->dev, " |\n"); - } - for (j = 0; j < w->nconns; j++) { - cw = hdac_widget_get(devinfo, w->conns[j]); - device_printf(sc->dev, " + %s<- nid=%d [%s]", - (w->connsenable[j] == 0)?"[DISABLED] ":"", - 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 void -hdac_dump_dst_nid(struct hdac_pcm_devinfo *pdevinfo, nid_t nid, int depth) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w, *cw; - char buf[64]; - int i, printed = 0; - - if (depth > HDA_PARSE_MAXDEPTH) - return; - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return; - - if (depth == 0) - device_printf(pdevinfo->dev, "%*s", 4, ""); - else - device_printf(pdevinfo->dev, "%*s + <- ", 4 + (depth - 1) * 7, ""); - printf("nid=%d [%s]", w->nid, w->name); - - if (depth > 0) { - if (w->ossmask == 0) { - printf("\n"); - return; - } - printf(" [src: %s]", - hdac_audio_ctl_ossmixer_mask2allname( - w->ossmask, buf, sizeof(buf))); - if (w->ossdev >= 0) { - printf("\n"); - return; - } - } - printf("\n"); - - for (i = 0; i < w->nconns; i++) { - if (w->connsenable[i] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[i]); - if (cw == NULL || cw->enable == 0 || cw->bindas == -1) - continue; - if (printed == 0) { - device_printf(pdevinfo->dev, "%*s |\n", 4 + (depth) * 7, ""); - printed = 1; - } - hdac_dump_dst_nid(pdevinfo, w->conns[i], depth + 1); - } - -} - -static void -hdac_dump_dac(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as; - struct hdac_widget *w; - int i, printed = 0; - - if (pdevinfo->play < 0) - return; - - as = &devinfo->function.audio.as[sc->chans[pdevinfo->play].as]; - for (i = 0; i < 16; i++) { - if (as->pins[i] <= 0) - continue; - w = hdac_widget_get(devinfo, as->pins[i]); - if (w == NULL || w->enable == 0) - continue; - if (printed == 0) { - printed = 1; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Playback:\n"); - } - device_printf(pdevinfo->dev, "\n"); - hdac_dump_dst_nid(pdevinfo, as->pins[i], 0); - } -} - -static void -hdac_dump_adc(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_widget *w; - int i; - int printed = 0; - - if (pdevinfo->rec < 0) - return; - - 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->bindas != sc->chans[pdevinfo->rec].as) - continue; - if (printed == 0) { - printed = 1; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Record:\n"); - } - device_printf(pdevinfo->dev, "\n"); - hdac_dump_dst_nid(pdevinfo, i, 0); - } -} - -static void -hdac_dump_mix(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w; - int i; - int printed = 0; - - if (pdevinfo->index != 0) - return; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->ossdev != SOUND_MIXER_IMIX) - continue; - if (printed == 0) { - printed = 1; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Input Mix:\n"); - } - device_printf(pdevinfo->dev, "\n"); - hdac_dump_dst_nid(pdevinfo, i, 0); - } -} - -static void -hdac_dump_pcmchannels(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; - nid_t *nids; - int i; - - if (pdevinfo->play >= 0) { - i = pdevinfo->play; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Playback:\n"); - device_printf(pdevinfo->dev, "\n"); - hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, - sc->chans[i].supp_pcm_size_rate); - device_printf(pdevinfo->dev, " DAC:"); - for (nids = sc->chans[i].io; *nids != -1; nids++) - printf(" %d", *nids); - printf("\n"); - } - if (pdevinfo->rec >= 0) { - i = pdevinfo->rec; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Record:\n"); - device_printf(pdevinfo->dev, "\n"); - hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, - sc->chans[i].supp_pcm_size_rate); - device_printf(pdevinfo->dev, " ADC:"); - for (nids = sc->chans[i].io; *nids != -1; nids++) - printf(" %d", *nids); - printf("\n"); - } -} - -static void -hdac_release_resources(struct hdac_softc *sc) -{ - int i, j; - - if (sc == NULL) - return; - - hdac_lock(sc); - sc->polling = 0; - sc->poll_ival = 0; - callout_stop(&sc->poll_hda); - callout_stop(&sc->poll_hdac); - callout_stop(&sc->poll_jack); - hdac_reset(sc, 0); - hdac_unlock(sc); - taskqueue_drain(taskqueue_thread, &sc->unsolq_task); - callout_drain(&sc->poll_hda); - callout_drain(&sc->poll_hdac); - callout_drain(&sc->poll_jack); - - hdac_irq_free(sc); - - for (i = 0; i < HDAC_CODEC_MAX; i++) { - if (sc->codecs[i] == NULL) - continue; - for (j = 0; j < sc->codecs[i]->num_fgs; j++) { - free(sc->codecs[i]->fgs[j].widget, M_HDAC); - if (sc->codecs[i]->fgs[j].node_type == - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - free(sc->codecs[i]->fgs[j].function.audio.ctl, - M_HDAC); - free(sc->codecs[i]->fgs[j].function.audio.as, - M_HDAC); - free(sc->codecs[i]->fgs[j].function.audio.devs, - M_HDAC); - } + pollticks = 1; } - free(sc->codecs[i]->fgs, M_HDAC); - free(sc->codecs[i], M_HDAC); - sc->codecs[i] = NULL; - } - - hdac_dma_free(sc, &sc->pos_dma); - hdac_dma_free(sc, &sc->rirb_dma); - hdac_dma_free(sc, &sc->corb_dma); - for (i = 0; i < sc->num_chans; i++) { - if (sc->chans[i].blkcnt > 0) - hdac_dma_free(sc, &sc->chans[i].bdl_dma); - } - free(sc->chans, M_HDAC); - if (sc->chan_dmat != NULL) { - bus_dma_tag_destroy(sc->chan_dmat); - sc->chan_dmat = NULL; + if (min > pollticks) + min = pollticks; } - hdac_mem_free(sc); - snd_mtxfree(sc->lock); -} - -/* 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 Config:"); + device_printf(sc->dev, + "poll interval %d -> %d ticks\n", + sc->poll_ival, min); ); - 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)) - continue; - 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; - } + sc->poll_ival = min; + if (min == 1000000) + callout_stop(&sc->poll_callout); + else + callout_reset(&sc->poll_callout, 1, hdac_poll_callback, sc); } static int @@ -7512,11 +1409,9 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) hdac_lock(sc); if (val != sc->polling) { if (val == 0) { - callout_stop(&sc->poll_hda); - callout_stop(&sc->poll_hdac); + callout_stop(&sc->poll_callout); hdac_unlock(sc); - callout_drain(&sc->poll_hda); - callout_drain(&sc->poll_hdac); + callout_drain(&sc->poll_callout); hdac_lock(sc); sc->polling = 0; ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); @@ -7526,12 +1421,8 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl &= ~HDAC_INTCTL_GIE; HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); - hdac_unlock(sc); - taskqueue_drain(taskqueue_thread, &sc->unsolq_task); - hdac_lock(sc); sc->polling = 1; hdac_poll_reinit(sc); - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } } hdac_unlock(sc); @@ -7539,202 +1430,17 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) return (err); } -static int -sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) -{ - struct hdac_softc *sc; - device_t dev; - int err, val; - - dev = oidp->oid_arg1; - sc = device_get_softc(dev); - if (sc == NULL) - return (EINVAL); - hdac_lock(sc); - val = ((uint64_t)sc->poll_ival * 1000) / hz; - hdac_unlock(sc); - err = sysctl_handle_int(oidp, &val, 0, req); - - if (err != 0 || req->newptr == NULL) - return (err); - - if (val < 1) - val = 1; - if (val > 5000) - val = 5000; - val = ((uint64_t)val * hz) / 1000; - if (val < 1) - val = 1; - if (val > (hz * 5)) - val = hz * 5; - - hdac_lock(sc); - sc->poll_ival = val; - hdac_unlock(sc); - - return (err); -} - -static int -sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) -{ - struct hdac_softc *sc; - struct hdac_codec *codec; - struct hdac_devinfo *devinfo; - struct hdac_widget *w; - device_t dev; - uint32_t res, pincap, delay; - int codec_index, fg_index; - int i, err, val; - nid_t cad; - - dev = oidp->oid_arg1; - sc = device_get_softc(dev); - if (sc == NULL) - return (EINVAL); - val = 0; - err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL || val == 0) - return (err); - - /* XXX: Temporary. For debugging. */ - if (val == 100) { - hdac_suspend(dev); - return (0); - } else if (val == 101) { - hdac_resume(dev); - return (0); - } - - hdac_lock(sc); - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - cad = codec->cad; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - if (devinfo->node_type != - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) - continue; - - device_printf(dev, "Dumping AFG cad=%d nid=%d pins:\n", - codec_index, devinfo->nid); - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - hdac_dump_pin_config(w, w->wclass.pin.config); - pincap = w->wclass.pin.cap; - device_printf(dev, " Caps: %2s %3s %2s %4s %4s", - HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"", - HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"", - HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"", - HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"", - HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":""); - if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || - HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) { - if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { - delay = 0; - hdac_command(sc, - HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), cad); - do { - res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); - if (res != 0x7fffffff && res != 0xffffffff) - break; - DELAY(10); - } while (++delay < 10000); - } else { - delay = 0; - res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, - w->nid), cad); - } - printf(" Sense: 0x%08x", res); - if (delay > 0) - printf(" delay %dus", delay * 10); - } - printf("\n"); - } - device_printf(dev, - "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", - HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); - if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { - device_printf(dev, " GPI:"); - res = hdac_command(sc, - HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" wake=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" unsol=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); - printf(" sticky=0x%08x\n", res); - } - if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { - device_printf(dev, " GPO:"); - res = hdac_command(sc, - HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x\n", res); - } - if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { - device_printf(dev, "GPIO:"); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); - printf(" enable=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); - printf(" direction=0x%08x\n", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); - device_printf(dev, " wake=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" unsol=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); - printf(" sticky=0x%08x\n", res); - } - } - } - hdac_unlock(sc); - return (0); -} - static void hdac_attach2(void *arg) { - struct hdac_codec *codec; struct hdac_softc *sc; - struct hdac_audio_ctl *ctl; - uint32_t quirks_on, quirks_off; - int codec_index, fg_index; - int i, dmaalloc = 0; - struct hdac_devinfo *devinfo; + device_t child; + uint32_t vendorid, revisionid; + int i; + uint16_t statests; sc = (struct hdac_softc *)arg; - hdac_config_fetch(sc, &quirks_on, &quirks_off); - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "HDA Config: on=0x%08x off=0x%08x\n", - quirks_on, quirks_off); - ); - hdac_lock(sc); /* Remove ourselves from the config hooks */ @@ -7743,7 +1449,6 @@ hdac_attach2(void *arg) sc->intrhook.ich_func = NULL; } - /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(sc->dev, "Starting CORB Engine...\n"); ); @@ -7752,7 +1457,6 @@ hdac_attach2(void *arg) device_printf(sc->dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); - HDA_BOOTHVERBOSE( device_printf(sc->dev, "Enabling controller interrupt...\n"); @@ -7762,218 +1466,60 @@ hdac_attach2(void *arg) if (sc->polling == 0) { HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); - } else { - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } DELAY(1000); HDA_BOOTHVERBOSE( - device_printf(sc->dev, - "Scanning HDA codecs ...\n"); + device_printf(sc->dev, "Scanning HDA codecs ...\n"); ); - hdac_scan_codecs(sc); - - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - HDA_BOOTVERBOSE( - device_printf(sc->dev, "\n"); + statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); + hdac_unlock(sc); + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (HDAC_STATESTS_SDIWAKE(statests, i)) { + HDA_BOOTHVERBOSE( device_printf(sc->dev, - "Processing %s FG cad=%d nid=%d...\n", - (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": - (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": - "unknown", - devinfo->codec->cad, devinfo->nid); + "Found CODEC at address %d\n", i); ); - if (devinfo->node_type != - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - HDA_BOOTHVERBOSE( - device_printf(sc->dev, - "Powering down...\n"); - ); - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(codec->cad, - devinfo->nid, HDA_CMD_POWER_STATE_D3), - codec->cad); + hdac_lock(sc); + vendorid = hdac_send_command(sc, i, + HDA_CMD_GET_PARAMETER(0, 0x0, HDA_PARAM_VENDOR_ID)); + revisionid = hdac_send_command(sc, i, + HDA_CMD_GET_PARAMETER(0, 0x0, HDA_PARAM_REVISION_ID)); + hdac_unlock(sc); + if (vendorid == HDA_INVALID && + revisionid == HDA_INVALID) { + device_printf(sc->dev, + "CODEC is not responding!\n"); continue; } - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Powering up...\n"); - ); - hdac_powerup(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing audio FG...\n"); - ); - hdac_audio_parse(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing vendor patch...\n"); - ); - hdac_vendor_patch_parse(devinfo); - devinfo->function.audio.quirks |= quirks_on; - devinfo->function.audio.quirks &= ~quirks_off; - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing Ctls...\n"); - ); - hdac_audio_ctl_parse(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling nonaudio...\n"); - ); - hdac_audio_disable_nonaudio(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling useless...\n"); - ); - hdac_audio_disable_useless(devinfo); - HDA_BOOTVERBOSE( - device_printf(sc->dev, "Patched pins configuration:\n"); - hdac_dump_pin_configs(devinfo); - ); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing pin associations...\n"); - ); - hdac_audio_as_parse(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Building AFG tree...\n"); - ); - hdac_audio_build_tree(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling unassociated " - "widgets...\n"); - ); - hdac_audio_disable_unas(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling nonselected " - "inputs...\n"); - ); - hdac_audio_disable_notselected(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling useless...\n"); - ); - hdac_audio_disable_useless(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling " - "crossassociatement connections...\n"); - ); - hdac_audio_disable_crossas(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling useless...\n"); - ); - hdac_audio_disable_useless(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Binding associations to channels...\n"); - ); - hdac_audio_bind_as(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Assigning names to signal sources...\n"); - ); - hdac_audio_assign_names(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Assigning mixers to the tree...\n"); - ); - hdac_audio_assign_mixers(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Preparing pin controls...\n"); - ); - hdac_audio_prepare_pin_ctrl(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "AFG commit...\n"); - ); - hdac_audio_commit(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "HP switch init...\n"); - ); - hdac_hp_switch_init(devinfo); - - if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && - dmaalloc == 0) { - if (hdac_dma_alloc(sc, &sc->pos_dma, - (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, "Failed to " - "allocate DMA pos buffer " - "(non-fatal)\n"); - ); - } else - dmaalloc = 1; + sc->codecs[i].vendor_id = + HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); + sc->codecs[i].device_id = + HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); + sc->codecs[i].revision_id = + HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); + sc->codecs[i].stepping_id = + HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); + child = device_add_child(sc->dev, "hdacc", -1); + if (child == NULL) { + device_printf(sc->dev, + "Failed to add CODEC device\n"); + continue; } - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Creating PCM devices...\n"); - ); - hdac_create_pcms(devinfo); - - HDA_BOOTVERBOSE( - if (devinfo->function.audio.quirks != 0) { - device_printf(sc->dev, "FG config/quirks:"); - for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { - if ((devinfo->function.audio.quirks & - hdac_quirks_tab[i].value) == - 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); - ); - - HDA_BOOTHVERBOSE( - 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 %3d %s (%s) index %d", i, - (ctl->widget != NULL) ? ctl->widget->nid : -1, - (ctl->ndir == HDA_CTL_IN)?"in ":"out", - (ctl->dir == HDA_CTL_IN)?"in ":"out", - ctl->index); - if (ctl->childwidget != NULL) - printf(" cnid %3d", ctl->childwidget->nid); - else - printf(" "); - printf(" ossmask=0x%08x\n", - ctl->ossmask); - device_printf(sc->dev, - " mute: %d step: %3d size: %3d off: %3d%s\n", - ctl->mute, ctl->step, ctl->size, ctl->offset, - (ctl->enable == 0) ? " [DISABLED]" : - ((ctl->ossmask == 0) ? " [UNUSED]" : "")); - } - ); + device_set_ivars(child, (void *)(intptr_t)i); + sc->codecs[i].dev = child; } } - hdac_unlock(sc); - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "\n"); - ); - bus_generic_attach(sc->dev); SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, - "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), - sysctl_hdac_polling, "I", "Enable polling mode"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, - "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, - sizeof(sc->dev), sysctl_hdac_polling_interval, "I", - "Controller/Jack Sense polling interval (1-1000 ms)"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_pindump, "I", "Dump pin states/data"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_polling, "I", "Enable polling mode"); } /**************************************************************************** @@ -7984,64 +1530,23 @@ hdac_attach2(void *arg) static int hdac_suspend(device_t dev) { - struct hdac_softc *sc; - struct hdac_codec *codec; - struct hdac_devinfo *devinfo; - int codec_index, fg_index, i; + struct hdac_softc *sc = device_get_softc(dev); HDA_BOOTHVERBOSE( device_printf(dev, "Suspend...\n"); ); + bus_generic_suspend(dev); - sc = device_get_softc(dev); hdac_lock(sc); - - HDA_BOOTHVERBOSE( - device_printf(dev, "Stop streams...\n"); - ); - for (i = 0; i < sc->num_chans; i++) { - if (sc->chans[i].flags & HDAC_CHN_RUNNING) { - sc->chans[i].flags |= HDAC_CHN_SUSPEND; - hdac_channel_stop(sc, &sc->chans[i]); - } - } - - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - HDA_BOOTHVERBOSE( - device_printf(dev, - "Power down FG" - " cad=%d nid=%d to the D3 state...\n", - codec->cad, devinfo->nid); - ); - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(codec->cad, - devinfo->nid, HDA_CMD_POWER_STATE_D3), - codec->cad); - } - } - HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); - callout_stop(&sc->poll_hda); - callout_stop(&sc->poll_hdac); - callout_stop(&sc->poll_jack); hdac_reset(sc, 0); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); - callout_drain(&sc->poll_hda); - callout_drain(&sc->poll_hdac); - callout_drain(&sc->poll_jack); - HDA_BOOTHVERBOSE( device_printf(dev, "Suspend done\n"); ); - return (0); } @@ -8053,16 +1558,12 @@ hdac_suspend(device_t dev) static int hdac_resume(device_t dev) { - struct hdac_softc *sc; - struct hdac_codec *codec; - struct hdac_devinfo *devinfo; - int codec_index, fg_index, i; + struct hdac_softc *sc = device_get_softc(dev); + int error; HDA_BOOTHVERBOSE( device_printf(dev, "Resume...\n"); ); - - sc = device_get_softc(dev); hdac_lock(sc); /* Quiesce everything */ @@ -8075,7 +1576,6 @@ hdac_resume(device_t dev) hdac_corb_init(sc); hdac_rirb_init(sc); - /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(dev, "Starting CORB Engine...\n"); ); @@ -8084,91 +1584,22 @@ hdac_resume(device_t dev) device_printf(dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); - HDA_BOOTHVERBOSE( - device_printf(dev, - "Enabling controller interrupt...\n"); + device_printf(dev, "Enabling controller interrupt...\n"); ); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); - if (sc->polling == 0) { - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, - HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); - } else { - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); - } + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); DELAY(1000); - - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - if (devinfo->node_type != - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - HDA_BOOTHVERBOSE( - device_printf(dev, - "Power down unsupported non-audio FG" - " cad=%d nid=%d to the D3 state...\n", - codec->cad, devinfo->nid); - ); - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(codec->cad, - devinfo->nid, HDA_CMD_POWER_STATE_D3), - codec->cad); - continue; - } - - HDA_BOOTHVERBOSE( - device_printf(dev, - "Power up audio FG cad=%d nid=%d...\n", - devinfo->codec->cad, devinfo->nid); - ); - hdac_powerup(devinfo); - HDA_BOOTHVERBOSE( - device_printf(dev, "AFG commit...\n"); - ); - hdac_audio_commit(devinfo); - HDA_BOOTHVERBOSE( - device_printf(dev, "HP switch init...\n"); - ); - hdac_hp_switch_init(devinfo); - - hdac_unlock(sc); - for (i = 0; i < devinfo->function.audio.num_devs; i++) { - struct hdac_pcm_devinfo *pdevinfo = - &devinfo->function.audio.devs[i]; - HDA_BOOTHVERBOSE( - device_printf(pdevinfo->dev, - "OSS mixer reinitialization...\n"); - ); - if (mixer_reinit(pdevinfo->dev) == -1) - device_printf(pdevinfo->dev, - "unable to reinitialize the mixer\n"); - } - hdac_lock(sc); - } - } - - HDA_BOOTHVERBOSE( - device_printf(dev, "Start streams...\n"); - ); - for (i = 0; i < sc->num_chans; i++) { - if (sc->chans[i].flags & HDAC_CHN_SUSPEND) { - sc->chans[i].flags &= ~HDAC_CHN_SUSPEND; - hdac_channel_start(sc, &sc->chans[i]); - } - } - hdac_unlock(sc); + error = bus_generic_resume(dev); HDA_BOOTHVERBOSE( device_printf(dev, "Resume done\n"); ); - - return (0); + return (error); } + /**************************************************************************** * int hdac_detach(device_t) * @@ -8177,204 +1608,409 @@ hdac_resume(device_t dev) static int hdac_detach(device_t dev) { - struct hdac_softc *sc; + struct hdac_softc *sc = device_get_softc(dev); device_t *devlist; - int i, devcount, error; + int cad, i, devcount, error; if ((error = device_get_children(dev, &devlist, &devcount)) != 0) return (error); for (i = 0; i < devcount; i++) { + cad = (intptr_t)device_get_ivars(devlist[i]); if ((error = device_delete_child(dev, devlist[i])) != 0) { free(devlist, M_TEMP); return (error); } + sc->codecs[cad].dev = NULL; } free(devlist, M_TEMP); - sc = device_get_softc(dev); - hdac_release_resources(sc); + hdac_lock(sc); + hdac_reset(sc, 0); + hdac_unlock(sc); + taskqueue_drain(taskqueue_thread, &sc->unsolq_task); + hdac_irq_free(sc); + for (i = 0; i < sc->num_ss; i++) + hdac_dma_free(sc, &sc->streams[i].bdl); + free(sc->streams, M_HDAC); + hdac_dma_free(sc, &sc->pos_dma); + hdac_dma_free(sc, &sc->rirb_dma); + hdac_dma_free(sc, &sc->corb_dma); + if (sc->chan_dmat != NULL) { + bus_dma_tag_destroy(sc->chan_dmat); + sc->chan_dmat = NULL; + } + hdac_mem_free(sc); + snd_mtxfree(sc->lock); return (0); } +static bus_dma_tag_t +hdac_get_dma_tag(device_t dev, device_t child) +{ + struct hdac_softc *sc = device_get_softc(dev); + + return (sc->chan_dmat); +} + static int hdac_print_child(device_t dev, device_t child) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(child); int retval; retval = bus_print_child_header(dev, child); - retval += printf(" at cad %d nid %d", - pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid); + retval += printf(" at cad %d", + (int)(intptr_t)device_get_ivars(child)); retval += bus_print_child_footer(dev, child); return (retval); } -static device_method_t hdac_methods[] = { - /* device interface */ - DEVMETHOD(device_probe, hdac_probe), - DEVMETHOD(device_attach, hdac_attach), - DEVMETHOD(device_detach, hdac_detach), - DEVMETHOD(device_suspend, hdac_suspend), - DEVMETHOD(device_resume, hdac_resume), - /* Bus interface */ - DEVMETHOD(bus_print_child, hdac_print_child), - { 0, 0 } -}; +static int +hdac_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ -static driver_t hdac_driver = { - "hdac", - hdac_methods, - sizeof(struct hdac_softc), -}; + snprintf(buf, buflen, "cad=%d", + (int)(intptr_t)device_get_ivars(child)); + return (0); +} -static devclass_t hdac_devclass; +static int +hdac_child_pnpinfo_str_method(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdac_softc *sc = device_get_softc(dev); + nid_t cad = (uintptr_t)device_get_ivars(child); -DRIVER_MODULE(snd_hda, pci, hdac_driver, hdac_devclass, 0, 0); -MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); -MODULE_VERSION(snd_hda, 1); + snprintf(buf, buflen, "vendor=0x%04x device=0x%04x revision=0x%02x " + "stepping=0x%02x", + sc->codecs[cad].vendor_id, sc->codecs[cad].device_id, + sc->codecs[cad].revision_id, sc->codecs[cad].stepping_id); + return (0); +} static int -hdac_pcm_probe(device_t dev) +hdac_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(dev); - char buf[128]; - - snprintf(buf, sizeof(buf), "HDA %s PCM #%d %s", - hdac_codec_name(pdevinfo->devinfo->codec), - pdevinfo->index, - (pdevinfo->digital == 3)?"DisplayPort": - ((pdevinfo->digital == 2)?"HDMI": - ((pdevinfo->digital)?"Digital":"Analog"))); - device_set_desc_copy(dev, buf); + struct hdac_softc *sc = device_get_softc(dev); + nid_t cad = (uintptr_t)device_get_ivars(child); + + switch (which) { + case HDA_IVAR_CODEC_ID: + *result = cad; + break; + case HDA_IVAR_VENDOR_ID: + *result = sc->codecs[cad].vendor_id; + break; + case HDA_IVAR_DEVICE_ID: + *result = sc->codecs[cad].device_id; + break; + case HDA_IVAR_REVISION_ID: + *result = sc->codecs[cad].revision_id; + break; + case HDA_IVAR_STEPPING_ID: + *result = sc->codecs[cad].stepping_id; + break; + case HDA_IVAR_SUBVENDOR_ID: + *result = pci_get_subvendor(dev); + break; + case HDA_IVAR_SUBDEVICE_ID: + *result = pci_get_subdevice(dev); + break; + case HDA_IVAR_DMA_NOCACHE: + *result = (sc->flags & HDAC_F_DMA_NOCACHE) != 0; + break; + default: + return (ENOENT); + } return (0); } -static int -hdac_pcm_attach(device_t dev) +static struct mtx * +hdac_get_mtx(device_t dev, device_t child) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(dev); - struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; - char status[SND_STATUSLEN]; - int i; + struct hdac_softc *sc = device_get_softc(dev); - pdevinfo->chan_size = pcm_getbuffersize(dev, - HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); + return (sc->lock); +} - HDA_BOOTVERBOSE( - device_printf(dev, "+--------------------------------------+\n"); - device_printf(dev, "| DUMPING PCM Playback/Record Channels |\n"); - device_printf(dev, "+--------------------------------------+\n"); - hdac_dump_pcmchannels(pdevinfo); - device_printf(dev, "\n"); - device_printf(dev, "+-------------------------------+\n"); - device_printf(dev, "| DUMPING Playback/Record Paths |\n"); - device_printf(dev, "+-------------------------------+\n"); - hdac_dump_dac(pdevinfo); - hdac_dump_adc(pdevinfo); - hdac_dump_mix(pdevinfo); - device_printf(dev, "\n"); - device_printf(dev, "+-------------------------+\n"); - device_printf(dev, "| DUMPING Volume Controls |\n"); - device_printf(dev, "+-------------------------+\n"); - hdac_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME); - hdac_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM); - hdac_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD); - hdac_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC); - hdac_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR); - hdac_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE); - hdac_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER); - hdac_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV); - hdac_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX); - hdac_dump_ctls(pdevinfo, "Input Monitoring Level", SOUND_MASK_IGAIN); - hdac_dump_ctls(pdevinfo, NULL, 0); - device_printf(dev, "\n"); - ); +static uint32_t +hdac_codec_command(device_t dev, device_t child, uint32_t verb) +{ - if (resource_int_value(device_get_name(dev), - device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { - i &= HDA_BLK_ALIGN; - if (i < HDA_BLK_MIN) - i = HDA_BLK_MIN; - pdevinfo->chan_blkcnt = pdevinfo->chan_size / i; - i = 0; - while (pdevinfo->chan_blkcnt >> i) - i++; - pdevinfo->chan_blkcnt = 1 << (i - 1); - if (pdevinfo->chan_blkcnt < HDA_BDL_MIN) - pdevinfo->chan_blkcnt = HDA_BDL_MIN; - else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX) - pdevinfo->chan_blkcnt = HDA_BDL_MAX; - } else - pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT; - - /* - * We don't register interrupt handler with snd_setup_intr - * in pcm device. Mark pcm device as MPSAFE manually. - */ - pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + return (hdac_send_command(device_get_softc(dev), + (intptr_t)device_get_ivars(child), verb)); +} - HDA_BOOTHVERBOSE( - device_printf(dev, "OSS mixer initialization...\n"); - ); - if (mixer_init(dev, &hdac_audio_ctl_ossmixer_class, pdevinfo) != 0) - device_printf(dev, "Can't register mixer\n"); +static int +hdac_find_stream(struct hdac_softc *sc, int dir, int stream) +{ + int i, ss; - HDA_BOOTHVERBOSE( - device_printf(dev, "Registering PCM channels...\n"); - ); - if (pcm_register(dev, pdevinfo, (pdevinfo->play >= 0)?1:0, - (pdevinfo->rec >= 0)?1:0) != 0) - device_printf(dev, "Can't register PCM\n"); + ss = -1; + /* Allocate ISS/BSS first. */ + if (dir == 0) { + for (i = 0; i < sc->num_iss; i++) { + if (sc->streams[i].stream == stream) { + ss = i; + break; + } + } + } else { + for (i = 0; i < sc->num_oss; i++) { + if (sc->streams[i + sc->num_iss].stream == stream) { + ss = i + sc->num_iss; + break; + } + } + } + /* Fallback to BSS. */ + if (ss == -1) { + for (i = 0; i < sc->num_bss; i++) { + if (sc->streams[i + sc->num_iss + sc->num_oss].stream + == stream) { + ss = i + sc->num_iss + sc->num_oss; + break; + } + } + } + return (ss); +} - pdevinfo->registered++; +static int +hdac_stream_alloc(device_t dev, device_t child, int dir, int format, + uint32_t **dmapos) +{ + struct hdac_softc *sc = device_get_softc(dev); + int stream, ss; - if (pdevinfo->play >= 0) - pcm_addchan(dev, PCMDIR_PLAY, &hdac_channel_class, pdevinfo); - if (pdevinfo->rec >= 0) - pcm_addchan(dev, PCMDIR_REC, &hdac_channel_class, pdevinfo); + /* Look for empty stream. */ + ss = hdac_find_stream(sc, dir, 0); - snprintf(status, SND_STATUSLEN, "at cad %d nid %d on %s %s", - pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid, - device_get_nameunit(sc->dev), PCM_KLDSTRING(snd_hda)); - pcm_setstatus(dev, status); + /* Return if found nothing. */ + if (ss < 0) + return (0); - return (0); + /* Allocate stream number */ + if (ss >= sc->num_iss + sc->num_oss) + stream = 15 - (ss - sc->num_iss + sc->num_oss); + else if (ss >= sc->num_iss) + stream = ss - sc->num_iss + 1; + else + stream = ss + 1; + + sc->streams[ss].dev = child; + sc->streams[ss].dir = dir; + sc->streams[ss].stream = stream; + sc->streams[ss].format = format; + if (dmapos != NULL) { + if (sc->pos_dma.dma_vaddr != NULL) + *dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + ss * 8); + else + *dmapos = NULL; + } + return (stream); +} + +static void +hdac_stream_free(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int ss; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Free for not allocated stream (%d/%d)\n", dir, stream)); + sc->streams[ss].stream = 0; + sc->streams[ss].dev = NULL; } static int -hdac_pcm_detach(device_t dev) +hdac_stream_start(device_t dev, device_t child, + int dir, int stream, bus_addr_t buf, int blksz, int blkcnt) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(dev); - int err; - - if (pdevinfo->registered > 0) { - err = pcm_unregister(dev); - if (err != 0) - return (err); + struct hdac_softc *sc = device_get_softc(dev); + struct hdac_bdle *bdle; + uint64_t addr; + int i, ss, off; + uint32_t ctl; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Start for not allocated stream (%d/%d)\n", dir, stream)); + + addr = (uint64_t)buf; + bdle = (struct hdac_bdle *)sc->streams[ss].bdl.dma_vaddr; + for (i = 0; i < blkcnt; i++, bdle++) { + bdle->addrl = (uint32_t)addr; + bdle->addrh = (uint32_t)(addr >> 32); + bdle->len = blksz; + bdle->ioc = 1; + addr += blksz; } + off = ss << 5; + HDAC_WRITE_4(&sc->mem, off + HDAC_SDCBL, blksz * blkcnt); + HDAC_WRITE_2(&sc->mem, off + HDAC_SDLVI, blkcnt - 1); + addr = sc->streams[ss].bdl.dma_paddr; + HDAC_WRITE_4(&sc->mem, off + HDAC_SDBDPL, (uint32_t)addr); + HDAC_WRITE_4(&sc->mem, off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); + + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL2); + if (dir) + ctl |= HDAC_SDCTL2_DIR; + else + ctl &= ~HDAC_SDCTL2_DIR; + ctl &= ~HDAC_SDCTL2_STRM_MASK; + ctl |= stream << HDAC_SDCTL2_STRM_SHIFT; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL2, ctl); + + HDAC_WRITE_2(&sc->mem, off + HDAC_SDFMT, sc->streams[ss].format); + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= 1 << ss; + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + + sc->streams[ss].blksz = blksz; + sc->streams[ss].running = 1; + hdac_poll_reinit(sc); return (0); } -static device_method_t hdac_pcm_methods[] = { +static void +hdac_stream_stop(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int ss, off; + uint32_t ctl; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Stop for not allocated stream (%d/%d)\n", dir, stream)); + + off = ss << 5; + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN); + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~(1 << ss); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + + sc->streams[ss].running = 0; + hdac_poll_reinit(sc); +} + +static void +hdac_stream_reset(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int timeout = 1000; + int to = timeout; + int ss, off; + uint32_t ctl; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Reset for not allocated stream (%d/%d)\n", dir, stream)); + + off = ss << 5; + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + do { + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + if (ctl & HDAC_SDCTL_SRST) + break; + DELAY(10); + } while (--to); + if (!(ctl & HDAC_SDCTL_SRST)) + device_printf(dev, "Reset setting timeout\n"); + ctl &= ~HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + to = timeout; + do { + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + if (!(ctl & HDAC_SDCTL_SRST)) + break; + DELAY(10); + } while (--to); + if (ctl & HDAC_SDCTL_SRST) + device_printf(dev, "Reset timeout!\n"); +} + +static uint32_t +hdac_stream_getptr(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int ss, off; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Reset for not allocated stream (%d/%d)\n", dir, stream)); + + off = ss << 5; + return (HDAC_READ_4(&sc->mem, off + HDAC_SDLPIB)); +} + +static int +hdac_unsol_alloc(device_t dev, device_t child, int tag) +{ + struct hdac_softc *sc = device_get_softc(dev); + + sc->unsol_registered++; + hdac_poll_reinit(sc); + return (tag); +} + +static void +hdac_unsol_free(device_t dev, device_t child, int tag) +{ + struct hdac_softc *sc = device_get_softc(dev); + + sc->unsol_registered--; + hdac_poll_reinit(sc); +} + +static device_method_t hdac_methods[] = { /* device interface */ - DEVMETHOD(device_probe, hdac_pcm_probe), - DEVMETHOD(device_attach, hdac_pcm_attach), - DEVMETHOD(device_detach, hdac_pcm_detach), + DEVMETHOD(device_probe, hdac_probe), + DEVMETHOD(device_attach, hdac_attach), + DEVMETHOD(device_detach, hdac_detach), + DEVMETHOD(device_suspend, hdac_suspend), + DEVMETHOD(device_resume, hdac_resume), + /* Bus interface */ + DEVMETHOD(bus_get_dma_tag, hdac_get_dma_tag), + DEVMETHOD(bus_print_child, hdac_print_child), + DEVMETHOD(bus_child_location_str, hdac_child_location_str), + DEVMETHOD(bus_child_pnpinfo_str, hdac_child_pnpinfo_str_method), + DEVMETHOD(bus_read_ivar, hdac_read_ivar), + DEVMETHOD(hdac_get_mtx, hdac_get_mtx), + DEVMETHOD(hdac_codec_command, hdac_codec_command), + DEVMETHOD(hdac_stream_alloc, hdac_stream_alloc), + DEVMETHOD(hdac_stream_free, hdac_stream_free), + DEVMETHOD(hdac_stream_start, hdac_stream_start), + DEVMETHOD(hdac_stream_stop, hdac_stream_stop), + DEVMETHOD(hdac_stream_reset, hdac_stream_reset), + DEVMETHOD(hdac_stream_getptr, hdac_stream_getptr), + DEVMETHOD(hdac_unsol_alloc, hdac_unsol_alloc), + DEVMETHOD(hdac_unsol_free, hdac_unsol_free), { 0, 0 } }; -static driver_t hdac_pcm_driver = { - "pcm", - hdac_pcm_methods, - PCM_SOFTC_SIZE, +static driver_t hdac_driver = { + "hdac", + hdac_methods, + sizeof(struct hdac_softc), }; -DRIVER_MODULE(snd_hda_pcm, hdac, hdac_pcm_driver, pcm_devclass, 0, 0); +static devclass_t hdac_devclass; +DRIVER_MODULE(snd_hda, pci, hdac_driver, hdac_devclass, 0, 0); diff --git a/sys/dev/sound/pci/hda/hdac.h b/sys/dev/sound/pci/hda/hdac.h index 298afde..fd2390f 100644 --- a/sys/dev/sound/pci/hda/hdac.h +++ b/sys/dev/sound/pci/hda/hdac.h @@ -29,40 +29,546 @@ #ifndef _HDAC_H_ #define _HDAC_H_ +#include "hdac_if.h" -#if 0 /**************************************************************************** * Miscellanious defines ****************************************************************************/ +/* Controller models */ +#define HDA_MODEL_CONSTRUCT(vendor, model) \ + (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) + +/* Intel */ +#define INTEL_VENDORID 0x8086 +#define HDA_INTEL_CPT HDA_MODEL_CONSTRUCT(INTEL, 0x1c20) +#define HDA_INTEL_PATSBURG HDA_MODEL_CONSTRUCT(INTEL, 0x1d20) +#define HDA_INTEL_PPT1 HDA_MODEL_CONSTRUCT(INTEL, 0x1e20) +#define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) +#define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) +#define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) +#define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) +#define HDA_INTEL_82801I HDA_MODEL_CONSTRUCT(INTEL, 0x293e) +#define HDA_INTEL_82801JI HDA_MODEL_CONSTRUCT(INTEL, 0x3a3e) +#define HDA_INTEL_82801JD HDA_MODEL_CONSTRUCT(INTEL, 0x3a6e) +#define HDA_INTEL_PCH HDA_MODEL_CONSTRUCT(INTEL, 0x3b56) +#define HDA_INTEL_PCH2 HDA_MODEL_CONSTRUCT(INTEL, 0x3b57) +#define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b) +#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_MCP61_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) +#define HDA_NVIDIA_MCP61_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) +#define HDA_NVIDIA_MCP65_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) +#define HDA_NVIDIA_MCP65_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) +#define HDA_NVIDIA_MCP67_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055c) +#define HDA_NVIDIA_MCP67_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055d) +#define HDA_NVIDIA_MCP78_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0774) +#define HDA_NVIDIA_MCP78_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0775) +#define HDA_NVIDIA_MCP78_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0776) +#define HDA_NVIDIA_MCP78_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0777) +#define HDA_NVIDIA_MCP73_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fc) +#define HDA_NVIDIA_MCP73_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fd) +#define HDA_NVIDIA_MCP79_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac0) +#define HDA_NVIDIA_MCP79_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac1) +#define HDA_NVIDIA_MCP79_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac2) +#define HDA_NVIDIA_MCP79_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac3) +#define HDA_NVIDIA_MCP89_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d94) +#define HDA_NVIDIA_MCP89_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d95) +#define HDA_NVIDIA_MCP89_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d96) +#define HDA_NVIDIA_MCP89_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d97) +#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_RS600 HDA_MODEL_CONSTRUCT(ATI, 0x793b) +#define HDA_ATI_RS690 HDA_MODEL_CONSTRUCT(ATI, 0x7919) +#define HDA_ATI_RS780 HDA_MODEL_CONSTRUCT(ATI, 0x960f) +#define HDA_ATI_R600 HDA_MODEL_CONSTRUCT(ATI, 0xaa00) +#define HDA_ATI_RV630 HDA_MODEL_CONSTRUCT(ATI, 0xaa08) +#define HDA_ATI_RV610 HDA_MODEL_CONSTRUCT(ATI, 0xaa10) +#define HDA_ATI_RV670 HDA_MODEL_CONSTRUCT(ATI, 0xaa18) +#define HDA_ATI_RV635 HDA_MODEL_CONSTRUCT(ATI, 0xaa20) +#define HDA_ATI_RV620 HDA_MODEL_CONSTRUCT(ATI, 0xaa28) +#define HDA_ATI_RV770 HDA_MODEL_CONSTRUCT(ATI, 0xaa30) +#define HDA_ATI_RV730 HDA_MODEL_CONSTRUCT(ATI, 0xaa38) +#define HDA_ATI_RV710 HDA_MODEL_CONSTRUCT(ATI, 0xaa40) +#define HDA_ATI_RV740 HDA_MODEL_CONSTRUCT(ATI, 0xaa48) +#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) + +/* RDC */ +#define RDC_VENDORID 0x17f3 +#define HDA_RDC_M3010 HDA_MODEL_CONSTRUCT(RDC, 0x3010) + +/* 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) + +/* ULI */ +#define ULI_VENDORID 0x10b9 +#define HDA_ULI_M5461 HDA_MODEL_CONSTRUCT(ULI, 0x5461) +#define HDA_ULI_ALL HDA_MODEL_CONSTRUCT(ULI, 0xffff) + +/* OEM/subvendors */ + +/* Intel */ +#define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) + +/* 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_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) +#define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) +#define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) +#define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) +#define HP_DC7700S_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2801) +#define HP_DC7700_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2802) +#define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) +/* What is wrong with XN 2563 anyway? (Got the picture ?) */ +#define HP_NX6325_SUBVENDORX 0x103c30b0 + +/* Dell */ +#define DELL_VENDORID 0x1028 +#define DELL_D630_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01f9) +#define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) +#define DELL_V1400_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0227) +#define DELL_V1500_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0228) +#define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) +#define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) +#define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) +#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_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) +#define ACER_A4520_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0127) +#define ACER_A4710_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x012f) +#define ACER_A4715_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0133) +#define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) +#define ACER_T6292_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011b) +#define ACER_T5320_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011f) +#define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) + +/* Asus */ +#define ASUS_VENDORID 0x1043 +#define ASUS_A8X_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) +#define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) +#define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) +#define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339) +#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) +#define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) +#define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) +#define ASUS_P5PL2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x817f) +#define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_M2V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81e7) +#define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec) +#define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) +#define ASUS_A8NVMCSM_SUBVENDOR HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84) +#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) + +/* Lenovo */ +#define LENOVO_VENDORID 0x17aa +#define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) +#define LENOVO_3KN200_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e) +#define LENOVO_B450_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3a0d) +#define LENOVO_TCA55_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x1015) +#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) + +/* Samsung */ +#define SAMSUNG_VENDORID 0x144d +#define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) +#define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) + +/* Medion ? */ +#define MEDION_VENDORID 0x161f +#define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) +#define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) + +/* Apple Computer Inc. */ +#define APPLE_VENDORID 0x106b +#define APPLE_MB3_SUBVENDOR HDA_MODEL_CONSTRUCT(APPLE, 0x00a1) + +/* Sony */ +#define SONY_VENDORID 0x104d +#define SONY_S5_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0x81cc) +#define SONY_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0xffff) + +/* + * Apple Intel MacXXXX seems using Sigmatel codec/vendor id + * instead of their own, which is beyond my comprehension + * (see HDA_CODEC_STAC9221 below). + */ +#define APPLE_INTEL_MAC 0x76808384 +#define APPLE_MACBOOKPRO55 0xcb7910de + +/* LG Electronics */ +#define LG_VENDORID 0x1854 +#define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) +#define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) + +/* Fujitsu Siemens */ +#define FS_VENDORID 0x1734 +#define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) +#define FS_SI1848_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10cd) +#define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) + +/* Fujitsu Limited */ +#define FL_VENDORID 0x10cf +#define FL_S7020D_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x1326) +#define FL_U1010_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x142d) +#define FL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0xffff) + +/* Toshiba */ +#define TOSHIBA_VENDORID 0x1179 +#define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) +#define TOSHIBA_A135_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xff01) +#define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) + +/* Micro-Star International (MSI) */ +#define MSI_VENDORID 0x1462 +#define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) +#define MSI_MS034A_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x034a) +#define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) + +/* Giga-Byte Technology */ +#define GB_VENDORID 0x1458 +#define GB_G33S2H_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xa022) +#define GP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xffff) + +/* Uniwill ? */ +#define UNIWILL_VENDORID 0x1584 +#define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) +#define UNIWILL_9080_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9080) + +/* All codecs you can eat... */ +#define HDA_CODEC_CONSTRUCT(vendor, id) \ + (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) + +/* Cirrus Logic */ +#define CIRRUSLOGIC_VENDORID 0x1013 +#define HDA_CODEC_CS4206 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4206) +#define HDA_CODEC_CS4207 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4207) +#define HDA_CODEC_CS4210 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4210) +#define HDA_CODEC_CSXXXX HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0xffff) + +/* Realtek */ +#define REALTEK_VENDORID 0x10ec +#define HDA_CODEC_ALC221 HDA_CODEC_CONSTRUCT(REALTEK, 0x0221) +#define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) +#define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) +#define HDA_CODEC_ALC267 HDA_CODEC_CONSTRUCT(REALTEK, 0x0267) +#define HDA_CODEC_ALC268 HDA_CODEC_CONSTRUCT(REALTEK, 0x0268) +#define HDA_CODEC_ALC269 HDA_CODEC_CONSTRUCT(REALTEK, 0x0269) +#define HDA_CODEC_ALC270 HDA_CODEC_CONSTRUCT(REALTEK, 0x0270) +#define HDA_CODEC_ALC272 HDA_CODEC_CONSTRUCT(REALTEK, 0x0272) +#define HDA_CODEC_ALC273 HDA_CODEC_CONSTRUCT(REALTEK, 0x0273) +#define HDA_CODEC_ALC275 HDA_CODEC_CONSTRUCT(REALTEK, 0x0275) +#define HDA_CODEC_ALC276 HDA_CODEC_CONSTRUCT(REALTEK, 0x0276) +#define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660) +#define HDA_CODEC_ALC662 HDA_CODEC_CONSTRUCT(REALTEK, 0x0662) +#define HDA_CODEC_ALC663 HDA_CODEC_CONSTRUCT(REALTEK, 0x0663) +#define HDA_CODEC_ALC665 HDA_CODEC_CONSTRUCT(REALTEK, 0x0665) +#define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) +#define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) +#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_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) +#define HDA_CODEC_ALC887 HDA_CODEC_CONSTRUCT(REALTEK, 0x0887) +#define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) +#define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889) +#define HDA_CODEC_ALC892 HDA_CODEC_CONSTRUCT(REALTEK, 0x0892) +#define HDA_CODEC_ALC899 HDA_CODEC_CONSTRUCT(REALTEK, 0x0899) +#define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) + +/* Analog Devices */ +#define ANALOGDEVICES_VENDORID 0x11d4 +#define HDA_CODEC_AD1884A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x184a) +#define HDA_CODEC_AD1882 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1882) +#define HDA_CODEC_AD1883 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1883) +#define HDA_CODEC_AD1884 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1884) +#define HDA_CODEC_AD1984A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194a) +#define HDA_CODEC_AD1984B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194b) +#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) +#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) +#define HDA_CODEC_AD1984 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1984) +#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) +#define HDA_CODEC_AD1987 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1987) +#define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) +#define HDA_CODEC_AD1988B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b) +#define HDA_CODEC_AD1882A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x882a) +#define HDA_CODEC_AD1989A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989a) +#define HDA_CODEC_AD1989B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989b) +#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 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_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) +#define HDA_CODEC_STAC9230D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7613) +#define HDA_CODEC_STAC9229X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7614) +#define HDA_CODEC_STAC9229D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7615) +#define HDA_CODEC_STAC9228X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7616) +#define HDA_CODEC_STAC9228D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7617) +#define HDA_CODEC_STAC9227X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) +#define HDA_CODEC_STAC9227D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7619) +#define HDA_CODEC_STAC9274 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7620) +#define HDA_CODEC_STAC9274D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7621) +#define HDA_CODEC_STAC9273X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7622) +#define HDA_CODEC_STAC9273D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7623) +#define HDA_CODEC_STAC9272X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7624) +#define HDA_CODEC_STAC9272D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7625) +#define HDA_CODEC_STAC9271X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7626) +#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) +#define HDA_CODEC_STAC9274X5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7628) +#define HDA_CODEC_STAC9274D5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7629) +#define HDA_CODEC_STAC9250 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7634) +#define HDA_CODEC_STAC9251 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7636) +#define HDA_CODEC_IDT92HD700X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7638) +#define HDA_CODEC_IDT92HD700D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7639) +#define HDA_CODEC_IDT92HD206X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7645) +#define HDA_CODEC_IDT92HD206D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7646) +#define HDA_CODEC_CXD9872RDK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7661) +#define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) +#define HDA_CODEC_CXD9872AKD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7664) +#define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) +#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) +#define HDA_CODEC_STAC9221_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7682) +#define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) +#define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) +#define HDA_CODEC_STAC9200D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7691) +#define HDA_CODEC_IDT92HD005 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7698) +#define HDA_CODEC_IDT92HD005D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7699) +#define HDA_CODEC_STAC9205X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a0) +#define HDA_CODEC_STAC9205D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a1) +#define HDA_CODEC_STAC9204X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a2) +#define HDA_CODEC_STAC9204D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a3) +#define HDA_CODEC_STAC9220_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7880) +#define HDA_CODEC_STAC9220_A1 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7882) +#define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) + +/* IDT */ +#define IDT_VENDORID 0x111d +#define HDA_CODEC_IDT92HD75BX HDA_CODEC_CONSTRUCT(IDT, 0x7603) +#define HDA_CODEC_IDT92HD83C1X HDA_CODEC_CONSTRUCT(IDT, 0x7604) +#define HDA_CODEC_IDT92HD81B1X HDA_CODEC_CONSTRUCT(IDT, 0x7605) +#define HDA_CODEC_IDT92HD75B3 HDA_CODEC_CONSTRUCT(IDT, 0x7608) +#define HDA_CODEC_IDT92HD73D1 HDA_CODEC_CONSTRUCT(IDT, 0x7674) +#define HDA_CODEC_IDT92HD73C1 HDA_CODEC_CONSTRUCT(IDT, 0x7675) +#define HDA_CODEC_IDT92HD73E1 HDA_CODEC_CONSTRUCT(IDT, 0x7676) +#define HDA_CODEC_IDT92HD71B8 HDA_CODEC_CONSTRUCT(IDT, 0x76b0) +#define HDA_CODEC_IDT92HD71B7 HDA_CODEC_CONSTRUCT(IDT, 0x76b2) +#define HDA_CODEC_IDT92HD71B5 HDA_CODEC_CONSTRUCT(IDT, 0x76b6) +#define HDA_CODEC_IDT92HD83C1C HDA_CODEC_CONSTRUCT(IDT, 0x76d4) +#define HDA_CODEC_IDT92HD81B1C HDA_CODEC_CONSTRUCT(IDT, 0x76d5) +#define HDA_CODEC_IDTXXXX HDA_CODEC_CONSTRUCT(IDT, 0xffff) + +/* Silicon Image */ +#define SII_VENDORID 0x1095 +#define HDA_CODEC_SII1390 HDA_CODEC_CONSTRUCT(SII, 0x1390) +#define HDA_CODEC_SII1392 HDA_CODEC_CONSTRUCT(SII, 0x1392) +#define HDA_CODEC_SIIXXXX HDA_CODEC_CONSTRUCT(SII, 0xffff) + +/* Lucent/Agere */ +#define AGERE_VENDORID 0x11c1 +#define HDA_CODEC_AGEREXXXX HDA_CODEC_CONSTRUCT(AGERE, 0xffff) + +/* Conexant */ +#define CONEXANT_VENDORID 0x14f1 +#define HDA_CODEC_CX20549 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) +#define HDA_CODEC_CX20551 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) +#define HDA_CODEC_CX20561 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5051) +#define HDA_CODEC_CX20582 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5066) +#define HDA_CODEC_CX20583 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5067) +#define HDA_CODEC_CX20584 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5068) +#define HDA_CODEC_CX20585 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5069) +#define HDA_CODEC_CX20588 HDA_CODEC_CONSTRUCT(CONEXANT, 0x506c) +#define HDA_CODEC_CX20590 HDA_CODEC_CONSTRUCT(CONEXANT, 0x506e) +#define HDA_CODEC_CX20631 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5097) +#define HDA_CODEC_CX20632 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5098) +#define HDA_CODEC_CX20641 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a1) +#define HDA_CODEC_CX20642 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a2) +#define HDA_CODEC_CX20651 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ab) +#define HDA_CODEC_CX20652 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ac) +#define HDA_CODEC_CX20664 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b8) +#define HDA_CODEC_CX20665 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b9) +#define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) + +/* VIA */ +#define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) +#define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) +#define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) +#define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) +#define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) +#define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) +#define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) +#define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) +#define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) +#define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) +#define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) +#define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) +#define HDA_CODEC_VT1708B_0 HDA_CODEC_CONSTRUCT(VIA, 0xe720) +#define HDA_CODEC_VT1708B_1 HDA_CODEC_CONSTRUCT(VIA, 0xe721) +#define HDA_CODEC_VT1708B_2 HDA_CODEC_CONSTRUCT(VIA, 0xe722) +#define HDA_CODEC_VT1708B_3 HDA_CODEC_CONSTRUCT(VIA, 0xe723) +#define HDA_CODEC_VT1708B_4 HDA_CODEC_CONSTRUCT(VIA, 0xe724) +#define HDA_CODEC_VT1708B_5 HDA_CODEC_CONSTRUCT(VIA, 0xe725) +#define HDA_CODEC_VT1708B_6 HDA_CODEC_CONSTRUCT(VIA, 0xe726) +#define HDA_CODEC_VT1708B_7 HDA_CODEC_CONSTRUCT(VIA, 0xe727) +#define HDA_CODEC_VT1708S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0397) +#define HDA_CODEC_VT1708S_1 HDA_CODEC_CONSTRUCT(VIA, 0x1397) +#define HDA_CODEC_VT1708S_2 HDA_CODEC_CONSTRUCT(VIA, 0x2397) +#define HDA_CODEC_VT1708S_3 HDA_CODEC_CONSTRUCT(VIA, 0x3397) +#define HDA_CODEC_VT1708S_4 HDA_CODEC_CONSTRUCT(VIA, 0x4397) +#define HDA_CODEC_VT1708S_5 HDA_CODEC_CONSTRUCT(VIA, 0x5397) +#define HDA_CODEC_VT1708S_6 HDA_CODEC_CONSTRUCT(VIA, 0x6397) +#define HDA_CODEC_VT1708S_7 HDA_CODEC_CONSTRUCT(VIA, 0x7397) +#define HDA_CODEC_VT1702_0 HDA_CODEC_CONSTRUCT(VIA, 0x0398) +#define HDA_CODEC_VT1702_1 HDA_CODEC_CONSTRUCT(VIA, 0x1398) +#define HDA_CODEC_VT1702_2 HDA_CODEC_CONSTRUCT(VIA, 0x2398) +#define HDA_CODEC_VT1702_3 HDA_CODEC_CONSTRUCT(VIA, 0x3398) +#define HDA_CODEC_VT1702_4 HDA_CODEC_CONSTRUCT(VIA, 0x4398) +#define HDA_CODEC_VT1702_5 HDA_CODEC_CONSTRUCT(VIA, 0x5398) +#define HDA_CODEC_VT1702_6 HDA_CODEC_CONSTRUCT(VIA, 0x6398) +#define HDA_CODEC_VT1702_7 HDA_CODEC_CONSTRUCT(VIA, 0x7398) +#define HDA_CODEC_VT1716S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0433) +#define HDA_CODEC_VT1716S_1 HDA_CODEC_CONSTRUCT(VIA, 0xa721) +#define HDA_CODEC_VT1718S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0428) +#define HDA_CODEC_VT1718S_1 HDA_CODEC_CONSTRUCT(VIA, 0x4428) +#define HDA_CODEC_VT1802_0 HDA_CODEC_CONSTRUCT(VIA, 0x0446) +#define HDA_CODEC_VT1802_1 HDA_CODEC_CONSTRUCT(VIA, 0x8446) +#define HDA_CODEC_VT1812 HDA_CODEC_CONSTRUCT(VIA, 0x0448) +#define HDA_CODEC_VT1818S HDA_CODEC_CONSTRUCT(VIA, 0x0440) +#define HDA_CODEC_VT1828S HDA_CODEC_CONSTRUCT(VIA, 0x4441) +#define HDA_CODEC_VT2002P_0 HDA_CODEC_CONSTRUCT(VIA, 0x0438) +#define HDA_CODEC_VT2002P_1 HDA_CODEC_CONSTRUCT(VIA, 0x4438) +#define HDA_CODEC_VT2020 HDA_CODEC_CONSTRUCT(VIA, 0x0441) +#define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) + +/* ATI */ +#define HDA_CODEC_ATIRS600_1 HDA_CODEC_CONSTRUCT(ATI, 0x793c) +#define HDA_CODEC_ATIRS600_2 HDA_CODEC_CONSTRUCT(ATI, 0x7919) +#define HDA_CODEC_ATIRS690 HDA_CODEC_CONSTRUCT(ATI, 0x791a) +#define HDA_CODEC_ATIR6XX HDA_CODEC_CONSTRUCT(ATI, 0xaa01) +#define HDA_CODEC_ATIXXXX HDA_CODEC_CONSTRUCT(ATI, 0xffff) + +/* NVIDIA */ +#define HDA_CODEC_NVIDIAMCP78 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0002) +#define HDA_CODEC_NVIDIAMCP78_2 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0006) +#define HDA_CODEC_NVIDIAMCP7A HDA_CODEC_CONSTRUCT(NVIDIA, 0x0007) +#define HDA_CODEC_NVIDIAGT220 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000a) +#define HDA_CODEC_NVIDIAGT21X HDA_CODEC_CONSTRUCT(NVIDIA, 0x000b) +#define HDA_CODEC_NVIDIAMCP89 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000c) +#define HDA_CODEC_NVIDIAGT240 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000d) +#define HDA_CODEC_NVIDIAMCP67 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0067) +#define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001) +#define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) + +/* INTEL */ +#define HDA_CODEC_INTELIP HDA_CODEC_CONSTRUCT(INTEL, 0x0054) +#define HDA_CODEC_INTELBL HDA_CODEC_CONSTRUCT(INTEL, 0x2801) +#define HDA_CODEC_INTELCA HDA_CODEC_CONSTRUCT(INTEL, 0x2802) +#define HDA_CODEC_INTELEL HDA_CODEC_CONSTRUCT(INTEL, 0x2803) +#define HDA_CODEC_INTELIP2 HDA_CODEC_CONSTRUCT(INTEL, 0x2804) +#define HDA_CODEC_INTELCPT HDA_CODEC_CONSTRUCT(INTEL, 0x2805) +#define HDA_CODEC_INTELPPT HDA_CODEC_CONSTRUCT(INTEL, 0x2806) +#define HDA_CODEC_INTELCL HDA_CODEC_CONSTRUCT(INTEL, 0x29fb) +#define HDA_CODEC_INTELXXXX HDA_CODEC_CONSTRUCT(INTEL, 0xffff) + /**************************************************************************** * Helper Macros ****************************************************************************/ +#define HDA_DMA_ALIGNMENT 128 +#define HDA_GPIO_MAX 8 + +#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 HDA_INVALID 0xffffffff + +#define HDA_BOOTVERBOSE(stmt) do { \ + if (bootverbose != 0 || snd_verbose > 3) { \ + stmt \ + } \ +} while (0) + +#define HDA_BOOTHVERBOSE(stmt) do { \ + if (snd_verbose > 3) { \ + stmt \ + } \ +} while (0) + +#define hda_command(dev, verb) \ + HDAC_CODEC_COMMAND(device_get_parent(dev), (dev), (verb)) + +typedef int nid_t; + /**************************************************************************** * 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, + HDA_IVAR_CODEC_ID, + HDA_IVAR_NODE_ID, + HDA_IVAR_VENDOR_ID, + HDA_IVAR_DEVICE_ID, + HDA_IVAR_REVISION_ID, + HDA_IVAR_STEPPING_ID, + HDA_IVAR_SUBVENDOR_ID, + HDA_IVAR_SUBDEVICE_ID, + HDA_IVAR_SUBSYSTEM_ID, + HDA_IVAR_NODE_TYPE, + HDA_IVAR_DMA_NOCACHE, }; -#define HDAC_ACCESSOR(var, ivar, type) \ - __BUS_ACCESSOR(hdac, var, HDAC, ivar, type) +#define HDA_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(hda, var, HDA, 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 +HDA_ACCESSOR(codec_id, CODEC_ID, uint8_t); +HDA_ACCESSOR(node_id, NODE_ID, uint8_t); +HDA_ACCESSOR(vendor_id, VENDOR_ID, uint16_t); +HDA_ACCESSOR(device_id, DEVICE_ID, uint16_t); +HDA_ACCESSOR(revision_id, REVISION_ID, uint8_t); +HDA_ACCESSOR(stepping_id, STEPPING_ID, uint8_t); +HDA_ACCESSOR(subvendor_id, SUBVENDOR_ID, uint16_t); +HDA_ACCESSOR(subdevice_id, SUBDEVICE_ID, uint16_t); +HDA_ACCESSOR(node_type, NODE_TYPE, uint8_t); +HDA_ACCESSOR(dma_nocache, DMA_NOCACHE, uint8_t); #define PCIS_MULTIMEDIA_HDA 0x03 diff --git a/sys/dev/sound/pci/hda/hdac_if.m b/sys/dev/sound/pci/hda/hdac_if.m new file mode 100644 index 0000000..4fb77bd --- /dev/null +++ b/sys/dev/sound/pci/hda/hdac_if.m @@ -0,0 +1,114 @@ +# Copyright (c) 2012 Alexander Motin <mav@FreeBSD.org> +# 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, +# without modification, immediately at the beginning of the file. +# 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 ``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 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$ + +#include <sys/rman.h> + +INTERFACE hdac; + +METHOD struct mtx * get_mtx { + device_t dev; + device_t child; +}; + +METHOD uint32_t codec_command { + device_t dev; + device_t child; + uint32_t verb; +}; + +METHOD int stream_alloc { + device_t dev; + device_t child; + int dir; + int format; + uint32_t **dmapos; +}; + +METHOD void stream_free { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD int stream_start { + device_t dev; + device_t child; + int dir; + int stream; + bus_addr_t buf; + int blksz; + int blkcnt; +}; + +METHOD void stream_stop { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD void stream_reset { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD uint32_t stream_getptr { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD void stream_intr { + device_t dev; + int dir; + int stream; +}; + +METHOD int unsol_alloc { + device_t dev; + device_t child; + int wanted; +}; + +METHOD void unsol_free { + device_t dev; + device_t child; + int tag; +}; + +METHOD void unsol_intr { + device_t dev; + uint32_t resp; +}; + +METHOD void pindump { + device_t dev; +}; + diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h index 24beed7..8532f83 100644 --- a/sys/dev/sound/pci/hda/hdac_private.h +++ b/sys/dev/sound/pci/hda/hdac_private.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,15 +30,11 @@ #ifndef _HDAC_PRIVATE_H_ #define _HDAC_PRIVATE_H_ - /**************************************************************************** * Miscellaneous defines ****************************************************************************/ -#define HDAC_DMA_ALIGNMENT 128 #define HDAC_CODEC_MAX 16 -#define HDAC_MTX_NAME "hdac driver mutex" - /**************************************************************************** * Helper Macros ****************************************************************************/ @@ -82,7 +79,6 @@ #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 ****************************************************************************/ @@ -145,22 +141,6 @@ struct hdac_rirb { (((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_bdle { volatile uint32_t addrl; volatile uint32_t addrh; @@ -168,181 +148,36 @@ struct hdac_bdle { volatile uint32_t ioc; } __packed; -#define HDA_MAX_CONNS 32 -#define HDA_MAX_NAMELEN 32 - -struct hdac_widget { - nid_t nid; - int type; - int enable; - int nconns, selconn; - int waspin; - uint32_t pflags; - int bindas; - int bindseqmask; - int ossdev; - uint32_t ossmask; - nid_t conns[HDA_MAX_CONNS]; - u_char connsenable[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; - } 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, dir, ndir; - int mute, step, size, offset; - int left, right, forcemute; - uint32_t muted; - uint32_t ossmask, possmask; -}; - -/* Association is a group of pins bound for some special function. */ -struct hdac_audio_as { - u_char enable; - u_char index; - u_char dir; - u_char pincnt; - u_char fakeredir; - u_char digital; - uint16_t pinset; - nid_t hpredir; - nid_t pins[16]; - nid_t dacs[16]; - int chan; -}; - -struct hdac_pcm_devinfo { - device_t dev; - struct hdac_devinfo *devinfo; - int index; - int registered; - int play, rec; - u_char left[SOUND_MIXER_NRDEVICES]; - u_char right[SOUND_MIXER_NRDEVICES]; - int chan_size; - int chan_blkcnt; - u_char digital; -}; - -/**************************************************************************** - * 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 { - 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, ascnt; - struct hdac_audio_ctl *ctl; - struct hdac_audio_as *as; - uint32_t quirks; - uint32_t gpio; - struct hdac_pcm_devinfo *devs; - int num_devs; - } audio; - /* XXX undefined: modem, hdmi. */ - } function; -}; - -#define HDAC_CHN_RUNNING 0x00000001 -#define HDAC_CHN_SUSPEND 0x00000002 - -struct hdac_chan { - struct snd_dbuf *b; - struct pcm_channel *c; - struct pcmchan_caps caps; - struct hdac_devinfo *devinfo; - struct hdac_pcm_devinfo *pdevinfo; - struct hdac_dma bdl_dma; - uint32_t spd, fmt, fmtlist[16], pcmrates[16]; - uint32_t supp_stream_formats, supp_pcm_size_rate; - uint32_t ptr, prevptr, blkcnt, blksz; - uint32_t *dmapos; - uint32_t flags; - int dir; - int off; - int sid; - int bit16, bit32; - int as; - nid_t io[16]; -}; - -/**************************************************************************** - * struct hdac_codec - * - ****************************************************************************/ -struct hdac_codec { - int verbs_sent; - int responses_received; - nid_t cad; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision_id; - uint8_t stepping_id; - struct hdac_command_list *commands; - struct hdac_softc *sc; - struct hdac_devinfo *fgs; - int num_fgs; +struct hdac_stream { + device_t dev; + struct hdac_dma bdl; + int dir; + int stream; + int blksz; + int running; + uint16_t format; }; -/**************************************************************************** - * struct hdac_softc - * - * This structure holds the current state of the hdac driver. - ****************************************************************************/ - -#define HDAC_F_DMA_NOCACHE 0x00000001 -#define HDAC_F_MSI 0x00000002 - 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; + uint32_t quirks_on; + uint32_t quirks_off; uint32_t flags; +#define HDAC_F_DMA_NOCACHE 0x00000001 - struct hdac_chan *chans; - int num_chans; int num_iss; int num_oss; int num_bss; + int num_ss; int num_sdo; int support_64bit; - int streamcnt; int corb_size; struct hdac_dma corb_dma; @@ -356,18 +191,13 @@ struct hdac_softc { bus_dma_tag_t chan_dmat; - /* - * Polling - */ + /* Polling */ int polling; - int poll_ticks; int poll_ival; - struct callout poll_hda; - struct callout poll_hdac; - struct callout poll_jack; + struct callout poll_callout; + int unsol_registered; struct task unsolq_task; - #define HDAC_UNSOLQ_MAX 64 #define HDAC_UNSOLQ_READY 0 #define HDAC_UNSOLQ_BUSY 1 @@ -376,13 +206,17 @@ struct hdac_softc { int unsolq_st; uint32_t unsolq[HDAC_UNSOLQ_MAX]; - struct hdac_codec *codecs[HDAC_CODEC_MAX]; -}; + struct hdac_stream *streams; -/**************************************************************************** - * struct hdac_command flags - ****************************************************************************/ -#define HDAC_COMMAND_FLAG_WAITOK 0x0000 -#define HDAC_COMMAND_FLAG_NOWAIT 0x0001 + struct { + device_t dev; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision_id; + uint8_t stepping_id; + int pending; + uint32_t response; + } codecs[HDAC_CODEC_MAX]; +}; #endif diff --git a/sys/dev/sound/pci/hda/hdac_reg.h b/sys/dev/sound/pci/hda/hdac_reg.h index 813af72..893e3d2 100644 --- a/sys/dev/sound/pci/hda/hdac_reg.h +++ b/sys/dev/sound/pci/hda/hdac_reg.h @@ -254,10 +254,10 @@ #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_STRIPE_MASK 0x03 +#define HDAC_SDCTL2_STRIPE_SHIFT 0 +#define HDAC_SDCTL2_TP 0x04 +#define HDAC_SDCTL2_DIR 0x08 #define HDAC_SDCTL2_STRM_MASK 0xf0 #define HDAC_SDCTL2_STRM_SHIFT 4 diff --git a/sys/dev/sound/pci/hda/hdacc.c b/sys/dev/sound/pci/hda/hdacc.c new file mode 100644 index 0000000..47feb1b --- /dev/null +++ b/sys/dev/sound/pci/hda/hdacc.c @@ -0,0 +1,662 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * 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 (CODEC) driver for FreeBSD. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include <dev/sound/pcm/sound.h> + +#include <sys/ctype.h> + +#include <dev/sound/pci/hda/hda_reg.h> +#include <dev/sound/pci/hda/hdac.h> + +SND_DECLARE_FILE("$FreeBSD$"); + +struct hdacc_fg { + device_t dev; + nid_t nid; + uint8_t type; + uint32_t subsystem_id; +}; + +struct hdacc_softc { + device_t dev; + struct mtx *lock; + nid_t cad; + device_t streams[2][16]; + device_t tags[64]; + int fgcnt; + struct hdacc_fg *fgs; +}; + +#define hdacc_lock(codec) snd_mtxlock((codec)->lock) +#define hdacc_unlock(codec) snd_mtxunlock((codec)->lock) +#define hdacc_lockassert(codec) snd_mtxassert((codec)->lock) +#define hdacc_lockowned(codec) mtx_owned((codec)->lock) + +MALLOC_DEFINE(M_HDACC, "hdacc", "HDA CODEC"); + +/* CODECs */ +static const struct { + uint32_t id; + uint16_t revid; + char *name; +} hdacc_codecs[] = { + { HDA_CODEC_CS4206, 0, "Cirrus Logic CS4206" }, + { HDA_CODEC_CS4207, 0, "Cirrus Logic CS4207" }, + { HDA_CODEC_CS4210, 0, "Cirrus Logic CS4210" }, + { HDA_CODEC_ALC221, 0, "Realtek ALC221" }, + { HDA_CODEC_ALC260, 0, "Realtek ALC260" }, + { HDA_CODEC_ALC262, 0, "Realtek ALC262" }, + { HDA_CODEC_ALC267, 0, "Realtek ALC267" }, + { HDA_CODEC_ALC268, 0, "Realtek ALC268" }, + { HDA_CODEC_ALC269, 0, "Realtek ALC269" }, + { HDA_CODEC_ALC270, 0, "Realtek ALC270" }, + { HDA_CODEC_ALC272, 0, "Realtek ALC272" }, + { HDA_CODEC_ALC273, 0, "Realtek ALC273" }, + { HDA_CODEC_ALC275, 0, "Realtek ALC275" }, + { HDA_CODEC_ALC276, 0, "Realtek ALC276" }, + { HDA_CODEC_ALC660, 0, "Realtek ALC660-VD" }, + { HDA_CODEC_ALC662, 0x0002, "Realtek ALC662 rev2" }, + { HDA_CODEC_ALC662, 0, "Realtek ALC662" }, + { HDA_CODEC_ALC663, 0, "Realtek ALC663" }, + { HDA_CODEC_ALC665, 0, "Realtek ALC665" }, + { HDA_CODEC_ALC861, 0x0340, "Realtek ALC660" }, + { HDA_CODEC_ALC861, 0, "Realtek ALC861" }, + { HDA_CODEC_ALC861VD, 0, "Realtek ALC861-VD" }, + { HDA_CODEC_ALC880, 0, "Realtek ALC880" }, + { HDA_CODEC_ALC882, 0, "Realtek ALC882" }, + { HDA_CODEC_ALC883, 0, "Realtek ALC883" }, + { HDA_CODEC_ALC885, 0x0101, "Realtek ALC889A" }, + { HDA_CODEC_ALC885, 0x0103, "Realtek ALC889A" }, + { HDA_CODEC_ALC885, 0, "Realtek ALC885" }, + { HDA_CODEC_ALC887, 0, "Realtek ALC887" }, + { HDA_CODEC_ALC888, 0x0101, "Realtek ALC1200" }, + { HDA_CODEC_ALC888, 0, "Realtek ALC888" }, + { HDA_CODEC_ALC889, 0, "Realtek ALC889" }, + { HDA_CODEC_ALC892, 0, "Realtek ALC892" }, + { HDA_CODEC_ALC899, 0, "Realtek ALC899" }, + { HDA_CODEC_AD1882, 0, "Analog Devices AD1882" }, + { HDA_CODEC_AD1882A, 0, "Analog Devices AD1882A" }, + { HDA_CODEC_AD1883, 0, "Analog Devices AD1883" }, + { HDA_CODEC_AD1884, 0, "Analog Devices AD1884" }, + { HDA_CODEC_AD1884A, 0, "Analog Devices AD1884A" }, + { HDA_CODEC_AD1981HD, 0, "Analog Devices AD1981HD" }, + { HDA_CODEC_AD1983, 0, "Analog Devices AD1983" }, + { HDA_CODEC_AD1984, 0, "Analog Devices AD1984" }, + { HDA_CODEC_AD1984A, 0, "Analog Devices AD1984A" }, + { HDA_CODEC_AD1984B, 0, "Analog Devices AD1984B" }, + { HDA_CODEC_AD1986A, 0, "Analog Devices AD1986A" }, + { HDA_CODEC_AD1987, 0, "Analog Devices AD1987" }, + { HDA_CODEC_AD1988, 0, "Analog Devices AD1988A" }, + { HDA_CODEC_AD1988B, 0, "Analog Devices AD1988B" }, + { HDA_CODEC_AD1989A, 0, "Analog Devices AD1989A" }, + { HDA_CODEC_AD1989B, 0, "Analog Devices AD1989B" }, + { HDA_CODEC_CMI9880, 0, "CMedia CMI9880" }, + { HDA_CODEC_CXD9872RDK, 0, "Sigmatel CXD9872RD/K" }, + { HDA_CODEC_CXD9872AKD, 0, "Sigmatel CXD9872AKD" }, + { HDA_CODEC_STAC9200D, 0, "Sigmatel STAC9200D" }, + { HDA_CODEC_STAC9204X, 0, "Sigmatel STAC9204X" }, + { HDA_CODEC_STAC9204D, 0, "Sigmatel STAC9204D" }, + { HDA_CODEC_STAC9205X, 0, "Sigmatel STAC9205X" }, + { HDA_CODEC_STAC9205D, 0, "Sigmatel STAC9205D" }, + { HDA_CODEC_STAC9220, 0, "Sigmatel STAC9220" }, + { HDA_CODEC_STAC9220_A1, 0, "Sigmatel STAC9220_A1" }, + { HDA_CODEC_STAC9220_A2, 0, "Sigmatel STAC9220_A2" }, + { HDA_CODEC_STAC9221, 0, "Sigmatel STAC9221" }, + { HDA_CODEC_STAC9221_A2, 0, "Sigmatel STAC9221_A2" }, + { HDA_CODEC_STAC9221D, 0, "Sigmatel STAC9221D" }, + { HDA_CODEC_STAC922XD, 0, "Sigmatel STAC9220D/9223D" }, + { HDA_CODEC_STAC9227X, 0, "Sigmatel STAC9227X" }, + { HDA_CODEC_STAC9227D, 0, "Sigmatel STAC9227D" }, + { HDA_CODEC_STAC9228X, 0, "Sigmatel STAC9228X" }, + { HDA_CODEC_STAC9228D, 0, "Sigmatel STAC9228D" }, + { HDA_CODEC_STAC9229X, 0, "Sigmatel STAC9229X" }, + { HDA_CODEC_STAC9229D, 0, "Sigmatel STAC9229D" }, + { HDA_CODEC_STAC9230X, 0, "Sigmatel STAC9230X" }, + { HDA_CODEC_STAC9230D, 0, "Sigmatel STAC9230D" }, + { HDA_CODEC_STAC9250, 0, "Sigmatel STAC9250" }, + { HDA_CODEC_STAC9251, 0, "Sigmatel STAC9251" }, + { HDA_CODEC_STAC9271X, 0, "Sigmatel STAC9271X" }, + { HDA_CODEC_STAC9271D, 0, "Sigmatel STAC9271D" }, + { HDA_CODEC_STAC9272X, 0, "Sigmatel STAC9272X" }, + { HDA_CODEC_STAC9272D, 0, "Sigmatel STAC9272D" }, + { HDA_CODEC_STAC9273X, 0, "Sigmatel STAC9273X" }, + { HDA_CODEC_STAC9273D, 0, "Sigmatel STAC9273D" }, + { HDA_CODEC_STAC9274, 0, "Sigmatel STAC9274" }, + { HDA_CODEC_STAC9274D, 0, "Sigmatel STAC9274D" }, + { HDA_CODEC_STAC9274X5NH, 0, "Sigmatel STAC9274X5NH" }, + { HDA_CODEC_STAC9274D5NH, 0, "Sigmatel STAC9274D5NH" }, + { HDA_CODEC_STAC9872AK, 0, "Sigmatel STAC9872AK" }, + { HDA_CODEC_IDT92HD005, 0, "IDT 92HD005" }, + { HDA_CODEC_IDT92HD005D, 0, "IDT 92HD005D" }, + { HDA_CODEC_IDT92HD206X, 0, "IDT 92HD206X" }, + { HDA_CODEC_IDT92HD206D, 0, "IDT 92HD206D" }, + { HDA_CODEC_IDT92HD700X, 0, "IDT 92HD700X" }, + { HDA_CODEC_IDT92HD700D, 0, "IDT 92HD700D" }, + { HDA_CODEC_IDT92HD71B5, 0, "IDT 92HD71B5" }, + { HDA_CODEC_IDT92HD71B7, 0, "IDT 92HD71B7" }, + { HDA_CODEC_IDT92HD71B8, 0, "IDT 92HD71B8" }, + { HDA_CODEC_IDT92HD73C1, 0, "IDT 92HD73C1" }, + { HDA_CODEC_IDT92HD73D1, 0, "IDT 92HD73D1" }, + { HDA_CODEC_IDT92HD73E1, 0, "IDT 92HD73E1" }, + { HDA_CODEC_IDT92HD75B3, 0, "IDT 92HD75B3" }, + { HDA_CODEC_IDT92HD75BX, 0, "IDT 92HD75BX" }, + { HDA_CODEC_IDT92HD81B1C, 0, "IDT 92HD81B1C" }, + { HDA_CODEC_IDT92HD81B1X, 0, "IDT 92HD81B1X" }, + { HDA_CODEC_IDT92HD83C1C, 0, "IDT 92HD83C1C" }, + { HDA_CODEC_IDT92HD83C1X, 0, "IDT 92HD83C1X" }, + { HDA_CODEC_CX20549, 0, "Conexant CX20549 (Venice)" }, + { HDA_CODEC_CX20551, 0, "Conexant CX20551 (Waikiki)" }, + { HDA_CODEC_CX20561, 0, "Conexant CX20561 (Hermosa)" }, + { HDA_CODEC_CX20582, 0, "Conexant CX20582 (Pebble)" }, + { HDA_CODEC_CX20583, 0, "Conexant CX20583 (Pebble HSF)" }, + { HDA_CODEC_CX20584, 0, "Conexant CX20584" }, + { HDA_CODEC_CX20585, 0, "Conexant CX20585" }, + { HDA_CODEC_CX20588, 0, "Conexant CX20588" }, + { HDA_CODEC_CX20590, 0, "Conexant CX20590" }, + { HDA_CODEC_CX20631, 0, "Conexant CX20631" }, + { HDA_CODEC_CX20632, 0, "Conexant CX20632" }, + { HDA_CODEC_CX20641, 0, "Conexant CX20641" }, + { HDA_CODEC_CX20642, 0, "Conexant CX20642" }, + { HDA_CODEC_CX20651, 0, "Conexant CX20651" }, + { HDA_CODEC_CX20652, 0, "Conexant CX20652" }, + { HDA_CODEC_CX20664, 0, "Conexant CX20664" }, + { HDA_CODEC_CX20665, 0, "Conexant CX20665" }, + { HDA_CODEC_VT1708_8, 0, "VIA VT1708_8" }, + { HDA_CODEC_VT1708_9, 0, "VIA VT1708_9" }, + { HDA_CODEC_VT1708_A, 0, "VIA VT1708_A" }, + { HDA_CODEC_VT1708_B, 0, "VIA VT1708_B" }, + { HDA_CODEC_VT1709_0, 0, "VIA VT1709_0" }, + { HDA_CODEC_VT1709_1, 0, "VIA VT1709_1" }, + { HDA_CODEC_VT1709_2, 0, "VIA VT1709_2" }, + { HDA_CODEC_VT1709_3, 0, "VIA VT1709_3" }, + { HDA_CODEC_VT1709_4, 0, "VIA VT1709_4" }, + { HDA_CODEC_VT1709_5, 0, "VIA VT1709_5" }, + { HDA_CODEC_VT1709_6, 0, "VIA VT1709_6" }, + { HDA_CODEC_VT1709_7, 0, "VIA VT1709_7" }, + { HDA_CODEC_VT1708B_0, 0, "VIA VT1708B_0" }, + { HDA_CODEC_VT1708B_1, 0, "VIA VT1708B_1" }, + { HDA_CODEC_VT1708B_2, 0, "VIA VT1708B_2" }, + { HDA_CODEC_VT1708B_3, 0, "VIA VT1708B_3" }, + { HDA_CODEC_VT1708B_4, 0, "VIA VT1708B_4" }, + { HDA_CODEC_VT1708B_5, 0, "VIA VT1708B_5" }, + { HDA_CODEC_VT1708B_6, 0, "VIA VT1708B_6" }, + { HDA_CODEC_VT1708B_7, 0, "VIA VT1708B_7" }, + { HDA_CODEC_VT1708S_0, 0, "VIA VT1708S_0" }, + { HDA_CODEC_VT1708S_1, 0, "VIA VT1708S_1" }, + { HDA_CODEC_VT1708S_2, 0, "VIA VT1708S_2" }, + { HDA_CODEC_VT1708S_3, 0, "VIA VT1708S_3" }, + { HDA_CODEC_VT1708S_4, 0, "VIA VT1708S_4" }, + { HDA_CODEC_VT1708S_5, 0, "VIA VT1708S_5" }, + { HDA_CODEC_VT1708S_6, 0, "VIA VT1708S_6" }, + { HDA_CODEC_VT1708S_7, 0, "VIA VT1708S_7" }, + { HDA_CODEC_VT1702_0, 0, "VIA VT1702_0" }, + { HDA_CODEC_VT1702_1, 0, "VIA VT1702_1" }, + { HDA_CODEC_VT1702_2, 0, "VIA VT1702_2" }, + { HDA_CODEC_VT1702_3, 0, "VIA VT1702_3" }, + { HDA_CODEC_VT1702_4, 0, "VIA VT1702_4" }, + { HDA_CODEC_VT1702_5, 0, "VIA VT1702_5" }, + { HDA_CODEC_VT1702_6, 0, "VIA VT1702_6" }, + { HDA_CODEC_VT1702_7, 0, "VIA VT1702_7" }, + { HDA_CODEC_VT1716S_0, 0, "VIA VT1716S_0" }, + { HDA_CODEC_VT1716S_1, 0, "VIA VT1716S_1" }, + { HDA_CODEC_VT1718S_0, 0, "VIA VT1718S_0" }, + { HDA_CODEC_VT1718S_1, 0, "VIA VT1718S_1" }, + { HDA_CODEC_VT1802_0, 0, "VIA VT1802_0" }, + { HDA_CODEC_VT1802_1, 0, "VIA VT1802_1" }, + { HDA_CODEC_VT1812, 0, "VIA VT1812" }, + { HDA_CODEC_VT1818S, 0, "VIA VT1818S" }, + { HDA_CODEC_VT1828S, 0, "VIA VT1828S" }, + { HDA_CODEC_VT2002P_0, 0, "VIA VT2002P_0" }, + { HDA_CODEC_VT2002P_1, 0, "VIA VT2002P_1" }, + { HDA_CODEC_VT2020, 0, "VIA VT2020" }, + { HDA_CODEC_ATIRS600_1, 0, "ATI RS600" }, + { HDA_CODEC_ATIRS600_2, 0, "ATI RS600" }, + { HDA_CODEC_ATIRS690, 0, "ATI RS690/780" }, + { HDA_CODEC_ATIR6XX, 0, "ATI R6xx" }, + { HDA_CODEC_NVIDIAMCP67, 0, "NVIDIA MCP67" }, + { HDA_CODEC_NVIDIAMCP73, 0, "NVIDIA MCP73" }, + { HDA_CODEC_NVIDIAMCP78, 0, "NVIDIA MCP78" }, + { HDA_CODEC_NVIDIAMCP78_2, 0, "NVIDIA MCP78" }, + { HDA_CODEC_NVIDIAMCP7A, 0, "NVIDIA MCP7A" }, + { HDA_CODEC_NVIDIAGT220, 0, "NVIDIA GT220" }, + { HDA_CODEC_NVIDIAGT21X, 0, "NVIDIA GT21x" }, + { HDA_CODEC_NVIDIAMCP89, 0, "NVIDIA MCP89" }, + { HDA_CODEC_NVIDIAGT240, 0, "NVIDIA GT240" }, + { HDA_CODEC_INTELIP, 0, "Intel Ibex Peak" }, + { HDA_CODEC_INTELBL, 0, "Intel Bearlake" }, + { HDA_CODEC_INTELCA, 0, "Intel Cantiga" }, + { HDA_CODEC_INTELEL, 0, "Intel Eaglelake" }, + { HDA_CODEC_INTELIP2, 0, "Intel Ibex Peak" }, + { HDA_CODEC_INTELCPT, 0, "Intel Cougar Point" }, + { HDA_CODEC_INTELPPT, 0, "Intel Panther Point" }, + { HDA_CODEC_INTELCL, 0, "Intel Crestline" }, + { HDA_CODEC_SII1390, 0, "Silicon Image SiI1390" }, + { HDA_CODEC_SII1392, 0, "Silicon Image SiI1392" }, + /* Unknown CODECs */ + { HDA_CODEC_ALCXXXX, 0, "Realtek (Unknown)" }, + { HDA_CODEC_ADXXXX, 0, "Analog Devices (Unknown)" }, + { HDA_CODEC_CSXXXX, 0, "Cirrus Logic (Unknown)" }, + { HDA_CODEC_CMIXXXX, 0, "CMedia (Unknown)" }, + { HDA_CODEC_STACXXXX, 0, "Sigmatel (Unknown)" }, + { HDA_CODEC_SIIXXXX, 0, "Silicon Image (Unknown)" }, + { HDA_CODEC_AGEREXXXX, 0, "Lucent/Agere Systems (Unknown)" }, + { HDA_CODEC_CXXXXX, 0, "Conexant (Unknown)" }, + { HDA_CODEC_VTXXXX, 0, "VIA (Unknown)" }, + { HDA_CODEC_ATIXXXX, 0, "ATI (Unknown)" }, + { HDA_CODEC_NVIDIAXXXX, 0, "NVIDIA (Unknown)" }, + { HDA_CODEC_INTELXXXX, 0, "Intel (Unknown)" }, + { HDA_CODEC_IDTXXXX, 0, "IDT (Unknown)" }, +}; +#define HDACC_CODECS_LEN (sizeof(hdacc_codecs) / sizeof(hdacc_codecs[0])) + + +/**************************************************************************** + * Function prototypes + ****************************************************************************/ + +static char * +hdacc_codec_name(uint32_t id, uint16_t revid) +{ + int i; + + for (i = 0; i < HDACC_CODECS_LEN; i++) { + if (!HDA_DEV_MATCH(hdacc_codecs[i].id, id)) + continue; + if (hdacc_codecs[i].revid != 0 && + hdacc_codecs[i].revid != revid) + continue; + return (hdacc_codecs[i].name); + } + + return ((id == 0x00000000) ? "NULL CODEC" : "Unknown CODEC"); +} + +static int +hdacc_suspend(device_t dev) +{ + + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend...\n"); + ); + bus_generic_suspend(dev); + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend done\n"); + ); + return (0); +} + +static int +hdacc_resume(device_t dev) +{ + + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume...\n"); + ); + bus_generic_resume(dev); + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume done\n"); + ); + return (0); +} + +static int +hdacc_probe(device_t dev) +{ + uint32_t id, revid; + char buf[128]; + + id = ((uint32_t)hda_get_vendor_id(dev) << 16) + hda_get_device_id(dev); + revid = ((uint32_t)hda_get_revision_id(dev) << 8) + hda_get_stepping_id(dev); + snprintf(buf, sizeof(buf), "%s HDA CODEC", hdacc_codec_name(id, revid)); + device_set_desc_copy(dev, buf); + return (BUS_PROBE_DEFAULT); +} + +static int +hdacc_attach(device_t dev) +{ + struct hdacc_softc *codec = device_get_softc(dev); + device_t child; + int cad = (intptr_t)device_get_ivars(dev); + uint32_t subnode; + int startnode; + int endnode; + int i, n; + + codec->lock = HDAC_GET_MTX(device_get_parent(dev), dev); + codec->dev = dev; + codec->cad = cad; + + hdacc_lock(codec); + subnode = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, 0x0, HDA_PARAM_SUB_NODE_COUNT)); + hdacc_unlock(codec); + if (subnode == HDA_INVALID) + return (EIO); + codec->fgcnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); + startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); + endnode = startnode + codec->fgcnt; + + HDA_BOOTVERBOSE( + device_printf(dev, + "Root Node at nid=0: %d subnodes %d-%d\n", + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode), + startnode, endnode - 1); + ); + + codec->fgs = malloc(sizeof(struct hdacc_fg) * codec->fgcnt, + M_HDACC, M_ZERO | M_WAITOK); + for (i = startnode, n = 0; i < endnode; i++, n++) { + codec->fgs[n].nid = i; + hdacc_lock(codec); + codec->fgs[n].type = + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hda_command(dev, + HDA_CMD_GET_PARAMETER(0, i, HDA_PARAM_FCT_GRP_TYPE))); + codec->fgs[n].subsystem_id = hda_command(dev, + HDA_CMD_GET_SUBSYSTEM_ID(0, i)); + hdacc_unlock(codec); + codec->fgs[n].dev = child = device_add_child(dev, NULL, -1); + if (child == NULL) { + device_printf(dev, "Failed to add function device\n"); + continue; + } + device_set_ivars(child, &codec->fgs[n]); + } + + bus_generic_attach(dev); + + return (0); +} + +static int +hdacc_detach(device_t dev) +{ + + return (device_delete_children(dev)); +} + +static int +hdacc_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdacc_fg *fg = device_get_ivars(child); + + snprintf(buf, buflen, "nid=%d", fg->nid); + return (0); +} + +static int +hdacc_child_pnpinfo_str_method(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdacc_fg *fg = device_get_ivars(child); + + snprintf(buf, buflen, "type=0x%02x subsystem=0x%08x", + fg->type, fg->subsystem_id); + return (0); +} + +static int +hdacc_print_child(device_t dev, device_t child) +{ + struct hdacc_fg *fg = device_get_ivars(child); + int retval; + + retval = bus_print_child_header(dev, child); + retval += printf(" at nid %d", fg->nid); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static void +hdacc_probe_nomatch(device_t dev, device_t child) +{ + struct hdacc_softc *codec = device_get_softc(dev); + struct hdacc_fg *fg = device_get_ivars(child); + + device_printf(child, "<%s %s Function Group> at nid %d on %s " + "(no driver attached)\n", + device_get_desc(dev), + fg->type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO ? "Audio" : + (fg->type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM ? "Modem" : + "Unknown"), fg->nid, device_get_nameunit(dev)); + HDA_BOOTHVERBOSE( + device_printf(dev, "Power down FG nid=%d to the D3 state...\n", + fg->nid); + ); + hdacc_lock(codec); + hda_command(dev, HDA_CMD_SET_POWER_STATE(0, + fg->nid, HDA_CMD_POWER_STATE_D3)); + hdacc_unlock(codec); +} + +static int +hdacc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct hdacc_fg *fg = device_get_ivars(child); + + switch (which) { + case HDA_IVAR_NODE_ID: + *result = fg->nid; + break; + case HDA_IVAR_NODE_TYPE: + *result = fg->type; + break; + case HDA_IVAR_SUBSYSTEM_ID: + *result = fg->subsystem_id; + break; + default: + return(BUS_READ_IVAR(device_get_parent(dev), dev, + which, result)); + } + return (0); +} + +static struct mtx * +hdacc_get_mtx(device_t dev, device_t child) +{ + struct hdacc_softc *codec = device_get_softc(dev); + + return (codec->lock); +} + +static uint32_t +hdacc_codec_command(device_t dev, device_t child, uint32_t verb) +{ + + return (HDAC_CODEC_COMMAND(device_get_parent(dev), dev, verb)); +} + +static int +hdacc_stream_alloc(device_t dev, device_t child, int dir, int format, + uint32_t **dmapos) +{ + struct hdacc_softc *codec = device_get_softc(dev); + int stream; + + stream = HDAC_STREAM_ALLOC(device_get_parent(dev), dev, + dir, format, dmapos); + if (stream > 0) + codec->streams[dir][stream] = child; + return (stream); +} + +static void +hdacc_stream_free(device_t dev, device_t child, int dir, int stream) +{ + struct hdacc_softc *codec = device_get_softc(dev); + + codec->streams[dir][stream] = NULL; + HDAC_STREAM_FREE(device_get_parent(dev), dev, dir, stream); +} + +static int +hdacc_stream_start(device_t dev, device_t child, + int dir, int stream, bus_addr_t buf, int blksz, int blkcnt) +{ + + return (HDAC_STREAM_START(device_get_parent(dev), dev, + dir, stream, buf, blksz, blkcnt)); +} + +static void +hdacc_stream_stop(device_t dev, device_t child, int dir, int stream) +{ + + HDAC_STREAM_STOP(device_get_parent(dev), dev, dir, stream); +} + +static void +hdacc_stream_reset(device_t dev, device_t child, int dir, int stream) +{ + + HDAC_STREAM_RESET(device_get_parent(dev), dev, dir, stream); +} + +static uint32_t +hdacc_stream_getptr(device_t dev, device_t child, int dir, int stream) +{ + + return (HDAC_STREAM_GETPTR(device_get_parent(dev), dev, dir, stream)); +} + +static void +hdacc_stream_intr(device_t dev, int dir, int stream) +{ + struct hdacc_softc *codec = device_get_softc(dev); + device_t child; + + if ((child = codec->streams[dir][stream]) != NULL); + HDAC_STREAM_INTR(child, dir, stream); +} + +static int +hdacc_unsol_alloc(device_t dev, device_t child, int wanted) +{ + struct hdacc_softc *codec = device_get_softc(dev); + int tag; + + wanted &= 0x3f; + tag = wanted; + do { + if (codec->tags[tag] == NULL) { + codec->tags[tag] = child; + HDAC_UNSOL_ALLOC(device_get_parent(dev), dev, tag); + return (tag); + } + tag++; + tag &= 0x3f; + } while (tag != wanted); + return (-1); +} + +static void +hdacc_unsol_free(device_t dev, device_t child, int tag) +{ + struct hdacc_softc *codec = device_get_softc(dev); + + KASSERT(tag >= 0 && tag <= 0x3f, ("Wrong tag value %d\n", tag)); + codec->tags[tag] = NULL; + HDAC_UNSOL_FREE(device_get_parent(dev), dev, tag); +} + +static void +hdacc_unsol_intr(device_t dev, uint32_t resp) +{ + struct hdacc_softc *codec = device_get_softc(dev); + device_t child; + int tag; + + tag = resp >> 26; + if ((child = codec->tags[tag]) != NULL) + HDAC_UNSOL_INTR(child, resp); + else + device_printf(codec->dev, "Unexpected unsolicited " + "response with tag %d: %08x\n", tag, resp); +} + +static void +hdacc_pindump(device_t dev) +{ + device_t *devlist; + int devcount, i; + + if (device_get_children(dev, &devlist, &devcount) != 0) + return; + for (i = 0; i < devcount; i++) + HDAC_PINDUMP(devlist[i]); + free(devlist, M_TEMP); +} + +static device_method_t hdacc_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdacc_probe), + DEVMETHOD(device_attach, hdacc_attach), + DEVMETHOD(device_detach, hdacc_detach), + DEVMETHOD(device_suspend, hdacc_suspend), + DEVMETHOD(device_resume, hdacc_resume), + /* Bus interface */ + DEVMETHOD(bus_child_location_str, hdacc_child_location_str), + DEVMETHOD(bus_child_pnpinfo_str, hdacc_child_pnpinfo_str_method), + DEVMETHOD(bus_print_child, hdacc_print_child), + DEVMETHOD(bus_probe_nomatch, hdacc_probe_nomatch), + DEVMETHOD(bus_read_ivar, hdacc_read_ivar), + DEVMETHOD(hdac_get_mtx, hdacc_get_mtx), + DEVMETHOD(hdac_codec_command, hdacc_codec_command), + DEVMETHOD(hdac_stream_alloc, hdacc_stream_alloc), + DEVMETHOD(hdac_stream_free, hdacc_stream_free), + DEVMETHOD(hdac_stream_start, hdacc_stream_start), + DEVMETHOD(hdac_stream_stop, hdacc_stream_stop), + DEVMETHOD(hdac_stream_reset, hdacc_stream_reset), + DEVMETHOD(hdac_stream_getptr, hdacc_stream_getptr), + DEVMETHOD(hdac_stream_intr, hdacc_stream_intr), + DEVMETHOD(hdac_unsol_alloc, hdacc_unsol_alloc), + DEVMETHOD(hdac_unsol_free, hdacc_unsol_free), + DEVMETHOD(hdac_unsol_intr, hdacc_unsol_intr), + DEVMETHOD(hdac_pindump, hdacc_pindump), + { 0, 0 } +}; + +static driver_t hdacc_driver = { + "hdacc", + hdacc_methods, + sizeof(struct hdacc_softc), +}; + +static devclass_t hdacc_devclass; + +DRIVER_MODULE(snd_hda, hdac, hdacc_driver, hdacc_devclass, 0, 0); |