diff options
-rw-r--r-- | sys/dev/ic/ac97.c | 681 | ||||
-rw-r--r-- | sys/dev/ic/ac97.h | 52 | ||||
-rw-r--r-- | sys/dev/pci/auich.c | 26 | ||||
-rw-r--r-- | sys/dev/pci/auixp.c | 22 | ||||
-rw-r--r-- | sys/dev/pci/auvia.c | 241 | ||||
-rw-r--r-- | sys/dev/pci/auviavar.h | 3 |
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, ®val)) - 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; }; |