summaryrefslogtreecommitdiff
path: root/sys/dev/ic/ac97.c
diff options
context:
space:
mode:
authorJacob Meuser <jakemsr@cvs.openbsd.org>2008-10-23 21:50:02 +0000
committerJacob Meuser <jakemsr@cvs.openbsd.org>2008-10-23 21:50:02 +0000
commit6f459ae12d184272e7ef7b3f547537bbf501f31a (patch)
tree33f295d20f143e6eaf9ae5cb909524d945af7e16 /sys/dev/ic/ac97.c
parent403c943deb4dc1db75d2ecc26efa828ba93697cf (diff)
S/PDIF output support in ac97(4), auich(4) and auvia(4)
from NetBSD tested by a few with no regressions. optical works for jsg@. ok ratchov@
Diffstat (limited to 'sys/dev/ic/ac97.c')
-rw-r--r--sys/dev/ic/ac97.c76
1 files changed, 64 insertions, 12 deletions
diff --git a/sys/dev/ic/ac97.c b/sys/dev/ic/ac97.c
index 536c1bcf2f0..cef9a9b88f8 100644
--- a/sys/dev/ic/ac97.c
+++ b/sys/dev/ic/ac97.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ac97.c,v 1.69 2008/10/23 20:52:50 jakemsr Exp $ */
+/* $OpenBSD: ac97.c,v 1.70 2008/10/23 21:50:01 jakemsr Exp $ */
/*
* Copyright (c) 1999, 2000 Constantine Sapuntzakis
@@ -126,6 +126,8 @@ const struct audio_mixer_value ac97_volume_mono = {
1
};
+#define AudioNspdif "spdif"
+
#define WRAP(a) &a, sizeof(a)
const struct ac97_source_info {
@@ -152,7 +154,8 @@ const struct ac97_source_info {
CHECK_TONE,
CHECK_MIC,
CHECK_LOUDNESS,
- CHECK_3D
+ CHECK_3D,
+ CHECK_SPDIF
} req_feature;
int16_t prev;
@@ -293,10 +296,16 @@ const struct ac97_source_info {
AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
AC97_REG_3D_CONTROL, 0x0000, 4, 0, 0, 1, CHECK_3D
},
+ /* External Amp */
{ AudioCoutputs, AudioNextamp, NULL,
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
AC97_REG_POWER, 0x0000, 1, 15, 0, 0
},
+ /* S/PDIF output enable */
+ { AudioCoutputs, AudioNspdif, NULL,
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_REG_EXT_AUDIO_CTRL, 0x0000, 1, 2, 0, 0, CHECK_SPDIF
+ }
/* Missing features: Simulated Stereo, POP, Loopback mode */
};
@@ -321,10 +330,13 @@ struct ac97_softc {
u_int16_t caps; /* -> AC97_REG_RESET */
u_int16_t ext_id; /* -> AC97_REG_EXT_AUDIO_ID */
u_int16_t shadow_reg[128];
+ int lock_counter;
};
int ac97_mixer_get_port(struct ac97_codec_if *, mixer_ctrl_t *);
int ac97_mixer_set_port(struct ac97_codec_if *, mixer_ctrl_t *);
+void ac97_lock(struct ac97_codec_if *);
+void ac97_unlock(struct ac97_codec_if *);
int ac97_query_devinfo(struct ac97_codec_if *, mixer_devinfo_t *);
int ac97_get_portnum_by_name(struct ac97_codec_if *, char *, char *,
char *);
@@ -348,7 +360,9 @@ struct ac97_codec_if_vtbl ac97civ = {
ac97_restore_shadow,
ac97_get_extcaps,
ac97_set_rate,
- ac97_set_clock
+ ac97_set_clock,
+ ac97_lock,
+ ac97_unlock
};
const struct ac97_codecid {
@@ -670,6 +684,8 @@ ac97_check_capability(struct ac97_softc *as, int check)
return as->ext_id & AC97_EXT_AUDIO_CDAC;
case CHECK_LFE:
return as->ext_id & AC97_EXT_AUDIO_LDAC;
+ case CHECK_SPDIF:
+ return as->ext_id & AC97_EXT_AUDIO_SPDIF;
case CHECK_HEADPHONES:
return as->caps & AC97_CAPS_HEADPHONES;
case CHECK_TONE:
@@ -783,7 +799,7 @@ int
ac97_attach(struct ac97_host_if *host_if)
{
struct ac97_softc *as;
- u_int16_t id1, id2;
+ u_int16_t id1, id2, val;
u_int32_t id;
u_int16_t extstat, rate;
mixer_ctrl_t ctl;
@@ -868,17 +884,31 @@ ac97_attach(struct ac97_host_if *host_if)
| AC97_EXT_AUDIO_LDAC)) {
ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &extstat);
- if (as->ext_id & AC97_EXT_AUDIO_VRA)
- extstat |= AC97_EXT_AUDIO_VRA;
extstat &= ~AC97_EXT_AUDIO_DRA;
+
if (as->ext_id & AC97_EXT_AUDIO_VRM)
extstat |= AC97_EXT_AUDIO_VRM;
- if (as->ext_id & AC97_EXT_AUDIO_CDAC)
- extstat |= AC97_EXT_AUDIO_CDAC;
- if (as->ext_id & AC97_EXT_AUDIO_SDAC)
- extstat |= AC97_EXT_AUDIO_SDAC;
+
if (as->ext_id & AC97_EXT_AUDIO_LDAC)
extstat |= AC97_EXT_AUDIO_LDAC;
+ if (as->ext_id & AC97_EXT_AUDIO_SDAC)
+ extstat |= AC97_EXT_AUDIO_SDAC;
+ if (as->ext_id & AC97_EXT_AUDIO_CDAC)
+ extstat |= AC97_EXT_AUDIO_CDAC;
+ if (as->ext_id & AC97_EXT_AUDIO_SPDIF) {
+ /* XXX S/PDIF gets same data as DAC?
+ * maybe this should be settable?
+ * default is SPSAAB (10/11) on AD1980 and ALC codecs.
+ */
+ extstat &= ~AC97_EXT_AUDIO_SPSA_MASK;
+ extstat |= AC97_EXT_AUDIO_SPSA34;
+ ac97_read(as, AC97_REG_SPDIF_CTRL, &val);
+ val = (val & ~AC97_SPDIF_SPSR_MASK) |
+ AC97_SPDIF_SPSR_48K;
+ ac97_write(as, AC97_REG_SPDIF_CTRL, val);
+ }
+ if (as->ext_id & AC97_EXT_AUDIO_VRA)
+ extstat |= AC97_EXT_AUDIO_VRA;
ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, extstat);
if (as->ext_id & AC97_EXT_AUDIO_VRA) {
/* VRA should be enabled. */
@@ -929,6 +959,20 @@ ac97_attach(struct ac97_host_if *host_if)
return (0);
}
+void
+ac97_lock(struct ac97_codec_if *codec_if)
+{
+ struct ac97_softc *as = (struct ac97_softc *)codec_if;
+ as->lock_counter++;
+}
+
+void
+ac97_unlock(struct ac97_codec_if *codec_if)
+{
+ struct ac97_softc *as = (struct ac97_softc *)codec_if;
+ as->lock_counter--;
+}
+
int
ac97_query_devinfo(struct ac97_codec_if *codec_if, mixer_devinfo_t *dip)
{
@@ -974,12 +1018,17 @@ ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
struct ac97_source_info *si = &as->source_info[cp->dev];
u_int16_t mask;
u_int16_t val, newval;
- int error;
+ int error, spdif;
if (cp->dev < 0 || cp->dev >= as->num_source_info ||
- cp->type != si->type)
+ cp->type == AUDIO_MIXER_CLASS || cp->type != si->type)
return (EINVAL);
+ spdif = si->req_feature == CHECK_SPDIF &&
+ si->reg == AC97_REG_EXT_AUDIO_CTRL;
+ if (spdif && as->lock_counter >= 0)
+ return EBUSY;
+
ac97_read(as, si->reg, &val);
DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
@@ -1053,6 +1102,9 @@ ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
if (error)
return (error);
+ if (spdif && as->host_if->spdif_event != NULL)
+ as->host_if->spdif_event(as->host_if->arg, cp->un.ord);
+
return (0);
}