AC'97 modems don't have the same mixer controls as audio devices. Treat

them differently. Mixers are built depending on the features returned
in the ext_mid -- any combination of LINE1, LINE2, and HANDSET.
This commit is contained in:
jmcneill 2005-04-07 23:24:05 +00:00
parent c7acb3a606
commit 0f32e1f4a6

View File

@ -1,4 +1,4 @@
/* $NetBSD: ac97.c,v 1.68 2005/04/04 18:52:30 jmcneill Exp $ */
/* $NetBSD: ac97.c,v 1.69 2005/04/07 23:24:05 jmcneill Exp $ */
/* $OpenBSD: ac97.c,v 1.8 2000/07/19 09:01:35 csapuntz Exp $ */
/*
@ -63,7 +63,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ac97.c,v 1.68 2005/04/04 18:52:30 jmcneill Exp $");
__KERNEL_RCSID(0, "$NetBSD: ac97.c,v 1.69 2005/04/07 23:24:05 jmcneill Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -107,6 +107,11 @@ static int ac97_modem_offhook_set(struct ac97_softc *, int, int);
static int ac97_sysctl_verify(SYSCTLFN_ARGS);
#define Ac97Nphone "phone"
#define Ac97Cline1 "line1"
#define Ac97Cline2 "line2"
#define Ac97Chandset "handset"
#define Ac97Ndac "dac"
#define Ac97Nadc "adc"
static const struct audio_mixer_enum
ac97_on_off = { 2, { { { AudioNoff } , 0 },
@ -143,7 +148,7 @@ ac97_volume_mono = { { AudioNvolume }, 1 };
#define WRAP(a) &a, sizeof(a)
static const struct ac97_source_info {
struct ac97_source_info {
const char *class;
const char *device;
const char *qualifier;
@ -167,13 +172,18 @@ static const struct ac97_source_info {
CHECK_TONE,
CHECK_MIC,
CHECK_LOUDNESS,
CHECK_3D
CHECK_3D,
CHECK_LINE1,
CHECK_LINE2,
CHECK_HANDSET
} req_feature;
int prev;
int next;
int mixer_class;
} source_info[] = {
};
static const struct ac97_source_info audio_source_info[] = {
{ AudioCinputs, NULL, NULL,
AUDIO_MIXER_CLASS, },
{ AudioCoutputs, NULL, NULL,
@ -317,7 +327,79 @@ static const struct ac97_source_info {
/* Missing features: Simulated Stereo, POP, Loopback mode */
};
#define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
static const struct ac97_source_info modem_source_info[] = {
/* Classes */
{ AudioCmodem, NULL, NULL,
AUDIO_MIXER_CLASS, },
{ AudioCmodem, Ac97Cline1, NULL,
AUDIO_MIXER_CLASS, 0, 0, 0, 0, 0, 0, 0, CHECK_LINE1 },
#if 0
{ AudioCmodem, Ac97Cline2, NULL,
AUDIO_MIXER_CLASS, 0, 0, 0, 0, 0, 0, 0, CHECK_LINE2 },
{ AudioCmodem, Ac97Chandset, NULL,
AUDIO_MIXER_CLASS, 0, 0, 0, 0, 0, 0, 0, CHECK_HANDSET },
#endif
/* LINE1 */
{ Ac97Cline1, Ac97Nadc, NULL,
AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
AC97_REG_LINE1_LEVEL, 0x8080, 4, 0, 0, 1, CHECK_LINE1
},
{ Ac97Cline1, Ac97Nadc, AudioNmute,
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
AC97_REG_LINE1_LEVEL, 0x8080, 1, 7, 0, 0, CHECK_LINE1
},
{ Ac97Cline1, Ac97Ndac, NULL,
AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
AC97_REG_LINE1_LEVEL, 0x8080, 4, 8, 0, 1, CHECK_LINE1
},
{ Ac97Cline1, Ac97Ndac, AudioNmute,
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
AC97_REG_LINE1_LEVEL, 0x8080, 1, 15, 0, 0, CHECK_LINE1
},
#if 0
/* LINE2 */
{ AudioCmodem, Ac97Nline2, Ac97Nadcmute,
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
AC97_REG_LINE2_LEVEL, 0x8080, 1, 7, 0, 0, CHECK_LINE2
},
{ AudioCmodem, Ac97Nline2, Ac97Nadc,
AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
AC97_REG_LINE2_LEVEL, 0x8080, 4, 0, 0, 1, CHECK_LINE2
},
{ AudioCmodem, Ac97Nline2, Ac97Ndacmute,
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
AC97_REG_LINE2_LEVEL, 0x8080, 1, 15, 0, 0, CHECK_LINE2
},
{ AudioCmodem, Ac97Nline2, Ac97Ndac,
AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
AC97_REG_LINE2_LEVEL, 0x8080, 4, 8, 0, 1, CHECK_LINE2
},
/* HANDSET */
{ AudioCmodem, Ac97Nhandset, Ac97Nadcmute,
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
AC97_REG_HANDSET_LEVEL, 0x8080, 1, 7, 0, 0, CHECK_HANDSET
},
{ AudioCmodem, Ac97Nhandset, Ac97Nadc,
AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
AC97_REG_HANDSET_LEVEL, 0x8080, 4, 0, 0, 1, CHECK_HANDSET
},
{ AudioCmodem, Ac97Nhandset, Ac97Ndacmute,
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
AC97_REG_HANDSET_LEVEL, 0x8080, 1, 15, 0, 0, CHECK_HANDSET
},
{ AudioCmodem, Ac97Nhandset, Ac97Ndac,
AUDIO_MIXER_VALUE, WRAP(ac97_volume_mono),
AC97_REG_HANDSET_LEVEL, 0x8080, 4, 8, 0, 1, CHECK_HANDSET
},
#endif
};
#define AUDIO_SOURCE_INFO_SIZE \
(sizeof(audio_source_info)/sizeof(audio_source_info[0]))
#define MODEM_SOURCE_INFO_SIZE \
(sizeof(modem_source_info)/sizeof(modem_source_info[0]))
#define SOURCE_INFO_SIZE(as) ((as)->type == AC97_TYPE_MODEM ? \
MODEM_SOURCE_INFO_SIZE : AUDIO_SOURCE_INFO_SIZE)
/*
* Check out http://developer.intel.com/pc-supp/platform/ac97/ for
@ -330,8 +412,11 @@ struct ac97_softc {
struct ac97_host_if *host_if;
#define MAX_SOURCES (2 * SOURCE_INFO_SIZE)
struct ac97_source_info source_info[MAX_SOURCES];
#define AUDIO_MAX_SOURCES (2 * AUDIO_SOURCE_INFO_SIZE)
#define MODEM_MAX_SOURCES (2 * MODEM_SOURCE_INFO_SIZE)
struct ac97_source_info audio_source_info[AUDIO_MAX_SOURCES];
struct ac97_source_info modem_source_info[MODEM_MAX_SOURCES];
struct ac97_source_info *source_info;
int num_source_info;
enum ac97_host_flags host_flags;
@ -342,6 +427,10 @@ struct ac97_softc {
uint16_t ext_mid; /* -> AC97_REG_EXT_MODEM_ID */
uint16_t shadow_reg[128];
int type;
#define AC97_TYPE_AUDIO 1
#define AC97_TYPE_MODEM 2
/* sysctl */
struct sysctllog *log;
int offhook_line1_mib;
@ -636,6 +725,12 @@ static const struct ac97_codecid {
{ 0x83847684, 0xffffffff, "SigmaTel STAC9783/84", },
{ 0x83847600, AC97_VENDOR_ID_MASK, "SigmaTel unknown", },
/* Conexant AC'97 modems -- good luck finding datasheets! */
{ AC97_CODEC_ID('C', 'X', 'T', 34),
0xffffffff, "Conexant D480 MDC V.92 Modem", },
{ AC97_CODEC_ID('C', 'X', 'T', 0),
AC97_VENDOR_ID_MASK, "Conexant unknown", },
{ 0,
0, NULL, }
};
@ -770,8 +865,12 @@ ac97_setup_defaults(struct ac97_softc *as)
memset(as->shadow_reg, 0, sizeof(as->shadow_reg));
for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
si = &source_info[idx];
for (idx = 0; idx < AUDIO_SOURCE_INFO_SIZE; idx++) {
si = &audio_source_info[idx];
ac97_write(as, si->reg, si->default_value);
}
for (idx = 0; idx < MODEM_SOURCE_INFO_SIZE; idx++) {
si = &modem_source_info[idx];
ac97_write(as, si->reg, si->default_value);
}
}
@ -809,8 +908,11 @@ ac97_restore_shadow(struct ac97_codec_if *self)
DELAY(10);
}
for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
si = &source_info[idx];
for (idx = 0; idx < SOURCE_INFO_SIZE(as); idx++) {
if (as->type == AC97_TYPE_MODEM)
si = &modem_source_info[idx];
else
si = &audio_source_info[idx];
/* don't "restore" to the reset reg! */
if (si->reg != AC97_REG_RESET)
ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
@ -823,6 +925,13 @@ ac97_restore_shadow(struct ac97_codec_if *self)
ac97_write(as, AC97_REG_EXT_AUDIO_CTRL,
as->shadow_reg[AC97_REG_EXT_AUDIO_CTRL >> 1]);
}
if (as->ext_mid & (AC97_EXT_MODEM_LINE1 | AC97_EXT_MODEM_LINE2
| AC97_EXT_MODEM_HANDSET | AC97_EXT_MODEM_CID1
| AC97_EXT_MODEM_CID2 | AC97_EXT_MODEM_ID0
| AC97_EXT_MODEM_ID1)) {
ac97_write(as, AC97_REG_EXT_MODEM_CTRL,
as->shadow_reg[AC97_REG_EXT_MODEM_CTRL >> 1]);
}
}
static int
@ -853,6 +962,12 @@ ac97_check_capability(struct ac97_softc *as, int check)
return as->caps & AC97_CAPS_LOUDNESS;
case CHECK_3D:
return AC97_CAPS_ENHANCEMENT(as->caps) != 0;
case CHECK_LINE1:
return as->ext_mid & AC97_EXT_MODEM_LINE1;
case CHECK_LINE2:
return as->ext_mid & AC97_EXT_MODEM_LINE2;
case CHECK_HANDSET:
return as->ext_mid & AC97_EXT_MODEM_HANDSET;
default:
printf("%s: internal error: feature=%d\n", __func__, check);
return 0;
@ -865,13 +980,19 @@ ac97_setup_source_info(struct ac97_softc *as)
int idx, ouridx;
struct ac97_source_info *si, *si2;
for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE(as); idx++) {
si = &as->source_info[ouridx];
if (!ac97_check_capability(as, source_info[idx].req_feature))
continue;
memcpy(si, &source_info[idx], sizeof(*si));
if (as->type == AC97_TYPE_MODEM) {
if (!ac97_check_capability(as,
modem_source_info[idx].req_feature))
continue;
memcpy(si, &modem_source_info[idx], sizeof(*si));
} else {
if (!ac97_check_capability(as,
audio_source_info[idx].req_feature))
continue;
memcpy(si, &audio_source_info[idx], sizeof(*si));
}
switch (si->type) {
case AUDIO_MIXER_CLASS:
@ -885,7 +1006,12 @@ ac97_setup_source_info(struct ac97_softc *as)
/* Add an entry for mute, if necessary */
if (si->mute) {
si = &as->source_info[ouridx];
memcpy(si, &source_info[idx], sizeof(*si));
if (as->type == AC97_TYPE_MODEM)
memcpy(si, &modem_source_info[idx],
sizeof(*si));
else
memcpy(si, &audio_source_info[idx],
sizeof(*si));
si->qualifier = AudioNmute;
si->type = AUDIO_MIXER_ENUM;
si->info = &ac97_on_off;
@ -997,13 +1123,11 @@ ac97_attach(struct ac97_host_if *host_if, struct device *sc_dev)
#define AC97_POWER_ALL (AC97_POWER_REF | AC97_POWER_ANL | AC97_POWER_DAC \
| AC97_POWER_ADC)
if (!(as->host_flags & AC97_HOST_SKIP_AUDIO)) {
for (i = 500000; i >= 0; i--) {
ac97_read(as, AC97_REG_POWER, &val);
if ((val & AC97_POWER_ALL) == AC97_POWER_ALL)
break;
DELAY(1);
}
for (i = 500000; i >= 0; i--) {
ac97_read(as, AC97_REG_POWER, &val);
if ((val & AC97_POWER_ALL) == AC97_POWER_ALL)
break;
DELAY(1);
}
#undef AC97_POWER_ALL
@ -1050,6 +1174,8 @@ ac97_attach(struct ac97_host_if *host_if, struct device *sc_dev)
ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
as->ac97_clock = AC97_STANDARD_CLOCK;
as->type = AC97_TYPE_AUDIO; /* default to audio */
if (!(as->host_flags & AC97_HOST_SKIP_AUDIO))
ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
if (as->ext_id != 0) {
@ -1127,6 +1253,8 @@ ac97_attach(struct ac97_host_if *host_if, struct device *sc_dev)
uint16_t val, reg;
int err;
as->type = AC97_TYPE_MODEM;
/* Print capabilities */
bitmask_snprintf(as->ext_mid,
"\20\5CID2\4CID1\3HANDSET\2LINE2\1LINE1",
@ -1224,6 +1352,8 @@ ac97_attach(struct ac97_host_if *host_if, struct device *sc_dev)
}
sysctl_err:
as->source_info = (as->type == AC97_TYPE_MODEM ?
as->modem_source_info : as->audio_source_info);
ac97_setup_source_info(as);
memset(&ctl, 0, sizeof(ctl));
@ -1652,7 +1782,10 @@ ac97_add_port(struct ac97_softc *as, const struct ac97_source_info *src)
struct ac97_source_info *si;
int ouridx, idx;
if (as->num_source_info >= MAX_SOURCES) {
if ((as->type == AC97_TYPE_AUDIO &&
as->num_source_info >= AUDIO_MAX_SOURCES) ||
(as->type == AC97_TYPE_MODEM &&
as->num_source_info >= MODEM_MAX_SOURCES)) {
printf("%s: internal error: increase MAX_SOURCES in %s\n",
__func__, __FILE__);
return -1;
@ -1827,8 +1960,9 @@ ac97_vt1616_init(struct ac97_softc *as)
static int
ac97_modem_offhook_set(struct ac97_softc *as, int line, int newval)
{
uint16_t val = 0;
uint16_t val;
val = as->shadow_reg[AC97_REG_GPIO_STATUS >> 1];
switch (newval) {
case 0:
val &= ~line;