summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Meuser <jakemsr@cvs.openbsd.org>2009-06-09 05:16:43 +0000
committerJacob Meuser <jakemsr@cvs.openbsd.org>2009-06-09 05:16:43 +0000
commit74aa6f77bde55a52a48ea4fb3de9c232b8049cfd (patch)
treeba471a9ceadff2c48c01a1c336f50bbc25a5ff67
parent2139236498fe4a12f40d119c93b23c62ed7e6c56 (diff)
AD1981HD output amps are weird. they report 63 volume steps but the
first 30 do nothing. perhaps there are other codecs with such amps? (ab)use some reserved bits in the amplifier capabilities parameter to store the first volume step that actually changes the volume. problem reported and patch tested by LEVAI Daniel.
-rw-r--r--sys/dev/pci/azalia.c8
-rw-r--r--sys/dev/pci/azalia.h4
-rw-r--r--sys/dev/pci/azalia_codec.c107
3 files changed, 79 insertions, 40 deletions
diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c
index 5eb11808bcf..dcde9e2ef96 100644
--- a/sys/dev/pci/azalia.c
+++ b/sys/dev/pci/azalia.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: azalia.c,v 1.138 2009/06/09 05:05:48 jakemsr Exp $ */
+/* $OpenBSD: azalia.c,v 1.139 2009/06/09 05:16:42 jakemsr Exp $ */
/* $NetBSD: azalia.c,v 1.20 2006/05/07 08:31:44 kent Exp $ */
/*-
@@ -2544,12 +2544,17 @@ azalia_widget_init(widget_t *this, const codec_t *codec, nid_t nid)
}
/* amplifier information */
+ /* XXX (ab)use bits 24-30 to store the "control offset", which is
+ * the number of steps, starting at 0, that have no effect. these
+ * bits are reserved in HDA 1.0.
+ */
if (this->widgetcap & COP_AWCAP_INAMP) {
if (this->widgetcap & COP_AWCAP_AMPOV)
azalia_comresp(codec, nid, CORB_GET_PARAMETER,
COP_INPUT_AMPCAP, &this->inamp_cap);
else
this->inamp_cap = codec->w[codec->audiofunc].inamp_cap;
+ this->inamp_cap &= ~(0x7f << 24);
}
if (this->widgetcap & COP_AWCAP_OUTAMP) {
if (this->widgetcap & COP_AWCAP_AMPOV)
@@ -2557,6 +2562,7 @@ azalia_widget_init(widget_t *this, const codec_t *codec, nid_t nid)
COP_OUTPUT_AMPCAP, &this->outamp_cap);
else
this->outamp_cap = codec->w[codec->audiofunc].outamp_cap;
+ this->outamp_cap &= ~(0x7f << 24);
}
return 0;
}
diff --git a/sys/dev/pci/azalia.h b/sys/dev/pci/azalia.h
index 087292df225..f423108b9e2 100644
--- a/sys/dev/pci/azalia.h
+++ b/sys/dev/pci/azalia.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: azalia.h,v 1.51 2009/06/09 05:05:48 jakemsr Exp $ */
+/* $OpenBSD: azalia.h,v 1.52 2009/06/09 05:16:42 jakemsr Exp $ */
/* $NetBSD: azalia.h,v 1.6 2006/01/16 14:15:26 kent Exp $ */
/*-
@@ -263,6 +263,7 @@
#define COP_AMPCAP_OFFSET(x) (x & 0x0000007f)
#define COP_AMPCAP_NUMSTEPS(x) ((x >> 8) & 0x7f)
#define COP_AMPCAP_STEPSIZE(x) ((x >> 16) & 0x7f)
+#define COP_AMPCAP_CTLOFF(x) ((x >> 24) & 0x7f)
#define COP_AMPCAP_MUTE 0x80000000
#define COP_CONNECTION_LIST_LENGTH 0x0e
#define COP_CLL_LONG 0x00000080
@@ -506,6 +507,7 @@
#define AZ_QRK_WID_CDIN_1C 0x00001000
#define AZ_QRK_WID_BEEP_1D 0x00002000
#define AZ_QRK_WID_OVREF50 0x00004000
+#define AZ_QRK_WID_AD1981_OAMP 0x00008000
/* memory-mapped types */
typedef struct {
diff --git a/sys/dev/pci/azalia_codec.c b/sys/dev/pci/azalia_codec.c
index c42378c9d66..e8af91f440b 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.131 2009/06/09 05:05:48 jakemsr Exp $ */
+/* $OpenBSD: azalia_codec.c,v 1.132 2009/06/09 05:16:42 jakemsr Exp $ */
/* $NetBSD: azalia_codec.c,v 1.8 2006/05/10 11:17:27 kent Exp $ */
/*-
@@ -46,13 +46,12 @@ int azalia_add_convgroup(codec_t *, convgroupset_t *,
int azalia_mixer_fix_indexes(codec_t *);
int azalia_mixer_default(codec_t *);
int azalia_mixer_ensure_capacity(codec_t *, size_t);
-u_char azalia_mixer_from_device_value
- (const codec_t *, nid_t, int, uint32_t );
-uint32_t azalia_mixer_to_device_value
- (const codec_t *, nid_t, int, u_char);
+u_char azalia_mixer_from_device_value(const codec_t *, nid_t, int, uint32_t );
+uint32_t azalia_mixer_to_device_value(const codec_t *, nid_t, int, u_char);
void azalia_devinfo_offon(mixer_devinfo_t *);
void azalia_pin_config_ov(widget_t *, int, int);
+void azalia_ampcap_ov(widget_t *, int, int, int, int, int, int);
int azalia_gpio_unmute(codec_t *, int);
@@ -198,6 +197,7 @@ azalia_codec_init_vtbl(codec_t *this)
break;
case 0x11d41981:
this->name = "Analog Devices AD1981HD";
+ this->qrks |= AZ_QRK_WID_AD1981_OAMP;
break;
case 0x11d41983:
this->name = "Analog Devices AD1983";
@@ -1670,15 +1670,14 @@ azalia_mixer_get(const codec_t *this, nid_t nid, int target,
}
int
-azalia_mixer_set(codec_t *this, nid_t nid, int target,
- const mixer_ctrl_t *mc)
+azalia_mixer_set(codec_t *this, nid_t nid, int target, const mixer_ctrl_t *mc)
{
uint32_t result, value;
int i, err;
/* inamp mute */
if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
- /* We have to set stereo mute separately to keep each gain value. */
+ /* set stereo mute separately to keep each gain value */
err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
CORB_GAGM_INPUT | CORB_GAGM_LEFT |
MI_TARGET_INAMP(target), &result);
@@ -2189,42 +2188,51 @@ u_char
azalia_mixer_from_device_value(const codec_t *this, nid_t nid, int target,
uint32_t dv)
{
- uint32_t dmax;
-
- if (IS_MI_TARGET_INAMP(target))
- dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
- else if (target == MI_TARGET_OUTAMP)
- dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
- else {
- printf("unknown target: %d\n", target);
- dmax = 255;
- }
- if (dv <= 0 || dmax == 0)
- return AUDIO_MIN_GAIN;
- if (dv >= dmax)
- return AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax;
- return dv * (AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax) / dmax;
+ uint32_t steps;
+ int max_gain, ctloff;
+
+ if (IS_MI_TARGET_INAMP(target)) {
+ steps = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
+ ctloff = COP_AMPCAP_CTLOFF(this->w[nid].inamp_cap);
+ } else if (target == MI_TARGET_OUTAMP) {
+ steps = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
+ ctloff = COP_AMPCAP_CTLOFF(this->w[nid].outamp_cap);
+ } else {
+ printf("%s: unknown target: %d\n", __func__, target);
+ steps = 255;
+ }
+ dv -= ctloff;
+ if (dv <= 0 || steps == 0)
+ return(AUDIO_MIN_GAIN);
+ max_gain = AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % steps;
+ if (dv >= steps)
+ return(max_gain);
+ return(dv * max_gain / steps);
}
uint32_t
azalia_mixer_to_device_value(const codec_t *this, nid_t nid, int target,
u_char uv)
{
- uint32_t dmax;
-
- if (IS_MI_TARGET_INAMP(target))
- dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
- else if (target == MI_TARGET_OUTAMP)
- dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
- else {
- printf("unknown target: %d\n", target);
- dmax = 255;
- }
- if (uv <= AUDIO_MIN_GAIN || dmax == 0)
- return 0;
- if (uv >= AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax)
- return dmax;
- return uv * dmax / (AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % dmax);
+ uint32_t steps;
+ int max_gain, ctloff;
+
+ if (IS_MI_TARGET_INAMP(target)) {
+ steps = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
+ ctloff = COP_AMPCAP_CTLOFF(this->w[nid].inamp_cap);
+ } else if (target == MI_TARGET_OUTAMP) {
+ steps = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
+ ctloff = COP_AMPCAP_CTLOFF(this->w[nid].outamp_cap);
+ } else {
+ printf("%s: unknown target: %d\n", __func__, target);
+ steps = 255;
+ }
+ if (uv <= AUDIO_MIN_GAIN || steps == 0)
+ return(ctloff);
+ max_gain = AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % steps;
+ if (uv >= max_gain)
+ return(steps + ctloff);
+ return(uv * steps / max_gain + ctloff);
}
int
@@ -2249,6 +2257,23 @@ azalia_gpio_unmute(codec_t *this, int pin)
}
void
+azalia_ampcap_ov(widget_t *w, int type, int offset, int steps, int size,
+ int ctloff, int mute)
+{
+ uint32_t cap;
+
+ cap = (offset & 0x7f) | ((steps & 0x7f) << 8) |
+ ((size & 0x7f) << 16) | ((ctloff & 0x7f) << 24) |
+ (mute ? COP_AMPCAP_MUTE : 0);
+
+ if (type == COP_OUTPUT_AMPCAP) {
+ w->outamp_cap = cap;
+ } else if (type == COP_INPUT_AMPCAP) {
+ w->inamp_cap = cap;
+ }
+}
+
+void
azalia_pin_config_ov(widget_t *w, int mask, int val)
{
int bits, offset;
@@ -2314,5 +2339,11 @@ azalia_codec_widget_quirks(codec_t *this, nid_t nid)
w->enable = 1;
}
+ if ((this->qrks & AZ_QRK_WID_AD1981_OAMP) &&
+ ((nid == 0x05) || (nid == 0x06) || (nid == 0x07) ||
+ (nid == 0x09) || (nid == 0x18))) {
+ azalia_ampcap_ov(w, COP_OUTPUT_AMPCAP, 31, 33, 6, 30, 1);
+ }
+
return(0);
}