Project

General

Profile

Bug #2090 ยป hda_automute.patch

ssgriffon, 02/13/2012 06:10 AM

View differences:

sys/dev/sound/pci/hda/hdac.c
};
#define HDAC_CODECS_LEN NELEM(hdac_codecs)
enum {
HDAC_HP_SWITCH_CTL,
HDAC_HP_SWITCH_CTRL,
HDAC_HP_SWITCH_DEBUG
};
static const struct {
uint32_t model;
uint32_t id;
int type;
int inverted;
int polling;
int execsense;
nid_t hpnid;
nid_t spkrnid[8];
nid_t eapdnid;
} hdac_hp_switch[] = {
/* Specific OEM models */
{ HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL,
0, 0, -1, 17, { 16, -1 }, 16 },
/* { HP_XW4300_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_CTL,
0, 0, -1, 21, { 16, 17, -1 }, -1 } */
/* { HP_3010_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_DEBUG,
0, 1, 0, 16, { 15, 18, 19, 20, 21, -1 }, -1 }, */
{ HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
0, 0, -1, 6, { 5, -1 }, 5 },
{ HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
0, 0, -1, 6, { 5, -1 }, 5 },
{ HP_NX6325_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
0, 0, -1, 6, { 5, -1 }, 5 },
/* { HP_DC7700_SUBVENDOR, HDA_CODEC_ALC262, HDAC_HP_SWITCH_CTL,
0, 0, -1, 21, { 22, 27, -1 }, -1 }, */
{ TOSHIBA_U200_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
0, 0, -1, 6, { 5, -1 }, -1 },
{ TOSHIBA_A135_SUBVENDOR, HDA_CODEC_ALC861VD, HDAC_HP_SWITCH_CTL,
0, 0, -1, 27, { 20, -1 }, -1 },
{ DELL_D820_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL,
0, 0, -1, 13, { 14, -1 }, -1 },
{ DELL_I1300_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL,
0, 0, -1, 13, { 14, -1 }, -1 },
{ DELL_OPLX745_SUBVENDOR, HDA_CODEC_AD1983, HDAC_HP_SWITCH_CTL,
0, 0, -1, 6, { 5, 7, -1 }, -1 },
{ APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, HDAC_HP_SWITCH_CTL,
0, 0, -1, 21, { 20, 22, -1 }, -1 },
{ APPLE_INTEL_MAC, HDA_CODEC_STAC9221, HDAC_HP_SWITCH_CTRL,
0, 0, -1, 10, { 13, -1 }, -1 },
{ LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL,
1, 0, -1, 26, { 27, -1 }, -1 },
/* { LENOVO_TCA55_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL,
0, 0, -1, 26, { 27, 28, 29, 30, -1 }, -1 }, */
{ LG_LW20_SUBVENDOR, HDA_CODEC_ALC880, HDAC_HP_SWITCH_CTL,
0, 0, -1, 27, { 20, -1 }, -1 },
{ ACER_A5050_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 21, -1 }, -1 },
{ ACER_3681WXM_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 21, -1 }, -1 },
{ ACER_A4520_SUBVENDOR, HDA_CODEC_ALC268, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 21, -1 }, -1 },
{ UNIWILL_9080_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 21, -1 }, -1 },
{ MSI_MS1034_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 27, -1 }, -1 },
{ MSI_MS034A_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 27, -1 }, -1 },
{ FS_SI1848_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 21, -1 }, -1 },
{ FL_S7020D_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 16, -1 }, -1 },
/*
* All models that at least come from the same vendor with
* simmilar codec.
*/
{ HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL,
0, 0, -1, 17, { 16, -1 }, 16 },
{ HP_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
0, 0, -1, 6, { 5, -1 }, 5 },
{ TOSHIBA_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL,
0, 0, -1, 6, { 5, -1 }, -1 },
{ DELL_ALL_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL,
0, 0, -1, 13, { 14, -1 }, -1 },
#if 0
{ LENOVO_ALL_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL,
1, 0, -1, 26, { 27, -1 }, -1 },
{ ACER_ALL_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL,
0, 0, -1, 20, { 21, -1 }, -1 },
#endif
};
#define HDAC_HP_SWITCH_LEN NELEM(hdac_hp_switch)
static const struct {
uint32_t model;
uint32_t id;
......
struct hdac_widget *w;
struct hdac_audio_ctl *ctl;
uint32_t val, id, res;
int i = 0, j, timeout, forcemute;
nid_t cad;
int i = 0, timeout, forcemute;
nid_t cad, hp_nid;
if (devinfo == NULL || devinfo->codec == NULL ||
devinfo->codec->sc == NULL)
......
sc = devinfo->codec->sc;
cad = devinfo->codec->cad;
id = hdac_codec_id(devinfo);
for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) {
if (HDA_DEV_MATCH(hdac_hp_switch[i].model,
sc->pci_subvendor) &&
hdac_hp_switch[i].id == id)
break;
}
if (i >= HDAC_HP_SWITCH_LEN)
return;
forcemute = 0;
if (hdac_hp_switch[i].eapdnid != -1) {
w = hdac_widget_get(devinfo, hdac_hp_switch[i].eapdnid);
if (w != NULL && w->param.eapdbtl != HDAC_INVALID)
forcemute = (w->param.eapdbtl &
HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ? 0 : 1;
}
if (hdac_hp_switch[i].execsense != -1)
hdac_command(sc,
HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid,
hdac_hp_switch[i].execsense), cad);
/*For now, only the first headphone jack supports automute. So find
* that jack*/
hp_nid = devinfo->startnode;
for (; hp_nid < devinfo->endnode; hp_nid++) {
w = hdac_widget_get(devinfo, hp_nid);
if((w != NULL) &&
(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) &&
(HDA_PARAM_PIN_CAP_HEADPHONE_CAP(w->wclass.pin.config))) {
break;
}
}
timeout = 10000;
do {
res = hdac_command(sc,
HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid),
HDA_CMD_GET_PIN_SENSE(cad, hp_nid),
cad);
if (hdac_hp_switch[i].execsense == -1 || res != 0x7fffffff)
if (res != 0x7fffffff)
break;
DELAY(10);
} while (--timeout != 0);
......
HDA_BOOTVERBOSE(
device_printf(sc->dev,
"HDA_DEBUG: Pin sense: nid=%d timeout=%d res=0x%08x\n",
hdac_hp_switch[i].hpnid, timeout, res);
hp_nid, timeout, res);
);
res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res);
res ^= hdac_hp_switch[i].inverted;
switch (hdac_hp_switch[i].type) {
case HDAC_HP_SWITCH_CTL:
ctl = hdac_audio_ctl_amp_get(devinfo,
hdac_hp_switch[i].hpnid, 0, 1);
if (ctl != NULL) {
val = (res != 0 && forcemute == 0) ?
HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL;
if (val != ctl->muted) {
ctl->muted = val;
hdac_audio_ctl_amp_set(ctl,
HDA_AMP_MUTE_DEFAULT, ctl->left,
ctl->right);
}
}
for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
ctl = hdac_audio_ctl_amp_get(devinfo,
hdac_hp_switch[i].spkrnid[j], 0, 1);
if (ctl == NULL)
continue;
val = (res != 0 || forcemute == 1) ?
HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE;
if (val == ctl->muted)
continue;
ctl = hdac_audio_ctl_amp_get(devinfo,
hp_nid, 0, 1);
if (ctl != NULL) {
val = (res != 0 && forcemute == 0) ?
HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL;
if (val != ctl->muted) {
ctl->muted = val;
hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT,
ctl->left, ctl->right);
hdac_audio_ctl_amp_set(ctl,
HDA_AMP_MUTE_DEFAULT, ctl->left,
ctl->right);
}
break;
case HDAC_HP_SWITCH_CTRL:
if (res != 0) {
/* HP in */
w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid);
if (w != NULL && w->type ==
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
if (forcemute == 0)
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);
}
}
for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
w = hdac_widget_get(devinfo,
hdac_hp_switch[i].spkrnid[j]);
if (w == NULL || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
continue;
val = w->wclass.pin.ctrl &
~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
if (val == w->wclass.pin.ctrl)
continue;
w->wclass.pin.ctrl = val;
hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(
cad, w->nid, w->wclass.pin.ctrl), cad);
}
} else {
/* HP out */
w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid);
if (w != NULL && w->type ==
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
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);
}
}
for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
w = hdac_widget_get(devinfo,
hdac_hp_switch[i].spkrnid[j]);
if (w == NULL || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
continue;
if (forcemute == 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)
continue;
w->wclass.pin.ctrl = val;
hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(
cad, w->nid, w->wclass.pin.ctrl), cad);
}
}
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;
}
break;
case HDAC_HP_SWITCH_DEBUG:
if (hdac_hp_switch[i].execsense != -1)
hdac_command(sc,
HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid,
hdac_hp_switch[i].execsense), cad);
res = hdac_command(sc,
HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad);
device_printf(sc->dev,
"[ 0] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n",
hdac_hp_switch[i].hpnid, res);
for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) {
w = hdac_widget_get(devinfo,
hdac_hp_switch[i].spkrnid[j]);
if (w == NULL || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
continue;
if (hdac_hp_switch[i].execsense != -1)
hdac_command(sc,
HDA_CMD_SET_PIN_SENSE(cad, w->nid,
hdac_hp_switch[i].execsense), cad);
res = hdac_command(sc,
HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad);
device_printf(sc->dev,
"[%2d] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n",
j + 1, w->nid, res);
if(((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) !=
HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER)) {
continue;
}
break;
default:
break;
ctl = hdac_audio_ctl_amp_get(devinfo,
i, 0, 1);
if (ctl == NULL)
continue;
val = (res != 0 || forcemute == 1) ?
HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE;
if (val == ctl->muted)
continue;
ctl->muted = val;
hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT,
ctl->left, ctl->right);
}
}
......
};
CHANNEL_DECLARE(hdac_channel);
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);
}
static int
hdac_audio_ctl_ossmixer_init(struct snd_mixer *m)
{
......
id = hdac_codec_id(devinfo);
cad = devinfo->codec->cad;
for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) {
if (!(HDA_DEV_MATCH(hdac_hp_switch[i].model,
sc->pci_subvendor) && hdac_hp_switch[i].id == id))
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;
w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid);
if (w == NULL || w->enable == 0 || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
}
if(!HDA_PARAM_PIN_CAP_HEADPHONE_CAP(w->wclass.pin.config)) {
continue;
if (hdac_hp_switch[i].polling != 0)
callout_reset(&sc->poll_jack, 1,
hdac_jack_poll_callback, devinfo);
else if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap))
hdac_command(sc,
}
if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) {
device_printf(sc->dev, "Enabling unsolicited responses for nid %d.", w->nid);
hdac_command(sc,
HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid,
HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE |
HDAC_UNSOLTAG_EVENT_HP), cad);
else
} else {
continue;
}
hdac_hp_switch_handler(devinfo);
HDA_BOOTVERBOSE(
device_printf(sc->dev,
......
"pci_subvendor=0x%08x "
"codec=0x%08x [%s]\n",
i, w->nid, sc->pci_subvendor, id,
(hdac_hp_switch[i].polling != 0) ? "POLL" :
"UNSOL");
);
break;
}
for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) {
if (!(HDA_DEV_MATCH(hdac_eapd_switch[i].model,
......
callout_init(&sc->poll_hda);
callout_init(&sc->poll_hdac);
callout_init(&sc->poll_jack);
TASK_INIT(&sc->unsolq_task, 0, hdac_unsolq_task, sc);
......
sc->poll_ival = 0;
callout_stop(&sc->poll_hda);
callout_stop(&sc->poll_hdac);
callout_stop(&sc->poll_jack);
hdac_reset(sc);
hdac_unlock(sc);
......
device_printf(sc->dev,
"HDA_DEBUG: Enabling controller interrupt...\n");
);
if (sc->polling == 0)
HDAC_WRITE_4(&sc->mem, HDAC_INTCTL,
HDAC_INTCTL_CIE | HDAC_INTCTL_GIE);
HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) |
HDAC_GCTL_UNSOL);
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);
sys/dev/sound/pci/hda/hdac_private.h
int poll_ival;
struct callout poll_hda;
struct callout poll_hdac;
struct callout poll_jack;
struct task unsolq_task;
    (1-1/1)