plan9fox/sys/src/9/pc/audioac97mix.c
cinap_lenrek 81f726b2b4 audioac97: remove i/o bar magic, fix ac97mixreset busywait-forever timeout
the standard is i/o bar 0 is the mixer and bar 1 is status/control.
the magic with the bar sizes made it fail in qemu. so removing it
for now as all devices seen so far comply to the standard.

if we ever see a sis7012 where this might be swaped uncomment the i=0;

the busywait timeout is too long in ac97mixreset() because rd/wr
have a timeout on ther own. just remove the busy looping and do
a one second delay after mixer reset. (tested with t23)
2013-09-26 23:34:06 +02:00

274 lines
5.7 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/audioif.h"
enum {
Reset = 0x0,
Capmic = 0x1,
Captonectl = 0x4,
Capsimstereo = 0x8,
Capheadphones = 0x10,
Caploudness = 0x20,
Capdac18 = 0x40,
Capdac20 = 0x80,
Capadc18 = 0x100,
Capadc20 = 0x200,
Capenh = 0xfc00,
Recsel = 0x1A,
General = 0x20,
ThreeDctl = 0x22,
Ac97RESER = 0x24,
Powerdowncsr = 0x26,
Adcpower = 0x1,
Dacpower = 0x2,
Anlpower = 0x4,
Refpower = 0x8,
Inpower = 0x100,
Outpower = 0x200,
Mixpower = 0x400,
Mixvrefpower = 0x800,
Aclinkpower = 0x1000,
Clkpower = 0x2000,
Auxpower = 0x4000,
Eamppower = 0x8000,
Extid = 0x28,
Extcsr = 0x2A,
Extvra = 1<<0,
Extdra = 1<<1,
Extspdif = 1<<2,
Extvrm = 1<<3,
Extiddsa0 = 0<<4, /* extid only */
Extiddsa1 = 1<<4, /* extid only */
Extiddsa2 = 2<<4, /* extid only */
Extiddsa3 = 3<<4, /* extid only */
Extcsrspsa34 = 0<<4, /* extcsr only */
Extcsrspsa78 = 1<<4, /* extcsr only */
Extcsrspsa69 = 2<<4, /* extcsr only */
ExtcsrspsaAB = 3<<4, /* extcsr only */
Extcdac = 1<<6,
Extsdac = 1<<7,
Extldac = 1<<8,
Extidamap = 1<<9, /* extid only */
Extidrev11 = 0<<10, /* extid only */
Extidrev22 = 1<<10, /* extid only */
Extidrev23 = 2<<10, /* extid only */
Extidprim = 0<<14, /* extid only */
Extidsec0 = 1<<14, /* extid only */
Extidsec1 = 2<<14, /* extid only */
Extidsec2 = 3<<14, /* extid only */
Extcsrmadc = 1<<9, /* extcsr only */
Extcsrspcv = 1<<10, /* extcsr only */
Extcsrpri = 1<<11, /* extcsr only */
Extcsrprj = 1<<12, /* extcsr only */
Extcsrprk = 1<<13, /* extcsr only */
Extcsrprl = 1<<14, /* extcsr only */
Extcsrvcfg = 1<<15, /* extcsr only */
Pcmfrontdacrate = 0x2C,
Pcmsurrounddacrate = 0x2E,
Pcmlfedacrate = 0x30,
Pcmadcrate = 0x32,
Pcmmicadcrate = 0x34,
CenterLfe = 0x36,
LrSurround = 0x38,
Spdifcsr = 0x3a,
Spdifpro = 1<<0,
Spdifnonaudio = 1<<1,
Spdifcopy = 1<<2,
Spdifpre = 1<<3,
SpdifCC = 0x7f<<4,
Spdifl = 1<<11,
Spdif44k1 = 0<<12,
Spdif32k = 1<<12,
Spdif48k = 2<<12,
Spdifdsr = 1<<14,
Spdifv = 1<<15,
VID1 = 0x7c,
VID2 = 0x7e,
};
enum {
Vmaster,
Vhead,
Vaudio,
Vcd,
Vbass,
Vtreb,
Vbeep,
Vphone,
Vmic,
Vline,
Vvideo,
Vaux,
Vrecgain,
Vmicgain,
Vspeed,
Vdelay,
};
static Volume voltab[] = {
[Vmaster] "master", 0x02, -63, Stereo, 0,
[Vaudio] "audio", 0x18, -31, Stereo, 0,
[Vhead] "head", 0x04, -31, Stereo, Capheadphones,
[Vbass] "bass", 0x08, 15, Left, Captonectl,
[Vtreb] "treb", 0x08, 15, Right, Captonectl,
[Vbeep] "beep", 0x0a, -31, Right, 0,
[Vphone] "phone", 0x0c, -31, Right, 0,
[Vmic] "mic", 0x0e, -31, Right, Capmic,
[Vline] "line", 0x10, -31, Stereo, 0,
[Vcd] "cd", 0x12, -31, Stereo, 0,
[Vvideo] "video", 0x14, -31, Stereo, 0,
[Vaux] "aux", 0x16, -63, Stereo, 0,
[Vrecgain] "recgain", 0x1c, 15, Stereo, 0,
[Vmicgain] "micgain", 0x1e, 15, Right, Capmic,
[Vspeed] "speed", 0x2c, 0, Absolute, 0,
[Vdelay] "delay", 0, 0, Absolute, 0,
0
};
typedef struct Mixer Mixer;
struct Mixer
{
ushort (*rr)(Audio *, int);
void (*wr)(Audio *, int, ushort);
int vra;
};
static int
ac97volget(Audio *adev, int x, int a[2])
{
Mixer *m = adev->mixer;
Volume *vol;
ushort v;
vol = voltab+x;
switch(vol->type){
case Absolute:
if(x == Vdelay){
a[0] = adev->delay;
break;
}
a[0] = m->rr(adev, vol->reg);
break;
default:
v = m->rr(adev, vol->reg);
if(v & 0x8000){
a[0] = a[1] = vol->range < 0 ? 0x7f : 0;
} else {
a[0] = ((v>>8) & 0x7f);
a[1] = (v & 0x7f);
}
}
return 0;
}
static int
ac97volset(Audio *adev, int x, int a[2])
{
Mixer *m = adev->mixer;
Volume *vol;
ushort v, w;
vol = voltab+x;
switch(vol->type){
case Absolute:
if(x == Vdelay){
adev->delay = a[0];
return 0;
}
m->wr(adev, vol->reg, a[0]);
if(x == Vspeed){
m->wr(adev, 0x32, a[0]); /* adc rate */
adev->speed = m->rr(adev, vol->reg);
}
break;
case Left:
v = a[0] & 0x7f;
w = m->rr(adev, vol->reg) & 0x7f;
m->wr(adev, vol->reg, (v<<8)|w);
break;
case Right:
v = m->rr(adev, vol->reg) & 0x7f00;
w = a[1] & 0x7f;
m->wr(adev, vol->reg, v|w);
break;
case Stereo:
v = a[0] & 0x7f;
w = a[1] & 0x7f;
m->wr(adev, vol->reg, (v<<8)|w);
break;
}
return 0;
}
static long
ac97mixread(Audio *adev, void *a, long n, vlong)
{
Mixer *m = adev->mixer;
ulong caps;
caps = m->rr(adev, Reset);
caps |= m->rr(adev, Extid) << 16;
return genaudiovolread(adev, a, n, 0, voltab, ac97volget, caps);
}
static long
ac97mixwrite(Audio *adev, void *a, long n, vlong)
{
Mixer *m = adev->mixer;
ulong caps;
caps = m->rr(adev, Reset);
caps |= m->rr(adev, Extid) << 16;
return genaudiovolwrite(adev, a, n, 0, voltab, ac97volset, caps);
}
void
ac97mixreset(Audio *adev, void (*wr)(Audio*,int,ushort), ushort (*rr)(Audio*,int))
{
Mixer *m;
ushort t;
m = malloc(sizeof(Mixer));
if(m == nil){
print("ac97mix: no memory for Mixer\n");
return;
}
m->wr = wr;
m->rr = rr;
m->wr(adev, Reset, 0);
m->wr(adev, Powerdowncsr, 0);
delay(1000);
t = (Adcpower | Dacpower | Anlpower | Refpower);
if((m->rr(adev, Powerdowncsr) & t) != t)
print("#A%d: ac97 exhausted waiting powerup\n", adev->ctlrno);
t = m->rr(adev, Extid);
print("#A%d: ac97 codec ext:%s%s%s%s%s%s%s\n", adev->ctlrno,
(t & Extvra) ? " vra" : "",
(t & Extdra) ? " dra" : "",
(t & Extspdif) ? " spdif" : "",
(t & Extvrm) ? " vrm" : "",
(t & Extcdac) ? " cdac" : "",
(t & Extsdac) ? " sdac" : "",
(t & Extldac) ? " ldac" : "");
if(t & Extvra){
m->wr(adev, Extcsr, Extvra);
m->vra = 1;
} else {
print("#A%d: ac97 vra extension not supported\n", adev->ctlrno);
m->vra = 0;
}
adev->mixer = m;
adev->volread = ac97mixread;
adev->volwrite = ac97mixwrite;
}