- add support for headphone-boost mixer controls

- fix a bug of creation of output mute
- unmute all controls by azalia_set_mixer()
- if a bidirectional pin is green, orange, gray, or black, set
it as "output" direction by default.
This commit is contained in:
kent 2005-06-19 12:34:36 +00:00
parent 9b5bad91b8
commit 8ed87adda9
1 changed files with 116 additions and 55 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: azalia.c,v 1.2 2005/06/19 07:42:39 kent Exp $ */
/* $NetBSD: azalia.c,v 1.3 2005/06/19 12:34:36 kent Exp $ */
/*-
* Copyright (c) 2005 The NetBSD Foundation, Inc.
@ -40,12 +40,14 @@
* TO DO:
* o recording support
* o mixer value scaling
* o Volume Knob widget
* o DAC selection
* o ADC selection
* o power hook
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: azalia.c,v 1.2 2005/06/19 07:42:39 kent Exp $");
__KERNEL_RCSID(0, "$NetBSD: azalia.c,v 1.3 2005/06/19 12:34:36 kent Exp $");
#include <sys/param.h>
#include <sys/device.h>
@ -246,7 +248,7 @@ __KERNEL_RCSID(0, "$NetBSD: azalia.c,v 1.2 2005/06/19 07:42:39 kent Exp $");
#define COP_AWCAP_STRIPE 0x020
#define COP_AWCAP_PROC 0x040
#define COP_AWCAP_UNSOL 0x080
#define COP_AWCAP_CONLIST 0x100
#define COP_AWCAP_CONNLIST 0x100
#define COP_AWCAP_DIGITAL 0x200
#define COP_AWCAP_POWER 0x400
#define COP_AWCAP_LRSWAP 0x800
@ -560,7 +562,8 @@ typedef struct {
#define MI_TARGET_INAMP(x) (x)
#define MI_TARGET_OUTAMP 0x100
#define MI_TARGET_CONNLIST 0x101
#define MI_TARGET_DIR 0x102 /* for bidirectional pin */
#define MI_TARGET_PINDIR 0x102 /* for bidirectional pin */
#define MI_TARGET_PINBOOST 0x103 /* for headphone pin */
} mixer_item_t;
typedef struct codec_t {
@ -708,14 +711,14 @@ static const struct audio_hw_if azalia_hw_if = {
static const char *pin_colors[16] = {
"unknown", "black", "gray", "blue",
"green", "red", "orange", "yellow",
"purple", "pink", "0xa", "0xb",
"0xc", "0xd", "white", "other"};
"purple", "pink", "col0a", "col0b",
"col0c", "col0d", "white", "other"};
#ifdef AZALIA_DEBUG
static const char *pin_devices[16] = {
"line-out", AudioNspeaker, AudioNheadphone, AudioNcd,
"SPDIF-out", "digital-out", "modem-line", "modem-handset",
"line-in", AudioNaux, AudioNmicrophone, "telephony",
"SPDIF-in", "digital-in", "0xe", "other"};
"SPDIF-in", "digital-in", "dev0e", "other"};
#endif
/* ================================================================
@ -1355,10 +1358,6 @@ azalia_codec_init(codec_t *this)
}
npin++;
}
for (i = 0; i < npin; i++) {
this->comresp(this, pindexes[i], CORB_SET_PIN_WIDGET_CONTROL,
CORB_PWC_OUTPUT | CORB_PWC_VREF_100, &result);
}
this->ndacindexes = npin;
DPRINTF(("%s: DACs:", __func__));
for (i = 0; i < npin; i++) {
@ -1413,25 +1412,6 @@ azalia_codec_init(codec_t *this)
if (err)
return err;
/* XXX unmute all of amplifiers */
DPRINTF(("%s: unmute all widgets\n", __func__));
FOR_EACH_WIDGET(this, i) {
if (this->w[i].widgetcap & COP_AWCAP_INAMP) {
for (n = 0; n < this->w[i].nconnections; n++) {
this->comresp(this, this->w[i].nid,
CORB_SET_AMPLIFIER_GAIN_MUTE, CORB_AGM_INPUT |
CORB_AGM_LEFT | CORB_AGM_RIGHT | (n << 8) |
COP_AMPCAP_OFFSET(this->w[i].inamp_cap), &result);
}
}
if (this->w[i].widgetcap & COP_AWCAP_OUTAMP) {
this->comresp(this, this->w[i].nid,
CORB_SET_AMPLIFIER_GAIN_MUTE, CORB_AGM_OUTPUT |
CORB_AGM_LEFT | CORB_AGM_RIGHT |
COP_AMPCAP_OFFSET(this->w[i].outamp_cap), &result);
}
}
DPRINTF(("%s: done.\n", __func__));
azalia_mixer_init(this);
return 0;
}
@ -1602,25 +1582,11 @@ static int
azalia_mixer_init(codec_t *this)
{
/*
* o pin "color%2.2x"
* conlist -> outputs.<name>.source
* inamp -> outputs.<name>.<cname>.mute, outputs.<name>.<cname>
* outamp -> inputs.<name>.mute, inputs.<name>
* I/O -> outputs.<name>.dir=input/output
* XXX usually, green=front, orange=surround, gray=c/lfe,
* black=side?, blue=line-in pink=mic-in
* o pin "<color>%2.2x"
* o audio output "dac%2.2x"
* outamp -> inputs.<name>.mute, inputs.<name>
* o audio input "adc%2.2x"
* inamp -> record.<name>.<cname>.mute, record.<name>.<cname>
* conlist -> record.<name>.source
* o mixer "mixer%2.2x"
* outamp -> outputs.<name>.mute, outputs.<name>
* inamp -> inputs.<name>.<cname>.mute, ...
* o selector "sel%2.2x"
* outamp -> outputs.<name>.mute, outputs.<name>
* inamp -> inputs.<name>.<cname>.mute, inputs.<name>.<cname>
* conlist -> inputs.<name>.source
* o mixer "mixer%2.2x"
* o selector "sel%2.2x"
*
* XXX DAC/ADC selection, SPDIF
*/
@ -1724,7 +1690,7 @@ azalia_mixer_init(codec_t *this)
d->mixer_class = AZ_CLASS_INPUT;
d->next = AUDIO_MIXER_LAST;
d->prev = AUDIO_MIXER_LAST;
m->target = MI_TARGET_CONNLIST;
m->target = MI_TARGET_OUTAMP;
d->un.e.num_mem = 2;
d->un.e.member[0].ord = 0;
strlcpy(d->un.e.member[0].label.name, AudioNoff, MAX_AUDIO_DEV_LEN);
@ -1821,7 +1787,7 @@ azalia_mixer_init(codec_t *this)
d->mixer_class = AZ_CLASS_OUTPUT;
d->next = AUDIO_MIXER_LAST;
d->prev = AUDIO_MIXER_LAST;
m->target = MI_TARGET_DIR;
m->target = MI_TARGET_PINDIR;
d->un.e.num_mem = 2;
d->un.e.member[0].ord = 0;
strlcpy(d->un.e.member[0].label.name, AudioNinput,
@ -1831,6 +1797,67 @@ azalia_mixer_init(codec_t *this)
MAX_AUDIO_DEV_LEN);
this->nmixers++;
}
/* pin headphone-boost */
if (w->type == COP_AWTYPE_PIN_COMPLEX &&
w->d.pin.cap & COP_PINCAP_HEADPHONE) {
MIXER_REG_PROLOG;
snprintf(d->label.name, sizeof(d->label.name),
"%s.boost", w->name);
d->type = AUDIO_MIXER_ENUM;
d->mixer_class = AZ_CLASS_OUTPUT;
d->next = AUDIO_MIXER_LAST;
d->prev = AUDIO_MIXER_LAST;
m->target = MI_TARGET_PINBOOST;
d->un.e.num_mem = 2;
d->un.e.member[0].ord = 0;
strlcpy(d->un.e.member[0].label.name, AudioNoff,
MAX_AUDIO_DEV_LEN);
d->un.e.member[1].ord = 1;
strlcpy(d->un.e.member[1].label.name, AudioNon,
MAX_AUDIO_DEV_LEN);
this->nmixers++;
}
}
/* unmute all */
for (i = 0; i < this->nmixers; i++) {
mixer_ctrl_t mc;
if (!IS_MI_TARGET_INAMP(this->mixers[i].target) &&
this->mixers[i].target != MI_TARGET_OUTAMP)
continue;
if (this->mixers[i].devinfo.type != AUDIO_MIXER_ENUM)
continue;
mc.dev = i;
mc.type = AUDIO_MIXER_ENUM;
mc.un.ord = 0;
azalia_mixer_set(this, &mc);
}
/*
* for bidirectional pins,
* green=front, orange=surround, gray=c/lfe, black=size --> output
* blue=line-in, pink=mic-in --> input
*/
for (i = 0; i < this->nmixers; i++) {
mixer_ctrl_t mc;
if (this->mixers[i].target != MI_TARGET_PINDIR)
continue;
mc.dev = i;
mc.type = AUDIO_MIXER_ENUM;
switch (this->w[this->mixers[i].nid].d.pin.color) {
case CORB_CD_GREEN:
case CORB_CD_ORANGE:
case CORB_CD_GRAY:
case CORB_CD_BLACK:
mc.un.ord = 1;
break;
default:
mc.un.ord = 0;
}
azalia_mixer_set(this, &mc);
}
return 0;
@ -1916,15 +1943,25 @@ azalia_mixer_get(const codec_t *this, mixer_ctrl_t *mc)
}
/* pin I/O */
else if (m->target == MI_TARGET_DIR) {
else if (m->target == MI_TARGET_PINDIR) {
err = this->comresp(this, m->nid,
CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
if (err)
return err;
mc->un.ord = result & CORB_PWC_OUTPUT ? 1 : 0;
}
/* pin headphone-boost */
else if (m->target == MI_TARGET_PINBOOST) {
err = this->comresp(this, m->nid,
CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
if (err)
return err;
mc->un.ord = result & CORB_PWC_HEADPHONE ? 1 : 0;
} else {
aprint_error("%s: internal error in %s: %x\n", XNAME(this->az), __func__, m->target);
aprint_error("%s: internal error in %s: %x\n", XNAME(this->az),
__func__, m->target);
return -1;
}
return 0;
@ -1947,6 +1984,7 @@ azalia_mixer_set(const codec_t *this, const mixer_ctrl_t *mc)
/* inamp mute */
if (IS_MI_TARGET_INAMP(m->target) && m->devinfo.type == AUDIO_MIXER_ENUM) {
/* We have to set stereo mute separately to keep each gain value. */
err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
CORB_GAGM_INPUT | CORB_GAGM_LEFT | MI_TARGET_INAMP(m->target), &result);
if (err)
@ -2082,7 +2120,7 @@ azalia_mixer_set(const codec_t *this, const mixer_ctrl_t *mc)
}
/* pin I/O */
else if (m->target == MI_TARGET_DIR) {
else if (m->target == MI_TARGET_PINDIR) {
if (mc->un.ord >= 2)
return EINVAL;
err = this->comresp(this, m->nid,
@ -2098,9 +2136,31 @@ azalia_mixer_set(const codec_t *this, const mixer_ctrl_t *mc)
}
err = this->comresp(this, m->nid,
CORB_SET_PIN_WIDGET_CONTROL, result, &result);
if (err)
return err;
}
/* pin headphone-boost */
else if (m->target == MI_TARGET_PINBOOST) {
if (mc->un.ord >= 2)
return EINVAL;
err = this->comresp(this, m->nid,
CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
if (err)
return err;
if (mc->un.ord == 0) {
result &= ~CORB_PWC_HEADPHONE;
} else {
result |= CORB_PWC_HEADPHONE;
}
err = this->comresp(this, m->nid,
CORB_SET_PIN_WIDGET_CONTROL, result, &result);
if (err)
return err;
} else {
aprint_error("%s: internal error in %s: %x\n", XNAME(this->az), __func__, m->target);
aprint_error("%s: internal error in %s: %x\n", XNAME(this->az),
__func__, m->target);
return -1;
}
return 0;
@ -2148,7 +2208,7 @@ azalia_widget_init(widget_t *this, const codec_t *codec, nid_t nid)
this->widgetcap = result;
this->type = COP_AWCAP_TYPE(result);
bitmask_snprintf(this->widgetcap, "\20\014LRSWAP\013POWER\012DIGITAL"
"\011CONLIST\010UNSOL\07PROC\06STRIPE\05FORMATOV\04AMPOV\03OUTAMP"
"\011CONNLIST\010UNSOL\07PROC\06STRIPE\05FORMATOV\04AMPOV\03OUTAMP"
"\02INAMP\01STEREO", flagbuf, FLAGBUFLEN);
DPRINTF(("%s: ", XNAME(codec->az)));
switch (this->type) {
@ -2275,6 +2335,7 @@ azalia_widget_init_pin(widget_t *this, const codec_t *codec)
this->d.pin.sequence = CORB_CD_SEQUENCE(result);
this->d.pin.association = CORB_CD_ASSOCIATION(result);
this->d.pin.color = CORB_CD_COLOR(result);
this->d.pin.device = CORB_CD_DEVICE(result);
err = codec->comresp(codec, this->nid, CORB_GET_PARAMETER,
COP_PINCAP, &result);
@ -2309,7 +2370,7 @@ azalia_widget_init_connection(widget_t *this, const codec_t *codec)
int length, i;
this->selected = -1;
if ((this->widgetcap & COP_AWCAP_CONLIST) == 0)
if ((this->widgetcap & COP_AWCAP_CONNLIST) == 0)
return 0;
err = codec->comresp(codec, this->nid, CORB_GET_PARAMETER,