summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/azalia.h4
-rw-r--r--sys/dev/pci/azalia_codec.c319
2 files changed, 320 insertions, 3 deletions
diff --git a/sys/dev/pci/azalia.h b/sys/dev/pci/azalia.h
index 24b900f1d18..18d29c28595 100644
--- a/sys/dev/pci/azalia.h
+++ b/sys/dev/pci/azalia.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: azalia.h,v 1.32 2009/01/02 00:25:33 jakemsr Exp $ */
+/* $OpenBSD: azalia.h,v 1.33 2009/01/02 00:39:25 jakemsr Exp $ */
/* $NetBSD: azalia.h,v 1.6 2006/01/16 14:15:26 kent Exp $ */
/*-
@@ -579,6 +579,8 @@ typedef struct {
#define MI_TARGET_MUTESET 0x10a
#define MI_TARGET_PINSENSE 0x10b
#define MI_TARGET_SENSESET 0x10c
+#define MI_TARGET_PLAYVOL 0x10d
+#define MI_TARGET_RECVOL 0x10e
} mixer_item_t;
#define VALID_WIDGET_NID(nid, codec) (nid == (codec)->audiofunc || \
diff --git a/sys/dev/pci/azalia_codec.c b/sys/dev/pci/azalia_codec.c
index 89999ae9e56..3cf988e814e 100644
--- a/sys/dev/pci/azalia_codec.c
+++ b/sys/dev/pci/azalia_codec.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: azalia_codec.c,v 1.101 2009/01/02 00:20:14 jakemsr Exp $ */
+/* $OpenBSD: azalia_codec.c,v 1.102 2009/01/02 00:39:25 jakemsr Exp $ */
/* $NetBSD: azalia_codec.c,v 1.8 2006/05/10 11:17:27 kent Exp $ */
/*-
@@ -815,6 +815,112 @@ azalia_generic_mixer_init(codec_t *this)
this->nmixers++;
}
+ /* playback volume group */
+ if (this->playvols.nslaves > 0) {
+ mixer_devinfo_t *d;
+ err = azalia_generic_mixer_ensure_capacity(this,
+ this->nmixers + 3);
+
+ /* volume */
+ m = &this->mixers[this->nmixers];
+ m->nid = this->playvols.master;
+ m->target = MI_TARGET_PLAYVOL;
+ d = &m->devinfo;
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s", AudioNmaster);
+ d->type = AUDIO_MIXER_VALUE;
+ d->un.v.num_channels = 2;
+ d->un.v.delta = 8;
+ this->nmixers++;
+ d->next = this->nmixers;
+
+ /* mute */
+ m = &this->mixers[this->nmixers];
+ m->nid = this->playvols.master;
+ m->target = MI_TARGET_PLAYVOL;
+ d = &m->devinfo;
+ d->prev = this->nmixers - 1;
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s", AudioNmute);
+ azalia_devinfo_offon(d);
+ this->nmixers++;
+ d->next = this->nmixers;
+
+ /* slaves */
+ m = &this->mixers[this->nmixers];
+ m->nid = this->playvols.master;
+ m->target = MI_TARGET_PLAYVOL;
+ d = &m->devinfo;
+ d->prev = this->nmixers - 1;
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s", "slaves");
+ d->type = AUDIO_MIXER_SET;
+ for (i = 0, j = 0; i < this->playvols.nslaves; i++) {
+ ww = &this->w[this->playvols.slaves[i]];
+ d->un.s.member[j].mask = (1 << i);
+ strlcpy(d->un.s.member[j++].label.name, ww->name,
+ MAX_AUDIO_DEV_LEN);
+ }
+ d->un.s.num_mem = j;
+ this->nmixers++;
+ }
+
+ /* recording volume group */
+ if (this->recvols.nslaves > 0) {
+ mixer_devinfo_t *d;
+ err = azalia_generic_mixer_ensure_capacity(this,
+ this->nmixers + 3);
+
+ /* volume */
+ m = &this->mixers[this->nmixers];
+ m->nid = this->recvols.master;
+ m->target = MI_TARGET_RECVOL;
+ d = &m->devinfo;
+ d->mixer_class = AZ_CLASS_RECORD;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s", AudioNvolume);
+ d->type = AUDIO_MIXER_VALUE;
+ d->un.v.num_channels = 2;
+ d->un.v.delta = 8;
+ this->nmixers++;
+ d->next = this->nmixers;
+
+ /* mute */
+ m = &this->mixers[this->nmixers];
+ m->nid = this->recvols.master;
+ m->target = MI_TARGET_RECVOL;
+ d = &m->devinfo;
+ d->prev = this->nmixers - 1;
+ d->mixer_class = AZ_CLASS_RECORD;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s", AudioNmute);
+ azalia_devinfo_offon(d);
+ this->nmixers++;
+ d->next = this->nmixers;
+
+ /* slaves */
+ m = &this->mixers[this->nmixers];
+ m->nid = this->recvols.master;
+ m->target = MI_TARGET_RECVOL;
+ d = &m->devinfo;
+ d->prev = this->nmixers - 1;
+ d->mixer_class = AZ_CLASS_RECORD;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s", "slaves");
+ d->type = AUDIO_MIXER_SET;
+ for (i = 0, j = 0; i < this->recvols.nslaves; i++) {
+ ww = &this->w[this->recvols.slaves[i]];
+ d->un.s.member[j].mask = (1 << i);
+ strlcpy(d->un.s.member[j++].label.name, ww->name,
+ MAX_AUDIO_DEV_LEN);
+ }
+ d->un.s.num_mem = j;
+ this->nmixers++;
+ }
+
/* if the codec has multiple DAC groups, create "inputs.usingdac" */
if (this->dacs.ngroups > 1) {
MIXER_REG_PROLOG;
@@ -924,7 +1030,7 @@ azalia_generic_mixer_default(codec_t *this)
widget_t *w;
mixer_item_t *m;
mixer_ctrl_t mc;
- int i, j;
+ int i, j, tgt, cap;
/* unmute all */
for (i = 0; i < this->nmixers; i++) {
@@ -996,6 +1102,44 @@ azalia_generic_mixer_default(codec_t *this)
if (this->spkr_muters != 0 && this->unsol_event != NULL)
this->unsol_event(this, AZ_TAG_SPKR);
+ /* get default value for play group master */
+ for (i = 0; i < this->playvols.nslaves; i++) {
+ if (!(this->playvols.cur & (1 << i)))
+ continue;
+ w = &this->w[this->playvols.slaves[i]];
+ if (!(COP_AMPCAP_NUMSTEPS(w->outamp_cap)))
+ continue;
+ mc.type = AUDIO_MIXER_VALUE;
+ tgt = MI_TARGET_OUTAMP;
+ azalia_generic_mixer_get(this, w->nid, tgt, &mc);
+ this->playvols.vol_l = mc.un.value.level[0];
+ this->playvols.vol_r = mc.un.value.level[0];
+ break;
+ }
+ this->playvols.mute = 0;
+
+ /* get default value for record group master */
+ for (i = 0; i < this->recvols.nslaves; i++) {
+ if (!(this->recvols.cur & (1 << i)))
+ continue;
+ w = &this->w[this->recvols.slaves[i]];
+ mc.type = AUDIO_MIXER_VALUE;
+ tgt = MI_TARGET_OUTAMP;
+ cap = w->outamp_cap;
+ if (w->type == COP_AWTYPE_PIN_COMPLEX ||
+ w->type == COP_AWTYPE_AUDIO_INPUT) {
+ tgt = 0;
+ cap = w->inamp_cap;
+ }
+ if (!(COP_AMPCAP_NUMSTEPS(cap)))
+ continue;
+ azalia_generic_mixer_get(this, w->nid, tgt, &mc);
+ this->recvols.vol_l = mc.un.value.level[0];
+ this->recvols.vol_r = mc.un.value.level[0];
+ break;
+ }
+ this->recvols.mute = 0;
+
return 0;
}
@@ -1220,6 +1364,44 @@ azalia_generic_mixer_get(const codec_t *this, nid_t nid, int target,
}
}
+ else if (target == MI_TARGET_PLAYVOL) {
+
+ if (mc->type == AUDIO_MIXER_VALUE) {
+ mc->un.value.num_channels = 2;
+ mc->un.value.level[0] = this->playvols.vol_l;
+ mc->un.value.level[1] = this->playvols.vol_r;
+
+ } else if (mc->type == AUDIO_MIXER_ENUM) {
+ mc->un.ord = this->playvols.mute;
+
+ } else if (mc->type == AUDIO_MIXER_SET) {
+ mc->un.mask = this->playvols.cur;
+
+ } else {
+ DPRINTF(("%s: invalid outmaster mixer type\n"));
+ return EINVAL;
+ }
+ }
+
+ else if (target == MI_TARGET_RECVOL) {
+
+ if (mc->type == AUDIO_MIXER_VALUE) {
+ mc->un.value.num_channels = 2;
+ mc->un.value.level[0] = this->recvols.vol_l;
+ mc->un.value.level[1] = this->recvols.vol_r;
+
+ } else if (mc->type == AUDIO_MIXER_ENUM) {
+ mc->un.ord = this->recvols.mute;
+
+ } else if (mc->type == AUDIO_MIXER_SET) {
+ mc->un.mask = this->recvols.cur;
+
+ } else {
+ DPRINTF(("%s: invalid inmaster mixer type\n"));
+ return EINVAL;
+ }
+ }
+
else {
printf("%s: internal error in %s: target=%x\n",
XNAME(this), __func__, target);
@@ -1587,6 +1769,139 @@ azalia_generic_mixer_set(codec_t *this, nid_t nid, int target,
}
}
+ else if (target == MI_TARGET_PLAYVOL) {
+
+ const widget_t *w;
+ mixer_ctrl_t mc2;
+
+ if (mc->type == AUDIO_MIXER_VALUE) {
+ if (mc->un.value.num_channels != 2)
+ return EINVAL;
+ this->playvols.vol_l = mc->un.value.level[0];
+ this->playvols.vol_r = mc->un.value.level[1];
+ for (i = 0; i < this->playvols.nslaves; i++) {
+ if (!(this->playvols.cur & (1 << i)))
+ continue;
+ w = &this->w[this->playvols.slaves[i]];
+ if (!(COP_AMPCAP_NUMSTEPS(w->outamp_cap)))
+ continue;
+ mc2.type = AUDIO_MIXER_VALUE;
+ mc2.un.value.num_channels = WIDGET_CHANNELS(w);
+ mc2.un.value.level[0] = this->playvols.vol_l;
+ mc2.un.value.level[1] = this->playvols.vol_r;
+ err = azalia_generic_mixer_set(this, w->nid,
+ MI_TARGET_OUTAMP, &mc2);
+ if (err) {
+ DPRINTF(("%s: out slave %2.2x vol\n",
+ __func__, w->nid));
+ return err;
+ }
+ }
+ } else if (mc->type == AUDIO_MIXER_ENUM) {
+ if (mc->un.ord != 0 && mc->un.ord != 1)
+ return EINVAL;
+ this->playvols.mute = mc->un.ord;
+ for (i = 0; i < this->playvols.nslaves; i++) {
+ if (!(this->playvols.cur & (1 << i)))
+ continue;
+ w = &this->w[this->playvols.slaves[i]];
+ if (!(w->outamp_cap & COP_AMPCAP_MUTE))
+ continue;
+ mc2.type = AUDIO_MIXER_ENUM;
+ mc2.un.ord = this->playvols.mute;
+ err = azalia_generic_mixer_set(this, w->nid,
+ MI_TARGET_OUTAMP, &mc2);
+ if (err) {
+ DPRINTF(("%s: out slave %2.2x mute\n",
+ __func__, w->nid));
+ return err;
+ }
+ }
+
+ } else if (mc->type == AUDIO_MIXER_SET) {
+ this->playvols.cur =
+ (mc->un.mask & this->playvols.mask);
+
+ } else {
+ DPRINTF(("%s: invalid output master mixer type\n"));
+ return EINVAL;
+ }
+ }
+
+ else if (target == MI_TARGET_RECVOL) {
+
+ const widget_t *w;
+ mixer_ctrl_t mc2;
+ uint32_t cap;
+ int tgt;
+
+ if (mc->type == AUDIO_MIXER_VALUE) {
+ if (mc->un.value.num_channels != 2)
+ return EINVAL;
+ this->recvols.vol_l = mc->un.value.level[0];
+ this->recvols.vol_r = mc->un.value.level[1];
+ for (i = 0; i < this->recvols.nslaves; i++) {
+ if (!(this->recvols.cur & (1 << i)))
+ continue;
+ w = &this->w[this->recvols.slaves[i]];
+ tgt = MI_TARGET_OUTAMP;
+ cap = w->outamp_cap;
+ if (w->type == COP_AWTYPE_AUDIO_INPUT ||
+ w->type == COP_AWTYPE_PIN_COMPLEX) {
+ tgt = 0;
+ cap = w->inamp_cap;
+ }
+ if (!(COP_AMPCAP_NUMSTEPS(cap)))
+ continue;
+ mc2.type = AUDIO_MIXER_VALUE;
+ mc2.un.value.num_channels = WIDGET_CHANNELS(w);
+ mc2.un.value.level[0] = this->recvols.vol_l;
+ mc2.un.value.level[1] = this->recvols.vol_r;
+ err = azalia_generic_mixer_set(this, w->nid,
+ tgt, &mc2);
+ if (err) {
+ DPRINTF(("%s: in slave %2.2x vol\n",
+ __func__, w->nid));
+ return err;
+ }
+ }
+ } else if (mc->type == AUDIO_MIXER_ENUM) {
+ if (mc->un.ord != 0 && mc->un.ord != 1)
+ return EINVAL;
+ this->recvols.mute = mc->un.ord;
+ for (i = 0; i < this->recvols.nslaves; i++) {
+ if (!(this->recvols.cur & (1 << i)))
+ continue;
+ w = &this->w[this->recvols.slaves[i]];
+ tgt = MI_TARGET_OUTAMP;
+ cap = w->outamp_cap;
+ if (w->type == COP_AWTYPE_AUDIO_INPUT ||
+ w->type == COP_AWTYPE_PIN_COMPLEX) {
+ tgt = 0;
+ cap = w->inamp_cap;
+ }
+ if (!(cap & COP_AMPCAP_MUTE))
+ continue;
+ mc2.type = AUDIO_MIXER_ENUM;
+ mc2.un.ord = this->recvols.mute;
+ err = azalia_generic_mixer_set(this, w->nid,
+ tgt, &mc2);
+ if (err) {
+ DPRINTF(("%s: out slave %2.2x mute\n",
+ __func__, w->nid));
+ return err;
+ }
+ }
+
+ } else if (mc->type == AUDIO_MIXER_SET) {
+ this->recvols.cur = (mc->un.mask & this->recvols.mask);
+
+ } else {
+ DPRINTF(("%s: invalid input master mixer type\n"));
+ return EINVAL;
+ }
+ }
+
else {
printf("%s: internal error in %s: target=%x\n",
XNAME(this), __func__, target);