summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2007-07-01 17:31:20 +0000
committerariff <ariff@FreeBSD.org>2007-07-01 17:31:20 +0000
commita3668c9a7bfb3eab67987fe292de5043b7cd880c (patch)
tree980abb0a428b8cd812e7052e778d1f31d2b9f043 /sys
parentf8b50fddf23757c71ef760ba26e81ef5b523e0c1 (diff)
downloadFreeBSD-src-a3668c9a7bfb3eab67987fe292de5043b7cd880c.zip
FreeBSD-src-a3668c9a7bfb3eab67987fe292de5043b7cd880c.tar.gz
- Fix input/microphone support for ASUS A8N-VMCSM series.
Submitted by: Simon Schubert <corecode@fs.ei.tum.de> - Defer flushing unsolicited response into taskqueue thread rather than handle it directly in interrupt handler, since few of its operations (like measuring/calibrating jack impedance) are quite expensive. - Misc. debugging cleanups. Tested by: joel Approved by: re (hrs) MFC after: 3 days
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/sound/pci/hda/hdac.c146
-rw-r--r--sys/dev/sound/pci/hda/hdac_private.h2
2 files changed, 109 insertions, 39 deletions
diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c
index 3c7d9d5..f4b1761 100644
--- a/sys/dev/sound/pci/hda/hdac.c
+++ b/sys/dev/sound/pci/hda/hdac.c
@@ -67,12 +67,13 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
-#include <sys/ctype.h>
-
#include <dev/sound/pcm/sound.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
+#include <sys/ctype.h>
+#include <sys/taskqueue.h>
+
#include <dev/sound/pci/hda/hdac_private.h>
#include <dev/sound/pci/hda/hdac_reg.h>
#include <dev/sound/pci/hda/hda_reg.h>
@@ -80,7 +81,7 @@
#include "mixer_if.h"
-#define HDA_DRV_TEST_REV "20070619_0045"
+#define HDA_DRV_TEST_REV "20070702_0046"
#define HDA_WIDGET_PARSER_REV 1
SND_DECLARE_FILE("$FreeBSD$");
@@ -114,6 +115,14 @@ SND_DECLARE_FILE("$FreeBSD$");
/* 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))
@@ -205,6 +214,7 @@ SND_DECLARE_FILE("$FreeBSD$");
#define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234)
#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb)
#define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec)
+#define ASUS_A8NVMCSM_SUBVENDOR HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84)
#define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff)
/* IBM / Lenovo */
@@ -317,6 +327,10 @@ SND_DECLARE_FILE("$FreeBSD$");
#define SOUND_MASK_SKIP (1 << 30)
#define SOUND_MASK_DISABLE (1 << 31)
+#if __FreeBSD_version < 600000
+#define taskqueue_drain(...)
+#endif
+
static const struct {
char *key;
uint32_t value;
@@ -830,7 +844,7 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo)
struct hdac_widget *w;
struct hdac_audio_ctl *ctl;
uint32_t val, id, res;
- int i = 0, j, forcemute;
+ int i = 0, j, timeout, forcemute;
nid_t cad;
if (devinfo == NULL || devinfo->codec == NULL ||
@@ -862,13 +876,23 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo)
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);
+
+ timeout = 10000;
+ do {
+ res = hdac_command(sc,
+ HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid),
+ cad);
+ if (hdac_hp_switch[i].execsense == -1 || res != 0x7fffffff)
+ break;
+ DELAY(10);
+ } while (--timeout != 0);
+
HDA_BOOTVERBOSE(
device_printf(sc->dev,
- "HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n",
- hdac_hp_switch[i].hpnid, res);
+ "HDA_DEBUG: Pin sense: nid=%d timeout=%d res=0x%08x\n",
+ hdac_hp_switch[i].hpnid, timeout, res);
);
+
res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res);
res ^= hdac_hp_switch[i].inverted;
@@ -1100,7 +1124,7 @@ hdac_intr_handler(void *context)
uint32_t intsts;
uint8_t rirbsts;
struct hdac_rirb *rirb_base;
- uint32_t trigger = 0;
+ uint32_t trigger;
sc = (struct hdac_softc *)context;
@@ -1109,6 +1133,7 @@ hdac_intr_handler(void *context)
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)) {
@@ -1116,6 +1141,8 @@ hdac_intr_handler(void *context)
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;
@@ -1124,7 +1151,8 @@ hdac_intr_handler(void *context)
while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) {
HDAC_WRITE_1(&sc->mem,
HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL);
- hdac_rirb_flush(sc);
+ if (hdac_rirb_flush(sc) != 0)
+ trigger |= HDAC_TRIGGER_UNSOL;
rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS);
}
/* XXX to be removed */
@@ -1134,15 +1162,13 @@ hdac_intr_handler(void *context)
#endif
}
- hdac_unsolq_flush(sc);
-
if (intsts & HDAC_INTSTS_SIS_MASK) {
if ((intsts & (1 << sc->num_iss)) &&
hdac_stream_intr(sc, &sc->play) != 0)
- trigger |= 1;
+ trigger |= HDAC_TRIGGER_PLAY;
if ((intsts & (1 << 0)) &&
hdac_stream_intr(sc, &sc->rec) != 0)
- trigger |= 2;
+ trigger |= HDAC_TRIGGER_REC;
/* XXX to be removed */
#ifdef HDAC_INTR_EXTRA
HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts &
@@ -1152,10 +1178,12 @@ hdac_intr_handler(void *context)
hdac_unlock(sc);
- if (trigger & 1)
+ if (trigger & HDAC_TRIGGER_PLAY)
chn_intr(sc->play.c);
- if (trigger & 2)
+ if (trigger & HDAC_TRIGGER_REC)
chn_intr(sc->rec.c);
+ if (trigger & HDAC_TRIGGER_UNSOL)
+ taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task);
}
/****************************************************************************
@@ -2054,8 +2082,9 @@ hdac_widget_pin_getconfig(struct hdac_widget *w)
default:
break;
}
- } else if (id == HDA_CODEC_AD1986A && sc->pci_subvendor ==
- ASUS_M2NPVMX_SUBVENDOR) {
+ } else if (id == HDA_CODEC_AD1986A &&
+ (sc->pci_subvendor == ASUS_M2NPVMX_SUBVENDOR ||
+ sc->pci_subvendor == ASUS_A8NVMCSM_SUBVENDOR)) {
switch (nid) {
case 28: /* LINE */
config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
@@ -2372,7 +2401,7 @@ static void
hda_poll_callback(void *arg)
{
struct hdac_softc *sc = arg;
- uint32_t trigger = 0;
+ uint32_t trigger;
if (sc == NULL)
return;
@@ -2383,8 +2412,9 @@ hda_poll_callback(void *arg)
return;
}
- trigger |= (hda_poll_channel(&sc->play) != 0) ? 1 : 0;
- trigger |= (hda_poll_channel(&sc->rec) != 0) ? 2 : 0;
+ trigger = 0;
+ trigger |= (hda_poll_channel(&sc->play) != 0) ? HDAC_TRIGGER_PLAY : 0;
+ trigger |= (hda_poll_channel(&sc->rec)) != 0 ? HDAC_TRIGGER_REC : 0;
/* XXX */
callout_reset(&sc->poll_hda, 1/*sc->poll_ticks*/,
@@ -2392,9 +2422,9 @@ hda_poll_callback(void *arg)
hdac_unlock(sc);
- if (trigger & 1)
+ if (trigger & HDAC_TRIGGER_PLAY)
chn_intr(sc->play.c);
- if (trigger & 2)
+ if (trigger & HDAC_TRIGGER_REC)
chn_intr(sc->rec.c);
}
@@ -2407,7 +2437,7 @@ hdac_rirb_flush(struct hdac_softc *sc)
nid_t cad;
uint32_t resp;
uint8_t rirbwp;
- int ret = 0;
+ int ret;
rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr;
rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP);
@@ -2416,6 +2446,8 @@ hdac_rirb_flush(struct hdac_softc *sc)
BUS_DMASYNC_POSTREAD);
#endif
+ ret = 0;
+
while (sc->rirb_rp != rirbwp) {
sc->rirb_rp++;
sc->rirb_rp %= sc->rirb_size;
@@ -2475,8 +2507,8 @@ hdac_poll_callback(void *arg)
hdac_unlock(sc);
return;
}
- hdac_rirb_flush(sc);
- hdac_unsolq_flush(sc);
+ 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);
}
@@ -3563,6 +3595,18 @@ static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = {
};
MIXER_DECLARE(hdac_audio_ctl_ossmixer);
+static void
+hdac_unsolq_task(void *context, int pending)
+{
+ struct hdac_softc *sc;
+
+ sc = (struct hdac_softc *)context;
+
+ hdac_lock(sc);
+ hdac_unsolq_flush(sc);
+ hdac_unlock(sc);
+}
+
/****************************************************************************
* int hdac_attach(device_t)
*
@@ -3595,6 +3639,8 @@ hdac_attach(device_t dev)
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 = 1;
sc->poll_ival = HDAC_POLL_INTERVAL;
if (resource_int_value(device_get_name(dev),
@@ -4221,7 +4267,8 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo)
if (w->nid != 3)
w->enable = 0;
}
- if (subvendor == ASUS_M2NPVMX_SUBVENDOR) {
+ if (subvendor == ASUS_M2NPVMX_SUBVENDOR ||
+ subvendor == ASUS_A8NVMCSM_SUBVENDOR) {
/* nid 28 is mic, nid 29 is line-in */
w = hdac_widget_get(devinfo, 15);
if (w != NULL)
@@ -5611,6 +5658,7 @@ hdac_release_resources(struct hdac_softc *sc)
callout_stop(&sc->poll_jack);
hdac_reset(sc);
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);
@@ -5766,6 +5814,9 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS)
ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL);
ctl &= ~HDAC_RIRBCTL_RINTCTL;
HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl);
+ hdac_unlock(sc);
+ taskqueue_drain(taskqueue_thread, &sc->unsolq_task);
+ hdac_lock(sc);
callout_reset(&sc->poll_hdac, 1, hdac_poll_callback,
sc);
sc->polling = 1;
@@ -5818,13 +5869,13 @@ sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS)
#ifdef SND_DEBUG
static int
-sysctl_hdac_dump(SYSCTL_HANDLER_ARGS)
+sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS)
{
struct hdac_softc *sc;
struct hdac_devinfo *devinfo;
struct hdac_widget *w;
device_t dev;
- uint32_t res, execres;
+ uint32_t res, pincap, timeout;
int i, err, val;
nid_t cad;
@@ -5846,11 +5897,28 @@ sysctl_hdac_dump(SYSCTL_HANDLER_ARGS)
if (w == NULL || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
continue;
- execres = hdac_command(sc, HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0),
- cad);
- res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad);
- device_printf(dev, "nid=%-3d exec=0x%08x sense=0x%08x [%s]\n",
- w->nid, execres, res,
+ pincap = w->wclass.pin.cap;
+ if ((HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) ||
+ HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) &&
+ HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) {
+ timeout = 10000;
+ 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)
+ break;
+ DELAY(10);
+ } while (--timeout != 0);
+ } else {
+ timeout = -1;
+ res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad,
+ w->nid), cad);
+ }
+ device_printf(dev,
+ "PIN_SENSE: nid=%-3d timeout=%d res=0x%08x [%s]\n",
+ w->nid, timeout, res,
(w->enable == 0) ? "DISABLED" : "ENABLED");
}
device_printf(dev,
@@ -5860,7 +5928,7 @@ sysctl_hdac_dump(SYSCTL_HANDLER_ARGS)
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 (1 || HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) {
+ 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);
@@ -5877,13 +5945,13 @@ sysctl_hdac_dump(SYSCTL_HANDLER_ARGS)
HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad);
printf(" sticky=0x%08x\n", res);
}
- if (1 || HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) {
+ 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 (1 || HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) {
+ if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) {
device_printf(dev, "GPI0:");
res = hdac_command(sc,
HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad);
@@ -6123,8 +6191,8 @@ hdac_attach2(void *arg)
#ifdef SND_DEBUG
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
- "dump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
- sysctl_hdac_dump, "I", "Dump states");
+ "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
+ sysctl_hdac_pindump, "I", "Dump pin states/data");
#endif
#endif
diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h
index 48b0b55..d003d21 100644
--- a/sys/dev/sound/pci/hda/hdac_private.h
+++ b/sys/dev/sound/pci/hda/hdac_private.h
@@ -327,6 +327,8 @@ struct hdac_softc {
struct callout poll_hdac;
struct callout poll_jack;
+ struct task unsolq_task;
+
#define HDAC_UNSOLQ_MAX 64
#define HDAC_UNSOLQ_READY 0
#define HDAC_UNSOLQ_BUSY 1
OpenPOWER on IntegriCloud