summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/azalia.c108
1 files changed, 107 insertions, 1 deletions
diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c
index 682ca245ad0..2e39f8d06ef 100644
--- a/sys/dev/pci/azalia.c
+++ b/sys/dev/pci/azalia.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: azalia.c,v 1.64 2008/10/31 06:44:20 jakemsr Exp $ */
+/* $OpenBSD: azalia.c,v 1.65 2008/10/31 06:52:15 jakemsr Exp $ */
/* $NetBSD: azalia.c,v 1.20 2006/05/07 08:31:44 kent Exp $ */
/*-
@@ -43,6 +43,7 @@
#include <sys/param.h>
#include <sys/device.h>
+#include <sys/timeout.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <uvm/uvm_param.h>
@@ -176,6 +177,7 @@ typedef struct azalia_t {
codec_t codecs[15];
int ncodecs; /* number of codecs */
int codecno; /* index of the using codec */
+ struct timeout timeo;
azalia_dma_t corb_dma;
int corb_size;
@@ -227,6 +229,8 @@ int azalia_free_dmamem(const azalia_t *, azalia_dma_t*);
int azalia_codec_init(codec_t *);
int azalia_codec_delete(codec_t *);
+void azalia_codec_jack_intr(void *);
+void azalia_codec_jack_handler(codec_t *);
void azalia_codec_add_bits(codec_t *, int, uint32_t, int);
void azalia_codec_add_format(codec_t *, int, int, int, uint32_t,
int32_t);
@@ -737,12 +741,114 @@ azalia_attach_intr(struct device *self)
goto err_exit;
az->audiodev = audio_attach_mi(&azalia_hw_if, az, &az->dev);
+
+ /* Setup jack_intr if only there is no unsol_event handler. */
+ if (az->codecs[az->codecno].unsol_event == NULL) {
+ timeout_set(&az->timeo, azalia_codec_jack_intr, az);
+ timeout_add_sec(&az->timeo, 2);
+ }
+
return;
err_exit:
azalia_pci_detach(self, 0);
return;
}
+void
+azalia_codec_jack_intr(void *addr)
+{
+ struct azalia_t *az = (struct azalia_t *)addr;
+ int s;
+
+ s = splaudio();
+ azalia_codec_jack_handler(&az->codecs[az->codecno]);
+ timeout_add_sec(&az->timeo, 2);
+ splx(s);
+}
+
+void
+azalia_codec_jack_handler(codec_t *this)
+{
+ int i, j, err, dev;
+ uint32_t sense, ctl, val, dir;
+
+ FOR_EACH_WIDGET(this, i) {
+ const widget_t *w;
+
+ dev = 0;
+ w = &this->w[i];
+
+ /* Find pin with conn=jack and presence capability. */
+ if (w == NULL || w->type != COP_AWTYPE_PIN_COMPLEX ||
+ (CORB_CD_PORT(w->d.pin.config)) != CORB_CD_JACK ||
+ (!(w->d.pin.cap & COP_PINCAP_PRESENCE)))
+ continue;
+
+ /* Headphones. */
+ if (w->d.pin.device == CORB_CD_HEADPHONE &&
+ (w->d.pin.cap & COP_PINCAP_HEADPHONE)) {
+ dev = CORB_CD_SPEAKER; /* (un)mute speakers */
+ dir = CORB_PWC_OUTPUT;
+ }
+
+ if (!dev)
+ continue;
+
+ err = this->comresp(this, w->nid, CORB_GET_PIN_SENSE,
+ 0, &sense);
+ if (err)
+ break;
+
+ err = this->comresp(this, w->nid, CORB_GET_PIN_WIDGET_CONTROL,
+ 0, &ctl);
+ if (err)
+ break;
+
+ if (sense & CORB_PS_PRESENCE)
+ val = ctl | dir;
+ else
+ val = ctl & ~dir;
+
+ if (val != ctl) {
+ ctl = val;
+ err = this->comresp(this, w->nid,
+ CORB_SET_PIN_WIDGET_CONTROL, ctl, NULL);
+ if (err)
+ break;
+ }
+
+ FOR_EACH_WIDGET(this, j) {
+ const widget_t *w;
+
+ w = &this->w[j];
+
+ /* Find suitable and connected pin for (un)mute. */
+ if (w == NULL || w->type != COP_AWTYPE_PIN_COMPLEX ||
+ (CORB_CD_PORT(w->d.pin.config)) == CORB_CD_NONE ||
+ j == i || w->d.pin.device != dev)
+ continue;
+
+ err = this->comresp(this, w->nid,
+ CORB_GET_PIN_WIDGET_CONTROL, 0, &ctl);
+ if (err)
+ break;
+
+ if (sense & CORB_PS_PRESENCE)
+ val = ctl & ~dir;
+ else
+ val = ctl | dir;
+
+ if (val != ctl) {
+ ctl = val;
+ err = this->comresp(this, w->nid,
+ CORB_SET_PIN_WIDGET_CONTROL, ctl, NULL);
+ if (err)
+ break;
+ }
+ }
+ }
+}
+
int
azalia_init_corb(azalia_t *az)
{