summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ic/ac97.c681
-rw-r--r--sys/dev/ic/ac97.h52
-rw-r--r--sys/dev/pci/auich.c26
-rw-r--r--sys/dev/pci/auixp.c22
-rw-r--r--sys/dev/pci/auvia.c241
-rw-r--r--sys/dev/pci/auviavar.h3
6 files changed, 689 insertions, 336 deletions
diff --git a/sys/dev/ic/ac97.c b/sys/dev/ic/ac97.c
index 14fdeaf6c45..a7e6df05b23 100644
--- a/sys/dev/ic/ac97.c
+++ b/sys/dev/ic/ac97.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ac97.c,v 1.65 2008/01/13 21:43:01 deanna Exp $ */
+/* $OpenBSD: ac97.c,v 1.66 2008/01/15 02:52:50 jakemsr Exp $ */
/*
* Copyright (c) 1999, 2000 Constantine Sapuntzakis
@@ -127,150 +127,165 @@ const struct ac97_source_info {
int16_t info_size;
u_int8_t reg;
+ u_int16_t default_value;
u_int8_t bits:3;
u_int8_t ofs:4;
u_int8_t mute:1;
u_int8_t polarity:1; /* Does 0 == MAX or MIN */
- u_int16_t default_value;
+ enum {
+ CHECK_NONE = 0,
+ CHECK_SURROUND,
+ CHECK_CENTER,
+ CHECK_LFE,
+ CHECK_HEADPHONES,
+ CHECK_TONE,
+ CHECK_MIC,
+ CHECK_LOUDNESS,
+ CHECK_3D
+ } req_feature;
int16_t prev;
int16_t next;
int16_t mixer_class;
} source_info[] = {
- {
- AudioCinputs, NULL, NULL, AUDIO_MIXER_CLASS,
- }, {
- AudioCoutputs, NULL, NULL, AUDIO_MIXER_CLASS,
- }, {
- AudioCrecord, NULL, NULL, AUDIO_MIXER_CLASS,
- }, {
- /* Stereo master volume*/
- AudioCoutputs, AudioNmaster, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_MASTER_VOLUME, 5, 0, 1, 0, 0x8000
- }, {
- /* Mono volume */
- AudioCoutputs, AudioNmono, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_MASTER_VOLUME_MONO, 6, 0, 1, 0, 0x8000
- }, {
- AudioCoutputs, AudioNmono, AudioNsource, AUDIO_MIXER_ENUM,
- WRAP(ac97_mono_select),
- AC97_REG_GP, 1, 9, 0, 0, 0x0000
- }, {
- /* Headphone volume */
- AudioCoutputs, AudioNheadphone, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_HEADPHONE_VOLUME, 6, 0, 1, 0, 0x8000
- }, {
- AudioCoutputs, AudioNbass, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_MASTER_TONE, 4, 8, 0, 0, 0x0f0f
- }, {
- AudioCoutputs, AudioNtreble, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_MASTER_TONE, 4, 0, 0, 0, 0x0f0f
- }, {
- /* PC Beep Volume */
- AudioCinputs, AudioNspeaker, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_PCBEEP_VOLUME, 4, 1, 1, 0, 0x0000
- }, {
- /* Phone */
- AudioCinputs, "phone", NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_PHONE_VOLUME, 5, 0, 1, 0, 0x8008
- }, {
- /* Mic Volume */
- AudioCinputs, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_MIC_VOLUME, 5, 0, 1, 0, 0x8008
- }, {
- AudioCinputs, AudioNmicrophone, AudioNpreamp, AUDIO_MIXER_ENUM,
- WRAP(ac97_on_off),
- AC97_REG_MIC_VOLUME, 1, 6, 0, 0, 0x8008
- }, {
- AudioCinputs, AudioNmicrophone, AudioNsource, AUDIO_MIXER_ENUM,
- WRAP(ac97_mic_select),
- AC97_REG_GP, 1, 8, 0, 0x0000
- }, {
- /* Line in Volume */
- AudioCinputs, AudioNline, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_LINEIN_VOLUME, 5, 0, 1, 0, 0x8808
- }, {
- /* CD Volume */
- AudioCinputs, AudioNcd, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_CD_VOLUME, 5, 0, 1, 0, 0x8808
- }, {
- /* Video Volume */
- AudioCinputs, "video", NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_VIDEO_VOLUME, 5, 0, 1, 0, 0x8808
- }, {
- /* AUX volume */
- AudioCinputs, AudioNaux, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_AUX_VOLUME, 5, 0, 1, 0, 0x8808
- }, {
- /* PCM out volume */
- AudioCinputs, AudioNdac, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_PCMOUT_VOLUME, 5, 0, 1, 0, 0x8808
- }, {
- /* Record Source - some logic for this is hard coded - see below */
- AudioCrecord, AudioNsource, NULL, AUDIO_MIXER_ENUM,
- WRAP(ac97_source),
- AC97_REG_RECORD_SELECT, 3, 0, 0, 0, 0x0000
- }, {
- /* Record Gain */
- AudioCrecord, AudioNvolume, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_RECORD_GAIN, 4, 0, 1, 0, 0x8000
- }, {
- /* Record Gain mic */
- AudioCrecord, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_RECORD_GAIN_MIC, 4, 0, 1, 1, 0x8000
- }, {
- /* */
- AudioCoutputs, AudioNloudness, NULL, AUDIO_MIXER_ENUM,
- WRAP(ac97_on_off),
- AC97_REG_GP, 1, 12, 0, 0, 0x0000
- }, {
- AudioCoutputs, AudioNspatial, NULL, AUDIO_MIXER_ENUM,
- WRAP(ac97_on_off),
- AC97_REG_GP, 1, 13, 0, 0, 0x0000
- }, {
- AudioCoutputs, AudioNspatial, AudioNcenter,AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_3D_CONTROL, 4, 8, 0, 1, 0x0000
- }, {
- AudioCoutputs, AudioNspatial, AudioNdepth, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_3D_CONTROL, 4, 0, 0, 1, 0x0000
- }, {
- /* Surround volume */
- AudioCoutputs, AudioNsurround, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_stereo),
- AC97_REG_SURROUND_VOLUME, 6, 0, 1, 0, 0x8080
- }, {
- /* Center volume */
- AudioCoutputs, AudioNcenter, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_CENTER_LFE_VOLUME, 6, 0, 1, 0, 0x8080
- }, {
- /* LFE volume */
- AudioCoutputs, AudioNlfe, NULL, AUDIO_MIXER_VALUE,
- WRAP(ac97_volume_mono),
- AC97_REG_CENTER_LFE_VOLUME, 6, 8, 1, 0, 0x8080
- }, {
- /* External Amp */
- AudioCoutputs, AudioNextamp, NULL, AUDIO_MIXER_ENUM,
- WRAP(ac97_on_off),
- AC97_REG_POWER, 1, 15, 0, 0, 0x0000
- }
+ { AudioCinputs, NULL, NULL,
+ AUDIO_MIXER_CLASS, },
+ { AudioCoutputs, NULL, NULL,
+ AUDIO_MIXER_CLASS, },
+ { AudioCrecord, NULL, NULL,
+ AUDIO_MIXER_CLASS, },
+ /* Stereo master volume*/
+ { AudioCoutputs, AudioNmaster, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_MASTER_VOLUME, 0x8000, 5, 0, 1,
+ },
+ /* Mono volume */
+ { AudioCoutputs, AudioNmono, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_MASTER_VOLUME_MONO, 0x8000, 6, 0, 1,
+ },
+ { AudioCoutputs, AudioNmono, AudioNsource,
+ AUDIO_MIXER_ENUM, WRAP(ac97_mono_select),
+ AC97_REG_GP, 0x0000, 1, 9, 0,
+ },
+ /* Headphone volume */
+ { AudioCoutputs, AudioNheadphone, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_HEADPHONE_VOLUME, 0x8000, 6, 0, 1, 0, CHECK_HEADPHONES
+ },
+ /* Surround volume - logic hard coded for mute */
+ { AudioCoutputs, AudioNsurround, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_SURR_MASTER, 0x8080, 5, 0, 1, 0, CHECK_SURROUND
+ },
+ /* Center volume*/
+ { AudioCoutputs, AudioNcenter, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_CENTER_LFE_MASTER, 0x8080, 5, 0, 0, 0, CHECK_CENTER
+ },
+ { AudioCoutputs, AudioNcenter, AudioNmute,
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_REG_CENTER_LFE_MASTER, 0x8080, 1, 7, 0, 0, CHECK_CENTER
+ },
+ /* LFE volume*/
+ { AudioCoutputs, AudioNlfe, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_CENTER_LFE_MASTER, 0x8080, 5, 8, 0, 0, CHECK_LFE
+ },
+ { AudioCoutputs, AudioNlfe, AudioNmute,
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_REG_CENTER_LFE_MASTER, 0x8080, 1, 15, 0, 0, CHECK_LFE
+ },
+ /* Tone */
+ { AudioCoutputs, "tone", NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_MASTER_TONE, 0x0f0f, 4, 0, 0, 0, CHECK_TONE
+ },
+ /* PC Beep Volume */
+ { AudioCinputs, AudioNspeaker, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_PCBEEP_VOLUME, 0x0000, 4, 1, 1,
+ },
+
+ /* Phone */
+ { AudioCinputs, "phone", NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_PHONE_VOLUME, 0x8008, 5, 0, 1,
+ },
+ /* Mic Volume */
+ { AudioCinputs, AudioNmicrophone, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_MIC_VOLUME, 0x8008, 5, 0, 1,
+ },
+ { AudioCinputs, AudioNmicrophone, AudioNpreamp,
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_REG_MIC_VOLUME, 0x8008, 1, 6, 0,
+ },
+ { AudioCinputs, AudioNmicrophone, AudioNsource,
+ AUDIO_MIXER_ENUM, WRAP(ac97_mic_select),
+ AC97_REG_GP, 0x0000, 1, 8, 0,
+ },
+ /* Line in Volume */
+ { AudioCinputs, AudioNline, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_LINEIN_VOLUME, 0x8808, 5, 0, 1,
+ },
+ /* CD Volume */
+ { AudioCinputs, AudioNcd, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_CD_VOLUME, 0x8808, 5, 0, 1,
+ },
+ /* Video Volume */
+ { AudioCinputs, AudioNvideo, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_VIDEO_VOLUME, 0x8808, 5, 0, 1,
+ },
+ /* AUX volume */
+ { AudioCinputs, AudioNaux, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_AUX_VOLUME, 0x8808, 5, 0, 1,
+ },
+ /* PCM out volume */
+ { AudioCinputs, AudioNdac, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_PCMOUT_VOLUME, 0x8808, 5, 0, 1,
+ },
+ /* Record Source - some logic for this is hard coded - see below */
+ { AudioCrecord, AudioNsource, NULL,
+ AUDIO_MIXER_ENUM, WRAP(ac97_source),
+ AC97_REG_RECORD_SELECT, 0x0000, 3, 0, 0,
+ },
+ /* Record Gain */
+ { AudioCrecord, AudioNvolume, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_stereo),
+ AC97_REG_RECORD_GAIN, 0x8000, 4, 0, 1,
+ },
+ /* Record Gain mic */
+ { AudioCrecord, AudioNmicrophone, NULL,
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_RECORD_GAIN_MIC, 0x8000, 4, 0, 1, 1, CHECK_MIC
+ },
+ /* */
+ { AudioCoutputs, AudioNloudness, NULL,
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_REG_GP, 0x0000, 1, 12, 0, 0, CHECK_LOUDNESS
+ },
+ { AudioCoutputs, AudioNspatial, NULL,
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_REG_GP, 0x0000, 1, 13, 0, 1, CHECK_3D
+ },
+ { AudioCoutputs, AudioNspatial, "center",
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_3D_CONTROL, 0x0000, 4, 8, 0, 1, CHECK_3D
+ },
+ { AudioCoutputs, AudioNspatial, "depth",
+ AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
+ AC97_REG_3D_CONTROL, 0x0000, 4, 0, 0, 1, CHECK_3D
+ },
+ { AudioCoutputs, AudioNextamp, NULL,
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_REG_POWER, 0x0000, 1, 15, 0, 0
+ },
/* Missing features: Simulated Stereo, POP, Loopback mode */
};
@@ -283,12 +298,17 @@ const struct ac97_source_info {
*/
struct ac97_softc {
+ /* ac97_codec_if must be at the first of ac97_softc. */
struct ac97_codec_if codec_if;
struct ac97_host_if *host_if;
- struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
+#define MAX_SOURCES (2 * SOURCE_INFO_SIZE)
+ struct ac97_source_info source_info[MAX_SOURCES];
int num_source_info;
enum ac97_host_flags host_flags;
- u_int16_t caps, ext_id;
+ unsigned int ac97_clock; /* usually 48000 */
+#define AC97_STANDARD_CLOCK 48000U
+ u_int16_t caps; /* -> AC97_REG_RESET */
+ u_int16_t ext_id; /* -> AC97_REG_EXT_AUDIO_ID */
u_int16_t shadow_reg[128];
};
@@ -298,10 +318,15 @@ int ac97_query_devinfo(struct ac97_codec_if *, mixer_devinfo_t *);
int ac97_get_portnum_by_name(struct ac97_codec_if *, char *, char *,
char *);
void ac97_restore_shadow(struct ac97_codec_if *);
+int ac97_set_rate(struct ac97_codec_if *codec_if, int target, u_long *rate);
+void ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock);
+u_int16_t ac97_get_extcaps(struct ac97_codec_if *codec_if);
+int ac97_add_port(struct ac97_softc *as, struct ac97_source_info *src);
+void ac97_ad1885_init(struct ac97_softc *);
void ac97_ad1886_init(struct ac97_softc *);
void ac97_ad198x_init(struct ac97_softc *);
-void ac97_alc655_init(struct ac97_softc *);
+void ac97_alc650_init(struct ac97_softc *);
void ac97_cx20468_init(struct ac97_softc *);
struct ac97_codec_if_vtbl ac97civ = {
@@ -309,7 +334,10 @@ struct ac97_codec_if_vtbl ac97civ = {
ac97_mixer_set_port,
ac97_query_devinfo,
ac97_get_portnum_by_name,
- ac97_restore_shadow
+ ac97_restore_shadow,
+ ac97_get_extcaps,
+ ac97_set_rate,
+ ac97_set_clock
};
const struct ac97_codecid {
@@ -323,11 +351,11 @@ const struct ac97_codecid {
{ 0x03, 0xff, 0, 0, "AD1819" },
{ 0x40, 0xff, 0, 0, "AD1881" },
{ 0x48, 0xff, 0, 0, "AD1881A" },
- { 0x60, 0xff, 0, 0, "AD1885" },
+ { 0x60, 0xff, 0, 0, "AD1885", ac97_ad1885_init },
{ 0x61, 0xff, 0, 0, "AD1886", ac97_ad1886_init },
{ 0x63, 0xff, 0, 0, "AD1886A" },
{ 0x68, 0xff, 0, 0, "AD1888", ac97_ad198x_init },
- { 0x70, 0xff, 0, 0, "AD1980" },
+ { 0x70, 0xff, 0, 0, "AD1980", ac97_ad198x_init },
{ 0x72, 0xff, 0, 0, "AD1981A" },
{ 0x74, 0xff, 0, 0, "AD1981B" },
{ 0x75, 0xff, 0, 0, "AD1985", ac97_ad198x_init },
@@ -348,9 +376,9 @@ const struct ac97_codecid {
{ 0x40, 0xff, 0, 0, "ALC202" },
{ 0x50, 0xff, 0, 0, "ALC250" },
{ 0x52, 0xff, 0, 0, "ALC250A?" },
- { 0x60, 0xf0, 0xf, 0, "ALC655", ac97_alc655_init },
+ { 0x60, 0xf0, 0xf, 0, "ALC655", ac97_alc650_init },
{ 0x70, 0xf0, 0xf, 0, "ALC203" },
- { 0x80, 0xf0, 0xf, 0, "ALC658", ac97_alc655_init },
+ { 0x80, 0xf0, 0xf, 0, "ALC658", ac97_alc650_init },
{ 0x90, 0xf0, 0xf, 0, "ALC850" },
}, ac97_rl[] = {
{ 0x00, 0xf0, 0xf, 0, "RL5306" },
@@ -535,6 +563,7 @@ const char * const ac97feature[] = {
int ac97_str_equal(const char *, const char *);
+int ac97_check_capability(struct ac97_softc *, int);
void ac97_setup_source_info(struct ac97_softc *);
void ac97_setup_defaults(struct ac97_softc *);
int ac97_read(struct ac97_softc *, u_int8_t, u_int16_t *);
@@ -612,6 +641,34 @@ ac97_str_equal(const char *a, const char *b)
return ((a == b) || (a && b && (!strcmp(a, b))));
}
+int
+ac97_check_capability(struct ac97_softc *as, int check)
+{
+ switch (check) {
+ case CHECK_NONE:
+ return 1;
+ case CHECK_SURROUND:
+ return as->ext_id & AC97_EXT_AUDIO_SDAC;
+ case CHECK_CENTER:
+ return as->ext_id & AC97_EXT_AUDIO_CDAC;
+ case CHECK_LFE:
+ return as->ext_id & AC97_EXT_AUDIO_LDAC;
+ case CHECK_HEADPHONES:
+ return as->caps & AC97_CAPS_HEADPHONES;
+ case CHECK_TONE:
+ return as->caps & AC97_CAPS_TONECTRL;
+ case CHECK_MIC:
+ return as->caps & AC97_CAPS_MICIN;
+ case CHECK_LOUDNESS:
+ return as->caps & AC97_CAPS_LOUDNESS;
+ case CHECK_3D:
+ return AC97_CAPS_ENHANCEMENT(as->caps) != 0;
+ default:
+ printf("%s: internal error: feature=%d\n", __func__, check);
+ return 0;
+ }
+}
+
void
ac97_setup_source_info(struct ac97_softc *as)
{
@@ -621,6 +678,9 @@ ac97_setup_source_info(struct ac97_softc *as)
for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
si = &as->source_info[ouridx];
+ if (!ac97_check_capability(as, source_info[idx].req_feature))
+ continue;
+
bcopy(&source_info[idx], si, sizeof(*si));
switch (si->type) {
@@ -708,6 +768,7 @@ ac97_attach(struct ac97_host_if *host_if)
struct ac97_softc *as;
u_int16_t id1, id2;
u_int32_t id;
+ u_int16_t extstat, rate;
mixer_ctrl_t ctl;
int error, i;
void (*initfunc)(struct ac97_softc *);
@@ -781,21 +842,46 @@ ac97_attach(struct ac97_host_if *host_if)
ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
}
+
+ as->ac97_clock = AC97_STANDARD_CLOCK;
ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
- if (as->ext_id)
- DPRINTF(("ac97: ext id %b\n", as->ext_id,
- AC97_EXT_AUDIO_BITS));
- if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_VRM)) {
- ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id1);
+ if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_DRA
+ | AC97_EXT_AUDIO_SPDIF | AC97_EXT_AUDIO_VRM
+ | AC97_EXT_AUDIO_CDAC | AC97_EXT_AUDIO_SDAC
+ | AC97_EXT_AUDIO_LDAC)) {
+
+ ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &extstat);
if (as->ext_id & AC97_EXT_AUDIO_VRA)
- id1 |= AC97_EXT_AUDIO_VRA;
+ extstat |= AC97_EXT_AUDIO_VRA;
+ extstat &= ~AC97_EXT_AUDIO_DRA;
if (as->ext_id & AC97_EXT_AUDIO_VRM)
- id1 |= AC97_EXT_AUDIO_VRM;
- ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id1);
+ 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;
+ ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, extstat);
+ if (as->ext_id & AC97_EXT_AUDIO_VRA) {
+ /* VRA should be enabled. */
+ /* so it claims to do variable rate, let's make sure */
+ ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE, 44100);
+ ac97_read(as, AC97_REG_PCM_FRONT_DAC_RATE, &rate);
+ if (rate != 44100) {
+ /* We can't believe ext_id */
+ as->ext_id = 0;
+ }
+ /* restore the default value */
+ ac97_write(as, AC97_REG_PCM_FRONT_DAC_RATE,
+ AC97_SINGLE_RATE);
+ }
}
ac97_setup_source_info(as);
+ DELAY(900 * 1000);
+
/* use initfunc for specific device */
if (initfunc != NULL)
initfunc(as);
@@ -846,6 +932,8 @@ ac97_query_devinfo(struct ac97_codec_if *codec_if, mixer_devinfo_t *dip)
name = si->device;
else if (si->class)
name = si->class;
+ else
+ name = NULL;
if (name)
strlcpy(dip->label.name, name, sizeof dip->label.name);
@@ -890,7 +978,12 @@ ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
if (si->reg == AC97_REG_RECORD_SELECT) {
newval |= (newval << (8 + si->ofs));
mask |= (mask << 8);
- }
+ mask = mask << si->ofs;
+ } else if (si->reg == AC97_REG_SURR_MASTER) {
+ newval = cp->un.ord ? 0x8080 : 0x0000;
+ mask = 0x8080;
+ } else
+ mask = mask << si->ofs;
if (si->mute) {
newval |= newval << 8;
@@ -932,14 +1025,13 @@ ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
newval |= ((r & mask) << (si->ofs + 8));
mask |= (mask << 8);
}
-
+ mask = mask << si->ofs;
break;
}
default:
return (EINVAL);
}
- mask = mask << si->ofs;
error = ac97_write(as, si->reg, (val & ~mask) | newval);
if (error)
return (error);
@@ -947,6 +1039,180 @@ ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
return (0);
}
+
+int
+ac97_set_rate(struct ac97_codec_if *codec_if, int target, u_long *rate)
+{
+ struct ac97_softc *as;
+ u_long value;
+ u_int16_t ext_stat;
+ u_int16_t actual;
+ u_int16_t power;
+ u_int16_t power_bit;
+
+ as = (struct ac97_softc *)codec_if;
+
+ if ((target == AC97_REG_PCM_SURR_DAC_RATE) &&
+ !(as->ext_id & AC97_EXT_AUDIO_SDAC))
+ return 0;
+ if ((target == AC97_REG_PCM_LFE_DAC_RATE) &&
+ !(as->ext_id & AC97_EXT_AUDIO_LDAC))
+ return 0;
+ if (target == AC97_REG_PCM_MIC_ADC_RATE) {
+ if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
+ *rate = AC97_SINGLE_RATE;
+ return 0;
+ }
+ } else {
+ if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
+ *rate = AC97_SINGLE_RATE;
+ return 0;
+ }
+ }
+ if (as->ac97_clock == 0)
+ as->ac97_clock = AC97_STANDARD_CLOCK;
+ value = *rate * AC97_STANDARD_CLOCK / as->ac97_clock;
+ ext_stat = 0;
+ /*
+ * PCM_FRONT_DAC_RATE/PCM_SURR_DAC_RATE/PCM_LFE_DAC_RATE
+ * Check VRA, DRA
+ * PCM_LR_ADC_RATE
+ * Check VRA
+ * PCM_MIC_ADC_RATE
+ * Check VRM
+ */
+ switch (target) {
+ case AC97_REG_PCM_FRONT_DAC_RATE:
+ case AC97_REG_PCM_SURR_DAC_RATE:
+ case AC97_REG_PCM_LFE_DAC_RATE:
+ power_bit = AC97_POWER_OUT;
+ if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
+ *rate = AC97_SINGLE_RATE;
+ return 0;
+ }
+ if (as->ext_id & AC97_EXT_AUDIO_DRA) {
+ ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &ext_stat);
+ if (value > 0x1ffff) {
+ return EINVAL;
+ } else if (value > 0xffff) {
+ /* Enable DRA */
+ ext_stat |= AC97_EXT_AUDIO_DRA;
+ ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
+ value /= 2;
+ } else {
+ /* Disable DRA */
+ ext_stat &= ~AC97_EXT_AUDIO_DRA;
+ ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, ext_stat);
+ }
+ } else {
+ if (value > 0xffff)
+ return EINVAL;
+ }
+ break;
+ case AC97_REG_PCM_LR_ADC_RATE:
+ power_bit = AC97_POWER_IN;
+ if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
+ *rate = AC97_SINGLE_RATE;
+ return 0;
+ }
+ if (value > 0xffff)
+ return EINVAL;
+ break;
+ case AC97_REG_PCM_MIC_ADC_RATE:
+ power_bit = AC97_POWER_IN;
+ if (!(as->ext_id & AC97_EXT_AUDIO_VRM)) {
+ *rate = AC97_SINGLE_RATE;
+ return 0;
+ }
+ if (value > 0xffff)
+ return EINVAL;
+ break;
+ default:
+ printf("%s: Unknown register: 0x%x\n", __func__, target);
+ return EINVAL;
+ }
+
+ ac97_read(as, AC97_REG_POWER, &power);
+ ac97_write(as, AC97_REG_POWER, power | power_bit);
+
+ ac97_write(as, target, (u_int16_t)value);
+ ac97_read(as, target, &actual);
+ actual = (u_int32_t)actual * as->ac97_clock / AC97_STANDARD_CLOCK;
+
+ ac97_write(as, AC97_REG_POWER, power);
+ if (ext_stat & AC97_EXT_AUDIO_DRA) {
+ *rate = actual * 2;
+ } else {
+ *rate = actual;
+ }
+ return 0;
+}
+
+void
+ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock)
+{
+ struct ac97_softc *as;
+
+ as = (struct ac97_softc *)codec_if;
+ as->ac97_clock = clock;
+}
+
+u_int16_t
+ac97_get_extcaps(struct ac97_codec_if *codec_if)
+{
+ struct ac97_softc *as;
+
+ as = (struct ac97_softc *)codec_if;
+ return as->ext_id;
+}
+
+int
+ac97_add_port(struct ac97_softc *as, struct ac97_source_info *src)
+{
+ struct ac97_source_info *si;
+ int ouridx, idx;
+
+ if (as->num_source_info >= MAX_SOURCES) {
+ printf("%s: internal error: increase MAX_SOURCES in %s\n",
+ __func__, __FILE__);
+ return -1;
+ }
+ if (!ac97_check_capability(as, src->req_feature))
+ return -1;
+ ouridx = as->num_source_info;
+ si = &as->source_info[ouridx];
+ memcpy(si, src, sizeof(*si));
+
+ switch (si->type) {
+ case AUDIO_MIXER_CLASS:
+ case AUDIO_MIXER_VALUE:
+ printf("%s: adding class/value is not supported yet.\n",
+ __func__);
+ return -1;
+ case AUDIO_MIXER_ENUM:
+ break;
+ default:
+ printf("%s: unknown type: %d\n", __func__, si->type);
+ return -1;
+ }
+ as->num_source_info++;
+
+ si->mixer_class = ac97_get_portnum_by_name(&as->codec_if, si->class,
+ NULL, NULL);
+ /* Find the root of the device */
+ idx = ac97_get_portnum_by_name(&as->codec_if, si->class,
+ si->device, NULL);
+ /* Find the last item */
+ while (as->source_info[idx].next != AUDIO_MIXER_LAST)
+ idx = as->source_info[idx].next;
+ /* Append */
+ as->source_info[idx].next = ouridx;
+ si->prev = idx;
+ si->next = AUDIO_MIXER_LAST;
+
+ return 0;
+}
+
int
ac97_get_portnum_by_name(struct ac97_codec_if *codec_if, char *class,
char *device, char *qualifier)
@@ -1038,61 +1304,24 @@ ac97_mixer_get_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
return (0);
}
-int
-ac97_set_rate(struct ac97_codec_if *codec_if, struct audio_params *p,
- int mode)
-{
- struct ac97_softc *as = (struct ac97_softc *)codec_if;
- u_int16_t reg, val, regval, id = 0;
- DPRINTFN(5, ("set_rate(%lu) ", p->sample_rate));
+/*
+ * Codec-dependent initialization
+ */
- if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
- p->sample_rate = AC97_SINGLERATE;
- return (0);
- }
+void
+ac97_ad1885_init(struct ac97_softc *as)
+{
+ int i;
- if (p->sample_rate > 0xffff) {
- if (mode != AUMODE_PLAY)
- return (EINVAL);
- if (!(as->ext_id & AC97_EXT_AUDIO_DRA))
- return (EINVAL);
- if (ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id))
- return (EIO);
- id |= AC97_EXT_AUDIO_DRA;
- if (ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id))
- return (EIO);
- p->sample_rate /= 2;
+ for (i = 0; i < as->num_source_info; i++) {
+ if (as->source_info[i].reg == AC97_REG_HEADPHONE_VOLUME)
+ as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
+ else if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME)
+ as->source_info[i].reg = AC97_REG_HEADPHONE_VOLUME;
}
-
- /* i guess it's better w/o clicks and squeecks when changing the rate */
- if (ac97_read(as, AC97_REG_POWER, &val) ||
- ac97_write(as, AC97_REG_POWER, val |
- (mode == AUMODE_PLAY? AC97_POWER_OUT : AC97_POWER_IN)))
- return (EIO);
-
- reg = mode == AUMODE_PLAY ?
- AC97_REG_FRONT_DAC_RATE : AC97_REG_PCM_ADC_RATE;
-
- if (ac97_write(as, reg, (u_int16_t) p->sample_rate) ||
- ac97_read(as, reg, &regval))
- return (EIO);
- p->sample_rate = regval;
- if (id & AC97_EXT_AUDIO_DRA)
- p->sample_rate *= 2;
-
- DPRINTFN(5, (" %lu\n", regval));
-
- if (ac97_write(as, AC97_REG_POWER, val))
- return (EIO);
-
- return (0);
}
-/*
- * Codec-dependent initialization
- */
-
#define AC97_AD1886_JACK_SENSE 0x72
void
@@ -1100,7 +1329,7 @@ ac97_ad1886_init(struct ac97_softc *as)
{
ac97_write(as, AC97_AD1886_JACK_SENSE, 0x0010);
}
-
+
void
ac97_ad198x_init(struct ac97_softc *as)
{
@@ -1112,33 +1341,33 @@ ac97_ad198x_init(struct ac97_softc *as)
misc|AC97_AD_MISC_HPSEL|AC97_AD_MISC_LOSEL);
for (i = 0; i < as->num_source_info; i++) {
- if (as->source_info[i].reg == AC97_REG_SURROUND_VOLUME)
+ if (as->source_info[i].reg == AC97_REG_SURR_MASTER)
as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
- else if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME) {
- as->source_info[i].reg = AC97_REG_SURROUND_VOLUME;
- if (as->source_info[i].type == AUDIO_MIXER_ENUM) {
- as->source_info[i].mute = 1;
- as->source_info[i].ofs = 7;
- }
- }
+ else if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME)
+ as->source_info[i].reg = AC97_REG_SURR_MASTER;
}
}
void
-ac97_alc655_init(struct ac97_softc *as)
+ac97_alc650_init(struct ac97_softc *as)
{
- u_int16_t misc;
-
- ac97_read(as, AC97_AV_REG_MISC, &misc);
- if (as->host_flags & AC97_HOST_DONT_ENABLE_SPDIF) {
- misc &= ~AC97_AV_MISC_SPDIFEN;
- } else {
- misc |= AC97_AV_MISC_SPDIFEN;
- }
- misc &= ~AC97_AV_MISC_VREFDIS;
- ac97_write(as, AC97_AV_REG_MISC, misc);
-
- ac97_write(as, AC97_AV_REG_MULTICH, AC97_AV_MULTICH_MAGIC);
+ struct ac97_source_info sources[3] = {
+ { AudioCoutputs, AudioNsurround, "lineinjack",
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_ALC650_REG_MULTI_CHANNEL_CONTROL,
+ 0x0000, 1, 9, 0, 0, CHECK_SURROUND },
+ { AudioCoutputs, AudioNcenter, "micjack",
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_ALC650_REG_MULTI_CHANNEL_CONTROL,
+ 0x0000, 1, 10, 0, 0, CHECK_CENTER },
+ { AudioCoutputs, AudioNlfe, "micjack",
+ AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
+ AC97_ALC650_REG_MULTI_CHANNEL_CONTROL,
+ 0x0000, 1, 10, 0, 0, CHECK_LFE }};
+
+ ac97_add_port(as, &sources[0]);
+ ac97_add_port(as, &sources[1]);
+ ac97_add_port(as, &sources[2]);
}
void
diff --git a/sys/dev/ic/ac97.h b/sys/dev/ic/ac97.h
index 4cd38c28d4c..1af33999510 100644
--- a/sys/dev/ic/ac97.h
+++ b/sys/dev/ic/ac97.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ac97.h,v 1.19 2006/04/27 21:40:00 matthieu Exp $ */
+/* $OpenBSD: ac97.h,v 1.20 2008/01/15 02:52:50 jakemsr Exp $ */
/*
* Copyright (c) 1999 Constantine Sapuntzakis
@@ -67,6 +67,12 @@ struct ac97_codec_if_vtbl {
* after resume from a laptop suspend to disk.
*/
void (*restore_ports)(struct ac97_codec_if *addr);
+
+ u_int16_t (*get_caps)(struct ac97_codec_if *codec_if);
+ int (*set_rate)(struct ac97_codec_if *codec_if, int target,
+ u_long *rate);
+ void (*set_clock)(struct ac97_codec_if *codec_if,
+ unsigned int clock);
};
struct ac97_codec_if {
@@ -74,7 +80,7 @@ struct ac97_codec_if {
};
int ac97_attach(struct ac97_host_if *);
-int ac97_set_rate(struct ac97_codec_if *, struct audio_params *, int);
+int ac97_set_rate(struct ac97_codec_if *, int, u_long *);
#define AC97_REG_RESET 0x00
#define AC97_CAPS_MICIN 0x0001
@@ -139,14 +145,14 @@ int ac97_set_rate(struct ac97_codec_if *, struct audio_params *, int);
#define AC97_EXT_AUDIO_REV_MASK 0x0c00
#define AC97_EXT_AUDIO_ID 0xc000
#define AC97_EXT_AUDIO_BITS "\020\01vra\02dra\03spdif\04vrm\05dsa0\06dsa1\07cdac\010sdac\011ldac\012amap\013rev0\014rev1\017id0\020id1"
-#define AC97_SINGLERATE 48000
-#define AC97_REG_FRONT_DAC_RATE 0x2c
-#define AC97_REG_SURROUND_DAC_RATE 0x2e
-#define AC97_REG_PCM_DAC_RATE 0x30
-#define AC97_REG_PCM_ADC_RATE 0x32
-#define AC97_REG_MIC_ADC_RATE 0x34
-#define AC97_REG_CENTER_LFE_VOLUME 0x36
-#define AC97_REG_SURROUND_VOLUME 0x38
+#define AC97_SINGLE_RATE 48000
+#define AC97_REG_PCM_FRONT_DAC_RATE 0x2c
+#define AC97_REG_PCM_SURR_DAC_RATE 0x2e
+#define AC97_REG_PCM_LFE_DAC_RATE 0x30
+#define AC97_REG_PCM_LR_ADC_RATE 0x32
+#define AC97_REG_PCM_MIC_ADC_RATE 0x34
+#define AC97_REG_CENTER_LFE_MASTER 0x36
+#define AC97_REG_SURR_MASTER 0x38
#define AC97_REG_SPDIF_CTRL 0x3a
#define AC97_REG_SPDIF_CTRL_BITS "\02\01pro\02/audio\03copy\04pre\014l\017drs\020valid"
@@ -154,6 +160,7 @@ int ac97_set_rate(struct ac97_codec_if *, struct audio_params *, int);
#define AC97_REG_VENDOR_ID2 0x7e
#define AC97_VENDOR_ID_MASK 0xffffff00
+
/* Analog Devices codec specific data */
#define AC97_AD_REG_MISC 0x76
#define AC97_AD_MISC_MBG0 0x0001 /* 0 */
@@ -174,11 +181,21 @@ int ac97_set_rate(struct ac97_codec_if *, struct audio_params *, int);
#define AC97_AD_MISC_DACZ 0x8000 /*15 */
/* Avance Logic codec specific data*/
-#define AC97_AV_REG_MULTICH 0x6a
-#define AC97_AV_MULTICH_MAGIC 0x8000
-#define AC97_AV_REG_MISC 0x7a
-#define AC97_AV_MISC_SPDIFEN 0x0002
-#define AC97_AV_MISC_VREFDIS 0x1000
+#define AC97_ALC650_REG_MULTI_CHANNEL_CONTROL 0x6a
+#define AC97_ALC650_MCC_SLOT_MODIFY_MASK 0xc000
+#define AC97_ALC650_MCC_FRONTDAC_FROM_SPDIFIN 0x2000
+#define AC97_ALC650_MCC_SPDIFOUT_FROM_ADC 0x1000
+#define AC97_ALC650_MCC_PCM_FROM_SPDIFIN 0x0800
+#define AC97_ALC650_MCC_MIC_OR_CENTERLFE 0x0400
+#define AC97_ALC650_MCC_LINEIN_OR_SURROUND 0x0200
+#define AC97_ALC650_MCC_INDEPENDENT_MASTER_L 0x0080
+#define AC97_ALC650_MCC_INDEPENDENT_MASTER_R 0x0040
+#define AC97_ALC650_MCC_ANALOG_TO_CENTERLFE 0x0020
+#define AC97_ALC650_MCC_ANALOG_TO_SURROUND 0x0010
+#define AC97_ALC650_MCC_EXCHANGE_CENTERLFE 0x0008
+#define AC97_ALC650_MCC_CENTERLFE_DOWNMIX 0x0004
+#define AC97_ALC650_MCC_SURROUND_DOWNMIX 0x0002
+#define AC97_ALC650_MCC_LINEOUT_TO_SURROUND 0x0001
/* Conexant codec specific data */
#define AC97_CX_REG_MISC 0x5c
@@ -187,3 +204,8 @@ int ac97_set_rate(struct ac97_codec_if *, struct audio_params *, int);
#define AC97_CX_MASK 0x03
#define AC97_CX_COPYRIGHT 0x04
#define AC97_CX_SPDIFEN 0x08
+
+#define AC97_IS_FIXED_RATE(codec) !((codec)->vtbl->get_caps(codec) & \
+ AC97_EXT_AUDIO_VRA)
+#define AC97_BITS_6CH (AC97_EXT_AUDIO_SDAC | AC97_EXT_AUDIO_CDAC | \
+ AC97_EXT_AUDIO_LDAC)
diff --git a/sys/dev/pci/auich.c b/sys/dev/pci/auich.c
index c5dfaed8fd4..95296a9fae2 100644
--- a/sys/dev/pci/auich.c
+++ b/sys/dev/pci/auich.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auich.c,v 1.67 2007/10/23 19:59:27 jakemsr Exp $ */
+/* $OpenBSD: auich.c,v 1.68 2008/01/15 02:52:50 jakemsr Exp $ */
/*
* Copyright (c) 2000,2001 Michael Shalayeff
@@ -874,12 +874,27 @@ auich_set_params(v, setmode, usemode, play, rec)
orate = adj_rate = play->sample_rate;
if (sc->sc_ac97rate != 0)
adj_rate = orate * AUICH_FIXED_RATE / sc->sc_ac97rate;
+
+ play->sample_rate = adj_rate;
+ error = ac97_set_rate(sc->codec_if,
+ AC97_REG_PCM_FRONT_DAC_RATE, &play->sample_rate);
+ if (error)
+ return (error);
+
play->sample_rate = adj_rate;
- error = ac97_set_rate(sc->codec_if, play, AUMODE_PLAY);
- if (play->sample_rate == adj_rate)
- play->sample_rate = orate;
+ error = ac97_set_rate(sc->codec_if,
+ AC97_REG_PCM_SURR_DAC_RATE, &play->sample_rate);
if (error)
return (error);
+
+ play->sample_rate = adj_rate;
+ error = ac97_set_rate(sc->codec_if,
+ AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate);
+ if (error)
+ return (error);
+
+ play->sample_rate = orate;
+ return (error);
}
if (setmode & AUMODE_RECORD) {
@@ -1033,7 +1048,8 @@ auich_set_params(v, setmode, usemode, play, rec)
if (sc->sc_ac97rate != 0)
rec->sample_rate = orate * AUICH_FIXED_RATE /
sc->sc_ac97rate;
- error = ac97_set_rate(sc->codec_if, rec, AUMODE_RECORD);
+ error = ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE,
+ &rec->sample_rate);
rec->sample_rate = orate;
if (error)
return (error);
diff --git a/sys/dev/pci/auixp.c b/sys/dev/pci/auixp.c
index 6c535acafa1..556ba54ccbf 100644
--- a/sys/dev/pci/auixp.c
+++ b/sys/dev/pci/auixp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auixp.c,v 1.13 2007/11/05 00:17:28 jakemsr Exp $ */
+/* $OpenBSD: auixp.c,v 1.14 2008/01/15 02:52:50 jakemsr Exp $ */
/* $NetBSD: auixp.c,v 1.9 2005/06/27 21:13:09 thorpej Exp $ */
/*
@@ -364,6 +364,7 @@ auixp_set_params(void *hdl, int setmode, int usemode,
struct auixp_codec *co;
struct auixp_softc *sc;
int error;
+ u_int temprate;
co = (struct auixp_codec *) hdl;
sc = co->sc;
@@ -534,9 +535,23 @@ auixp_set_params(void *hdl, int setmode, int usemode,
return (EINVAL);
}
- error = ac97_set_rate(co->codec_if, play, AUMODE_PLAY);
+ temprate = play->sample_rate;
+ error = ac97_set_rate(co->codec_if,
+ AC97_REG_PCM_FRONT_DAC_RATE, &play->sample_rate);
if (error)
return (error);
+ play->sample_rate = temprate;
+ error = ac97_set_rate(co->codec_if,
+ AC97_REG_PCM_SURR_DAC_RATE, &play->sample_rate);
+ if (error)
+ return (error);
+ play->sample_rate = temprate;
+ error = ac97_set_rate(co->codec_if,
+ AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate);
+
+ if (error)
+ return (error);
+
}
if (setmode & AUMODE_RECORD) {
@@ -571,7 +586,8 @@ auixp_set_params(void *hdl, int setmode, int usemode,
return (EINVAL);
}
- error = ac97_set_rate(co->codec_if, rec, AUMODE_RECORD);
+ error = ac97_set_rate(co->codec_if, AC97_REG_PCM_LR_ADC_RATE,
+ &rec->sample_rate);
if (error)
return (error);
}
diff --git a/sys/dev/pci/auvia.c b/sys/dev/pci/auvia.c
index cb09c29ce39..8d62f3c4cf5 100644
--- a/sys/dev/pci/auvia.c
+++ b/sys/dev/pci/auvia.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: auvia.c,v 1.33 2005/05/06 01:45:22 miod Exp $ */
-/* $NetBSD: auvia.c,v 1.7 2000/11/15 21:06:33 jdolecek Exp $ */
+/* $OpenBSD: auvia.c,v 1.34 2008/01/15 02:52:50 jakemsr Exp $ */
+/* $NetBSD: auvia.c,v 1.28 2002/11/04 16:38:49 kent Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -79,14 +79,13 @@ struct auvia_dma_op {
#define AUVIA_DMAOP_COUNT(x) ((x)&0x00FFFFFF)
};
-/* rev. H and later seem to support only fixed rate 44.1 kHz */
-#define AUVIA_FIXED_RATE 44100
-
int auvia_match(struct device *, void *, void *);
void auvia_attach(struct device *, struct device *, void *);
int auvia_open(void *, int);
void auvia_close(void *);
-int auvia_query_encoding(void *addr, struct audio_encoding *fp);
+int auvia_query_encoding(void *, struct audio_encoding *);
+void auvia_set_params_sub(struct auvia_softc *, struct auvia_softc_chan *,
+ struct audio_params *);
int auvia_set_params(void *, int, int, struct audio_params *,
struct audio_params *);
int auvia_round_blocksize(void *, int);
@@ -131,6 +130,7 @@ struct cfattach auvia_ca = {
#define AUVIA_PLAY_BASE 0x00
#define AUVIA_RECORD_BASE 0x10
+/* *_RP_* are offsets from AUVIA_PLAY_BASE or AUVIA_RECORD_BASE */
#define AUVIA_RP_STAT 0x00
#define AUVIA_RPSTAT_INTR 0x03
#define AUVIA_RP_CONTROL 0x01
@@ -141,7 +141,7 @@ struct cfattach auvia_ca = {
#define AUVIA_RPCTRL_STOP 0x04
#define AUVIA_RPCTRL_EOL 0x02
#define AUVIA_RPCTRL_FLAG 0x01
-#define AUVIA_RP_MODE 0x02
+#define AUVIA_RP_MODE 0x02 /* 82c686 specific */
#define AUVIA_RPMODE_INTR_FLAG 0x01
#define AUVIA_RPMODE_INTR_EOL 0x02
#define AUVIA_RPMODE_STEREO 0x10
@@ -156,7 +156,16 @@ struct cfattach auvia_ca = {
#define VIA8233_RATEFMT_STEREO 0x00100000
#define VIA8233_RATEFMT_16BIT 0x00200000
-#define VIA_RP_DMAOPS_COUNT 0x0C
+#define VIA_RP_DMAOPS_COUNT 0x0c
+
+#define VIA8233_MP_BASE 0x40
+ /* STAT, CONTROL, DMAOPS_BASE, DMAOPS_COUNT are valid */
+#define VIA8233_OFF_MP_FORMAT 0x02
+#define VIA8233_MP_FORMAT_8BIT 0x00
+#define VIA8233_MP_FORMAT_16BIT 0x80
+#define VIA8233_MP_FORMAT_CHANNLE_MASK 0x70 /* 1, 2, 4, 6 */
+#define VIA8233_OFF_MP_SCRATCH 0x03
+#define VIA8233_OFF_MP_STOP 0x08
#define AUVIA_CODEC_CTL 0x80
#define AUVIA_CODEC_READ 0x00800000
@@ -164,6 +173,15 @@ struct cfattach auvia_ca = {
#define AUVIA_CODEC_PRIVALID 0x02000000
#define AUVIA_CODEC_INDEX(x) ((x)<<16)
+#define CH_WRITE1(sc, ch, off, v) \
+ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off), v)
+#define CH_WRITE4(sc, ch, off, v) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off), v)
+#define CH_READ1(sc, ch, off) \
+ bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off))
+#define CH_READ4(sc, ch, off) \
+ bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off))
+
#define TIMEOUT 50
struct audio_hw_if auvia_hw_if = {
@@ -229,8 +247,12 @@ auvia_attach(struct device *parent, struct device *self, void *aux)
pcireg_t pr;
int r, i;
- if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT8233_AC97)
+ sc->sc_play.sc_base = AUVIA_PLAY_BASE;
+ sc->sc_record.sc_base = AUVIA_RECORD_BASE;
+ if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT8233_AC97) {
sc->sc_flags |= AUVIA_FLAGS_VT8233;
+ sc->sc_play.sc_base = VIA8233_MP_BASE;
+ }
if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
&sc->sc_ioh, NULL, &iosize, 0)) {
@@ -514,15 +536,57 @@ auvia_query_encoding(void *addr, struct audio_encoding *fp)
}
}
+void
+auvia_set_params_sub(struct auvia_softc *sc, struct auvia_softc_chan *ch,
+ struct audio_params *p)
+{
+ u_int32_t v;
+ u_int16_t regval;
+
+ if (!(sc->sc_flags & AUVIA_FLAGS_VT8233)) {
+ regval = (p->channels == 2 ? AUVIA_RPMODE_STEREO : 0)
+ | (p->precision * p->factor == 16 ?
+ AUVIA_RPMODE_16BIT : 0)
+ | AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL
+ | AUVIA_RPMODE_AUTOSTART;
+ ch->sc_reg = regval;
+ } else if (ch->sc_base != VIA8233_MP_BASE) {
+ v = CH_READ4(sc, ch, VIA8233_RP_RATEFMT);
+ v &= ~(VIA8233_RATEFMT_48K | VIA8233_RATEFMT_STEREO
+ | VIA8233_RATEFMT_16BIT);
+
+ v |= VIA8233_RATEFMT_48K * (p->sample_rate / 20)
+ / (48000 / 20);
+ if (p->channels == 2)
+ v |= VIA8233_RATEFMT_STEREO;
+ if (p->precision == 16)
+ v |= VIA8233_RATEFMT_16BIT;
+
+ CH_WRITE4(sc, ch, VIA8233_RP_RATEFMT, v);
+ } else {
+ static const u_int32_t slottab[7] =
+ { 0, 0xff000011, 0xff000021, 0,
+ 0xff004321, 0, 0xff436521};
+
+ regval = (p->precision == 16
+ ? VIA8233_MP_FORMAT_16BIT : VIA8233_MP_FORMAT_8BIT)
+ | (p->channels << 4);
+ CH_WRITE1(sc, ch, VIA8233_OFF_MP_FORMAT, regval);
+ CH_WRITE4(sc, ch, VIA8233_OFF_MP_STOP, slottab[p->channels]);
+ }
+}
+
int
auvia_set_params(void *addr, int setmode, int usemode,
struct audio_params *play, struct audio_params *rec)
{
struct auvia_softc *sc = addr;
+ struct auvia_softc_chan *ch;
struct audio_params *p;
- u_int16_t regval;
- int mode, base;
+ struct ac97_codec_if* codec = sc->codec_if;
+ int reg, mode;
+ u_int16_t ext_id;
/* for mode in (RECORD, PLAY) */
for (mode = AUMODE_RECORD; mode != -1;
@@ -532,35 +596,58 @@ auvia_set_params(void *addr, int setmode, int usemode,
if (mode == AUMODE_PLAY) {
p = play;
- base = AUVIA_PLAY_BASE;
+ ch = &sc->sc_play;
+ reg = AC97_REG_PCM_FRONT_DAC_RATE;
} else {
p = rec;
- base = AUVIA_RECORD_BASE;
+ ch = &sc->sc_record;
+ reg = AC97_REG_PCM_LR_ADC_RATE;
}
- if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
- u_int32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
- base + VIA8233_RP_RATEFMT) & ~(VIA8233_RATEFMT_48K
- | VIA8233_RATEFMT_STEREO | VIA8233_RATEFMT_16BIT);
-
- v |= VIA8233_RATEFMT_48K *
- (p->sample_rate / 20) / (48000 / 20);
-
- if (p->channels == 2)
- v |= VIA8233_RATEFMT_STEREO;
- if (p->precision == 16)
- v |= VIA8233_RATEFMT_16BIT;
-
- bus_space_write_4(sc->sc_iot, sc->sc_ioh,
- base + VIA8233_RP_RATEFMT, v);
+ if (ch->sc_base == VIA8233_MP_BASE && mode == AUMODE_PLAY) {
+ ext_id = codec->vtbl->get_caps(codec);
+ if (p->channels == 1) {
+ /* ok */
+ } else if (p->channels == 2) {
+ /* ok */
+ } else if (p->channels == 4
+ && ext_id & AC97_EXT_AUDIO_SDAC) {
+ /* ok */
+ } else if (p->channels == 6
+ && (ext_id & AC97_BITS_6CH) == AC97_BITS_6CH) {
+ /* ok */
+ } else {
+ return (EINVAL);
+ }
+ } else {
+ if (p->channels != 1 && p->channels != 2)
+ return (EINVAL);
}
- if (ac97_set_rate(sc->codec_if, p, mode))
+ if ((p->sample_rate < 4000 || p->sample_rate > 48000) ||
+ (p->precision != 8 && p->precision != 16))
return (EINVAL);
- if ((p->precision != 8 && p->precision != 16) ||
- (p->channels != 1 && p->channels != 2))
- return (EINVAL);
+ if (AC97_IS_FIXED_RATE(codec)) {
+ p->sample_rate = AC97_SINGLE_RATE;
+ } else {
+ if (codec->vtbl->set_rate(codec, reg, &p->sample_rate))
+ return (EINVAL);
+
+ if (ch->sc_base == VIA8233_MP_BASE &&
+ mode == AUMODE_PLAY) {
+ reg = AC97_REG_PCM_SURR_DAC_RATE;
+ if (p->channels >= 4
+ && codec->vtbl->set_rate(codec, reg,
+ &p->sample_rate))
+ return (EINVAL);
+ reg = AC97_REG_PCM_LFE_DAC_RATE;
+ if (p->channels == 6
+ && codec->vtbl->set_rate(codec, reg,
+ &p->sample_rate))
+ return (EINVAL);
+ }
+ }
p->factor = 1;
p->sw_code = 0;
@@ -605,17 +692,7 @@ auvia_set_params(void *addr, int setmode, int usemode,
default:
return (EINVAL);
}
-
- regval = (p->channels == 2 ? AUVIA_RPMODE_STEREO : 0)
- | (p->precision * p->factor == 16 ?
- AUVIA_RPMODE_16BIT : 0)
- | AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL
- | AUVIA_RPMODE_AUTOSTART;
-
- if (mode == AUMODE_PLAY)
- sc->sc_play.sc_reg = regval;
- else
- sc->sc_record.sc_reg = regval;
+ auvia_set_params_sub(sc, ch, p);
}
return 0;
@@ -633,10 +710,10 @@ int
auvia_halt_output(void *addr)
{
struct auvia_softc *sc = addr;
+ struct auvia_softc_chan *ch = &(sc->sc_play);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
-
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
+ ch->sc_intr = NULL;
return 0;
}
@@ -645,10 +722,10 @@ int
auvia_halt_input(void *addr)
{
struct auvia_softc *sc = addr;
+ struct auvia_softc_chan *ch = &(sc->sc_record);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
-
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
+ ch->sc_intr = NULL;
return 0;
}
@@ -878,25 +955,21 @@ auvia_trigger_output(void *addr, void *start, void *end, int blksize,
ch->sc_intr = intr;
ch->sc_arg = arg;
- bus_space_write_4(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_DMAOPS_BASE,
+ CH_WRITE4(sc, ch, AUVIA_RP_DMAOPS_BASE,
ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_MODE, ch->sc_reg);
-
if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + VIA8233_RP_DXS_LVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + VIA8233_RP_DXS_RVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_CONTROL,
+ if (ch->sc_base != VIA8233_MP_BASE) {
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_LVOL, 0);
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_RVOL, 0);
+ }
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL,
AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
AUVIA_RPCTRL_STOP | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
- } else
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
+ } else {
+ CH_WRITE1(sc, ch, AUVIA_RP_MODE, ch->sc_reg);
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
+ }
return 0;
}
@@ -923,25 +996,21 @@ auvia_trigger_input(void *addr, void *start, void *end, int blksize,
ch->sc_intr = intr;
ch->sc_arg = arg;
- bus_space_write_4(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_DMAOPS_BASE,
- ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_MODE, ch->sc_reg);
+ CH_WRITE4(sc, ch, AUVIA_RP_DMAOPS_BASE,
+ ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + VIA8233_RP_DXS_LVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + VIA8233_RP_DXS_RVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_CONTROL,
+ if (ch->sc_base != VIA8233_MP_BASE) {
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_LVOL, 0);
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_RVOL, 0);
+ }
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL,
AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
AUVIA_RPCTRL_STOP | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
- } else
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
-
+ } else {
+ CH_WRITE1(sc, ch, AUVIA_RP_MODE, ch->sc_reg);
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
+ }
return 0;
}
@@ -950,30 +1019,30 @@ int
auvia_intr(void *arg)
{
struct auvia_softc *sc = arg;
+ struct auvia_softc_chan *ch;
u_int8_t r;
int i = 0;
- r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_STAT);
+
+ ch = &sc->sc_record;
+ r = CH_READ1(sc, ch, AUVIA_RP_STAT);
if (r & AUVIA_RPSTAT_INTR) {
if (sc->sc_record.sc_intr)
sc->sc_record.sc_intr(sc->sc_record.sc_arg);
/* clear interrupts */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
+ CH_WRITE1(sc, ch, AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
i++;
}
- r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_STAT);
+ ch = &sc->sc_play;
+ r = CH_READ1(sc, ch, AUVIA_RP_STAT);
if (r & AUVIA_RPSTAT_INTR) {
if (sc->sc_play.sc_intr)
sc->sc_play.sc_intr(sc->sc_play.sc_arg);
/* clear interrupts */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
+ CH_WRITE1(sc, ch, AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
i++;
}
diff --git a/sys/dev/pci/auviavar.h b/sys/dev/pci/auviavar.h
index 67203d24a8c..e86f3d24097 100644
--- a/sys/dev/pci/auviavar.h
+++ b/sys/dev/pci/auviavar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: auviavar.h,v 1.7 2003/02/28 15:26:23 mickey Exp $ */
+/* $OpenBSD: auviavar.h,v 1.8 2008/01/15 02:52:50 jakemsr Exp $ */
/* $NetBSD: auviavar.h,v 1.1 2000/03/31 04:45:29 tsarna Exp $ */
/*-
@@ -48,6 +48,7 @@ struct auvia_softc_chan {
struct auvia_dma_op *sc_dma_ops;
struct auvia_dma *sc_dma_ops_dma;
u_int16_t sc_dma_op_count;
+ int sc_base;
u_int16_t sc_reg;
};