- Merge input/microphone support for ASUS A8N-VMCSM series.
authorHasso Tepper <hasso@dragonflybsd.org>
Fri, 30 Nov 2007 07:41:28 +0000 (07:41 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Fri, 30 Nov 2007 07:41:28 +0000 (07:41 +0000)
- 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.

Obtained-from: FreeBSD

sys/dev/sound/pci/hda/hdac.c
sys/dev/sound/pci/hda/hdac_private.h

index ae23956..733ced3 100644 (file)
@@ -24,8 +24,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.36.2.3 2007/06/21 20:58:44 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.9 2007/07/22 18:53:19 dillon Exp $
+ * $FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.36.2.4 2007/07/04 04:05:22 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.10 2007/11/30 07:41:28 hasso Exp $
  */
 
 /*
  *    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  */
 
-#include <sys/ctype.h>
-
 #include <dev/sound/pcm/sound.h>
 #include <bus/pci/pcireg.h>
 #include <bus/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>
 
 #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("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.9 2007/07/22 18:53:19 dillon Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.10 2007/11/30 07:41:28 hasso Exp $");
 
 #define HDA_BOOTVERBOSE(stmt)  do {                    \
        if (bootverbose != 0) {                         \
@@ -129,6 +130,14 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.9 2007/07/22
 /* 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))
 
@@ -220,7 +229,7 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.9 2007/07/22
 #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_VMCSM_SUBVENDOR   HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84)
+#define ASUS_A8NVMCSM_SUBVENDOR        HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84)
 #define ASUS_ALL_SUBVENDOR     HDA_MODEL_CONSTRUCT(ASUS, 0xffff)
 
 /* IBM / Lenovo */
@@ -846,7 +855,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 ||
@@ -878,13 +887,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;
 
@@ -1116,7 +1135,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;
 
@@ -1125,6 +1144,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)) {
@@ -1132,6 +1152,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;
@@ -1140,7 +1162,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 */
@@ -1150,15 +1173,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 &
@@ -1168,10 +1189,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_swi, &sc->unsolq_task);
 }
 
 /****************************************************************************
@@ -2075,7 +2098,7 @@ hdac_widget_pin_getconfig(struct hdac_widget *w)
                }
        } else if (id == HDA_CODEC_AD1986A &&
            (sc->pci_subvendor == ASUS_M2NPVMX_SUBVENDOR ||
-            sc->pci_subvendor == ASUS_VMCSM_SUBVENDOR)) {
+           sc->pci_subvendor == ASUS_A8NVMCSM_SUBVENDOR)) {
                switch (nid) {
                case 28:        /* LINE */
                        config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
@@ -2392,7 +2415,7 @@ static void
 hda_poll_callback(void *arg)
 {
        struct hdac_softc *sc = arg;
-       uint32_t trigger = 0;
+       uint32_t trigger;
 
        if (sc == NULL)
                return;
@@ -2403,8 +2426,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*/,
@@ -2412,9 +2436,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);
 }
 
@@ -2427,7 +2451,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);
@@ -2435,6 +2459,7 @@ hdac_rirb_flush(struct hdac_softc *sc)
        bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map,
            BUS_DMASYNC_POSTREAD);
 #endif
+       ret = 0;
 
        while (sc->rirb_rp != rirbwp) {
                sc->rirb_rp++;
@@ -2495,8 +2520,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);
 }
@@ -3602,6 +3627,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)
  *
@@ -3634,6 +3671,8 @@ hdac_attach(device_t dev)
        callout_init(&sc->poll_hdac);
        callout_init(&sc->poll_jack);
 
+       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),
@@ -4259,7 +4298,7 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo)
                                w->enable = 0;
                }
                if (subvendor == ASUS_M2NPVMX_SUBVENDOR ||
-                   subvendor == ASUS_VMCSM_SUBVENDOR) {
+                   subvendor == ASUS_A8NVMCSM_SUBVENDOR) {
                        /* nid 28 is mic, nid 29 is line-in */
                        w = hdac_widget_get(devinfo, 15);
                        if (w != NULL)
@@ -5856,13 +5895,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, execres;
        int i, err, val;
        nid_t cad;
 
@@ -5884,11 +5923,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,
@@ -5898,7 +5954,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);
@@ -5915,13 +5971,13 @@ sysctl_hdac_dump(SYSCTL_HANDLER_ARGS)
                    HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad);
                kprintf(" 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);
                kprintf(" 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);
@@ -6161,8 +6217,8 @@ hdac_attach2(void *arg)
 #ifdef SND_DEBUG
        SYSCTL_ADD_PROC(snd_sysctl_tree(sc->dev),
            SYSCTL_CHILDREN(snd_sysctl_tree_top(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
 
index 9b642f7..eac9b69 100644 (file)
@@ -23,8 +23,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.6.2.1 2007/05/13 21:09:24 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.4 2007/06/16 20:07:21 dillon Exp $
+ * $FreeBSD: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.6.2.2 2007/07/04 04:05:22 ariff Exp $
+ * $DragonFly: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.5 2007/11/30 07:41:28 hasso Exp $
  */
 
 #ifndef _HDAC_PRIVATE_H_
@@ -328,6 +328,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