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:
parent
c7acb3a606
commit
0f32e1f4a6
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user