summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pcm/ac97.c
diff options
context:
space:
mode:
authorcg <cg@FreeBSD.org>2000-03-20 15:30:50 +0000
committercg <cg@FreeBSD.org>2000-03-20 15:30:50 +0000
commiteffa7bc907a13c9166491626c1edcaa9d6598a96 (patch)
tree52fe9e3aa64125021a03319292c745ca3651bdbd /sys/dev/sound/pcm/ac97.c
parent2a1e86107da4228384ff5e206f3bd033a6913ee7 (diff)
downloadFreeBSD-src-effa7bc907a13c9166491626c1edcaa9d6598a96.zip
FreeBSD-src-effa7bc907a13c9166491626c1edcaa9d6598a96.tar.gz
update the ac97 layer:
* add a callback for initialising the mixer interface * support ac97 2.1 variable rate audio feature fix ac97-using drivers for the above add suspend/resume support for neomagic
Diffstat (limited to 'sys/dev/sound/pcm/ac97.c')
-rw-r--r--sys/dev/sound/pcm/ac97.c197
1 files changed, 135 insertions, 62 deletions
diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c
index e0ddad7..93d5843 100644
--- a/sys/dev/sound/pcm/ac97.c
+++ b/sys/dev/sound/pcm/ac97.c
@@ -29,30 +29,6 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
-#define AC97_MUTE 0x8000
-
-#define AC97_REG_RESET 0x00
-#define AC97_MIX_MASTER 0x02
-#define AC97_MIX_PHONES 0x04
-#define AC97_MIX_MONO 0x06
-#define AC97_MIX_TONE 0x08
-#define AC97_MIX_BEEP 0x0a
-#define AC97_MIX_PHONE 0x0c
-#define AC97_MIX_MIC 0x0e
-#define AC97_MIX_LINE 0x10
-#define AC97_MIX_CD 0x12
-#define AC97_MIX_VIDEO 0x14
-#define AC97_MIX_AUX 0x16
-#define AC97_MIX_PCM 0x18
-#define AC97_REG_RECSEL 0x1a
-#define AC97_MIX_RGAIN 0x1c
-#define AC97_MIX_MGAIN 0x1e
-#define AC97_REG_GEN 0x20
-#define AC97_REG_3D 0x22
-#define AC97_REG_POWER 0x26
-#define AC97_REG_ID1 0x7c
-#define AC97_REG_ID2 0x7e
-
struct ac97mixtable_entry {
int reg:8;
unsigned bits:4;
@@ -65,12 +41,13 @@ struct ac97mixtable_entry {
struct ac97_info {
device_t dev;
+ ac97_init *init;
ac97_read *read;
ac97_write *write;
void *devinfo;
char id[4];
char rev;
- unsigned caps, se;
+ unsigned caps, se, extcaps, extid, extstat;
struct ac97mixtable_entry mix[32];
};
@@ -162,6 +139,75 @@ static char *ac97feature[] = {
"20 bit ADC"
};
+static char *ac97extfeature[] = {
+ "variable rate PCM",
+ "double rate PCM",
+ "reserved 1",
+ "variable rate mic",
+ "reserved 2",
+ "reserved 3",
+ "center DAC",
+ "surround DAC",
+ "LFE DAC",
+ "AMAP",
+ "reserved 4",
+ "reserved 5",
+ "reserved 6",
+ "reserved 7",
+};
+
+static u_int16_t
+rdcd(struct ac97_info *codec, int reg)
+{
+ return codec->read(codec->devinfo, reg);
+}
+
+static void
+wrcd(struct ac97_info *codec, int reg, u_int16_t val)
+{
+ codec->write(codec->devinfo, reg, val);
+}
+
+int
+ac97_setrate(struct ac97_info *codec, int which, int rate)
+{
+ u_int16_t v;
+
+ switch(which) {
+ case AC97_REGEXT_FDACRATE:
+ case AC97_REGEXT_SDACRATE:
+ case AC97_REGEXT_LDACRATE:
+ case AC97_REGEXT_LADCRATE:
+ case AC97_REGEXT_MADCRATE:
+ break;
+
+ default:
+ return -1;
+ }
+
+ if (rate != 0) {
+ v = rate;
+ if (codec->extstat & AC97_EXTCAP_DRA)
+ v >>= 1;
+ wrcd(codec, which, v);
+ }
+ v = rdcd(codec, which);
+ if (codec->extstat & AC97_EXTCAP_DRA)
+ v <<= 1;
+ return v;
+}
+
+int
+ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
+{
+ mode &= AC97_EXTCAPS;
+ if ((mode & ~codec->extcaps) != 0)
+ return -1;
+ wrcd(codec, AC97_REGEXT_STAT, mode);
+ codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
+ return (mode == codec->extstat)? 0 : -1;
+}
+
static int
ac97_setrecsrc(struct ac97_info *codec, int channel)
{
@@ -169,9 +215,10 @@ ac97_setrecsrc(struct ac97_info *codec, int channel)
if (e->recidx > 0) {
int val = e->recidx - 1;
val |= val << 8;
- codec->write(codec->devinfo, AC97_REG_RECSEL, val);
+ wrcd(codec, AC97_REG_RECSEL, val);
return 0;
- } else return -1;
+ } else
+ return -1;
}
static int
@@ -181,7 +228,8 @@ ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned
if (e->reg != 0) {
int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
- if (!e->stereo) right = left;
+ if (!e->stereo)
+ right = left;
if (e->reg > 0) {
left = 100 - left;
right = 100 - right;
@@ -205,14 +253,16 @@ ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned
val &= max;
val <<= e->ofs;
if (e->mask) {
- int cur = codec->read(codec->devinfo, e->reg);
+ int cur = rdcd(codec, e->reg);
val |= cur & ~(max << e->ofs);
}
}
- if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
- codec->write(codec->devinfo, reg, val);
+ if (left == 0 && right == 0 && e->mute == 1)
+ val = AC97_MUTE;
+ wrcd(codec, reg, val);
return left | (right << 8);
- } else return -1;
+ } else
+ return -1;
}
#if 0
@@ -224,8 +274,9 @@ ac97_getmixer(struct ac97_info *codec, int channel)
int max, val, volume;
max = (1 << e->bits) - 1;
- val = codec->read(codec->devinfo, e->reg);
- if (val == AC97_MUTE && e->mute == 1) volume = 0;
+ val = rdcd(code, e->reg);
+ if (val == AC97_MUTE && e->mute == 1)
+ volume = 0;
else {
if (e->stereo == 0) val >>= e->ofs;
val &= max;
@@ -233,65 +284,83 @@ ac97_getmixer(struct ac97_info *codec, int channel)
if (e->reg > 0) volume = 100 - volume;
}
return volume;
- } else return -1;
+ } else
+ return -1;
}
#endif
static unsigned
-ac97_init(struct ac97_info *codec)
+ac97_initmixer(struct ac97_info *codec)
{
unsigned i, j;
u_int32_t id;
- for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
+ for (i = 0; i < 32; i++)
+ codec->mix[i] = ac97mixtable_default[i];
- codec->write(codec->devinfo, AC97_REG_POWER, 0);
- codec->write(codec->devinfo, AC97_REG_RESET, 0);
+ if (codec->init)
+ codec->init(codec->devinfo);
+ wrcd(codec, AC97_REG_POWER, 0);
+ wrcd(codec, AC97_REG_RESET, 0);
DELAY(10000);
- i = codec->read(codec->devinfo, AC97_REG_RESET);
+ i = rdcd(codec, AC97_REG_RESET);
codec->caps = i & 0x03ff;
codec->se = (i & 0x7c00) >> 10;
- id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
- codec->read(codec->devinfo, AC97_REG_ID2);
+ i = rdcd(codec, AC97_REGEXT_ID);
+ codec->extcaps = i & 0x3fff;
+ codec->extid = (i & 0xc000) >> 14;
+
+ codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
+
+ id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2);
codec->rev = id & 0x000000ff;
- codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
- if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
+ wrcd(codec, AC97_MIX_MASTER, 0x20);
+ if ((rdcd(codec, AC97_MIX_MASTER) & 0x20) == 0x20)
codec->mix[SOUND_MIXER_VOLUME].bits++;
- codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
+ wrcd(codec, AC97_MIX_MASTER, 0x00);
if (bootverbose) {
device_printf(codec->dev, "ac97 codec id 0x%8x", id);
- for (i = 0; ac97codecid[i].id; i++) {
- if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
- }
+ for (i = 0; ac97codecid[i].id; i++)
+ if (ac97codecid[i].id == id)
+ printf(" (%s)", ac97codecid[i].name);
printf("\n");
device_printf(codec->dev, "ac97 codec features ");
- for (i = j = 0; i < 10; i++) {
- if (codec->caps & (1 << i)) {
- printf("%s%s", j? ", " : "", ac97feature[i]);
- j++;
- }
- }
- printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
+ for (i = j = 0; i < 10; i++)
+ if (codec->caps & (1 << i))
+ printf("%s%s", j++? ", " : "", ac97feature[i]);
+ printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
+
+ if (codec->extcaps != 0 || codec->extid) {
+ device_printf(codec->dev, "ac97 %s codec",
+ codec->extid? "secondary" : "primary");
+ if (codec->extcaps)
+ printf(" extended features ");
+ for (i = j = 0; i < 14; i++)
+ if (codec->extcaps & (1 << i))
+ printf("%s%s", j++? ", " : "", ac97extfeature[i]);
+ printf("\n");
+ }
}
- if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
+ if ((rdcd(codec, AC97_REG_POWER) & 2) == 0)
device_printf(codec->dev, "ac97 codec reports dac not ready\n");
return 0;
}
struct ac97_info *
-ac97_create(device_t dev, void *devinfo, ac97_read *rd, ac97_write *wr)
+ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr)
{
struct ac97_info *codec;
codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
if (codec != NULL) {
codec->dev = dev;
+ codec->init = init;
codec->read = rd;
codec->write = wr;
codec->devinfo = devinfo;
@@ -303,8 +372,9 @@ static int
ac97mix_init(snd_mixer *m)
{
struct ac97_info *codec = mix_getdevinfo(m);
- if (codec == NULL) return -1;
- ac97_init(codec);
+ if (codec == NULL)
+ return -1;
+ ac97_initmixer(codec);
mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
mix_setrecdevs(m, ac97recdevs);
return 0;
@@ -314,7 +384,8 @@ static int
ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
struct ac97_info *codec = mix_getdevinfo(m);
- if (codec == NULL) return -1;
+ if (codec == NULL)
+ return -1;
return ac97_setmixer(codec, dev, left, right);
}
@@ -323,9 +394,11 @@ ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
{
int i;
struct ac97_info *codec = mix_getdevinfo(m);
- if (codec == NULL) return -1;
+ if (codec == NULL)
+ return -1;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if ((src & (1 << i)) != 0) break;
+ if ((src & (1 << i)) != 0)
+ break;
return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
}
OpenPOWER on IntegriCloud