]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/audioac97mix.c
merge
[plan9front.git] / sys / src / 9 / pc / audioac97mix.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 #include "../port/audioif.h"
9
10 enum { Maxbusywait = 500000 };
11
12 enum {
13         Reset = 0x0,
14                 Capmic = 0x1,
15                 Captonectl = 0x4,
16                 Capsimstereo = 0x8,
17                 Capheadphones = 0x10,
18                 Caploudness = 0x20,
19                 Capdac18 = 0x40,
20                 Capdac20 = 0x80,
21                 Capadc18 = 0x100,
22                 Capadc20 = 0x200,
23                 Capenh = 0xfc00,
24
25         Recsel = 0x1A,
26         General = 0x20,
27         ThreeDctl = 0x22,
28         Ac97RESER = 0x24,
29         Powerdowncsr = 0x26,
30                 Adcpower = 0x1,
31                 Dacpower = 0x2,
32                 Anlpower = 0x4,
33                 Refpower = 0x8,
34                 Inpower = 0x100,
35                 Outpower = 0x200,
36                 Mixpower = 0x400,
37                 Mixvrefpower = 0x800,
38                 Aclinkpower = 0x1000,
39                 Clkpower = 0x2000,
40                 Auxpower = 0x4000,
41                 Eamppower = 0x8000,
42         Extid = 0x28,
43         Extcsr = 0x2A,
44                 Extvra = 1<<0,
45                 Extdra = 1<<1,
46                 Extspdif = 1<<2,
47                 Extvrm = 1<<3,
48                 Extiddsa0 = 0<<4,       /* extid only */
49                 Extiddsa1 = 1<<4,       /* extid only */
50                 Extiddsa2 = 2<<4,       /* extid only */
51                 Extiddsa3 = 3<<4,       /* extid only */
52                 Extcsrspsa34 = 0<<4,    /* extcsr only */
53                 Extcsrspsa78 = 1<<4,    /* extcsr only */
54                 Extcsrspsa69 = 2<<4,    /* extcsr only */
55                 ExtcsrspsaAB = 3<<4,    /* extcsr only */
56                 Extcdac = 1<<6,
57                 Extsdac = 1<<7,
58                 Extldac = 1<<8,
59                 Extidamap = 1<<9,       /* extid only */
60                 Extidrev11 = 0<<10,     /* extid only */
61                 Extidrev22 = 1<<10,     /* extid only */
62                 Extidrev23 = 2<<10,     /* extid only */
63                 Extidprim = 0<<14,      /* extid only */
64                 Extidsec0 = 1<<14,      /* extid only */
65                 Extidsec1 = 2<<14,      /* extid only */
66                 Extidsec2 = 3<<14,      /* extid only */
67                 Extcsrmadc = 1<<9,      /* extcsr only */
68                 Extcsrspcv = 1<<10,     /* extcsr only */
69                 Extcsrpri = 1<<11,      /* extcsr only */
70                 Extcsrprj = 1<<12,      /* extcsr only */
71                 Extcsrprk = 1<<13,      /* extcsr only */
72                 Extcsrprl = 1<<14,      /* extcsr only */
73                 Extcsrvcfg = 1<<15,     /* extcsr only */
74         Pcmfrontdacrate = 0x2C,
75         Pcmsurrounddacrate = 0x2E,
76         Pcmlfedacrate = 0x30,
77         Pcmadcrate = 0x32,
78         Pcmmicadcrate = 0x34,
79         CenterLfe = 0x36,
80         LrSurround = 0x38,
81         Spdifcsr = 0x3a,
82                 Spdifpro = 1<<0,
83                 Spdifnonaudio = 1<<1,
84                 Spdifcopy = 1<<2,
85                 Spdifpre = 1<<3,
86                 SpdifCC = 0x7f<<4,
87                 Spdifl = 1<<11,
88                 Spdif44k1 = 0<<12,
89                 Spdif32k = 1<<12,
90                 Spdif48k = 2<<12,
91                 Spdifdsr = 1<<14,
92                 Spdifv = 1<<15,
93         VID1 = 0x7c,
94         VID2 = 0x7e,
95 };
96
97 enum {
98         Vmaster,
99         Vhead,
100         Vaudio,
101         Vcd,
102         Vbass,
103         Vtreb,
104         Vbeep,
105         Vphone,
106         Vmic,
107         Vline,
108         Vvideo,
109         Vaux,
110         Vrecgain,
111         Vmicgain,
112         Vspeed,
113         Vdelay,
114 };
115
116 static Volume voltab[] = {
117         [Vmaster] "master", 0x02, -63, Stereo, 0,
118         [Vaudio] "audio", 0x18, -31, Stereo, 0,
119         [Vhead] "head", 0x04, -31, Stereo, Capheadphones,
120         [Vbass] "bass", 0x08, 15, Left, Captonectl,
121         [Vtreb] "treb", 0x08, 15, Right, Captonectl,
122         [Vbeep] "beep", 0x0a, -31, Right, 0,
123         [Vphone] "phone", 0x0c, -31, Right, 0,
124         [Vmic] "mic", 0x0e, -31, Right, Capmic,
125         [Vline] "line", 0x10, -31, Stereo, 0,
126         [Vcd] "cd", 0x12, -31, Stereo,  0,
127         [Vvideo] "video", 0x14, -31, Stereo, 0,
128         [Vaux] "aux", 0x16, -63, Stereo, 0,
129         [Vrecgain] "recgain", 0x1c, 15, Stereo, 0,
130         [Vmicgain] "micgain", 0x1e, 15, Right, Capmic,
131         [Vspeed] "speed", 0x2c, 0, Absolute, 0,
132         [Vdelay] "delay", 0, 0, Absolute, 0,
133         0
134 };
135
136 typedef struct Mixer Mixer;
137 struct Mixer
138 {
139         ushort (*rr)(Audio *, int);
140         void (*wr)(Audio *, int, ushort);
141         int vra;
142 };
143
144 static int
145 ac97volget(Audio *adev, int x, int a[2])
146 {
147         Mixer *m = adev->mixer;
148         Volume *vol;
149         ushort v;
150
151         vol = voltab+x;
152         switch(vol->type){
153         case Absolute:
154                 if(x == Vdelay){
155                         a[0] = adev->delay;
156                         break;
157                 }
158                 a[0] = m->rr(adev, vol->reg);
159                 break;
160         default:
161                 v = m->rr(adev, vol->reg);
162                 if(v & 0x8000){
163                         a[0] = a[1] = vol->range < 0 ? 0x7f : 0;
164                 } else {
165                         a[0] = ((v>>8) & 0x7f);
166                         a[1] = (v & 0x7f);
167                 }
168         }
169         return 0;
170 }
171
172 static int
173 ac97volset(Audio *adev, int x, int a[2])
174 {
175         Mixer *m = adev->mixer;
176         Volume *vol;
177         ushort v, w;
178
179         vol = voltab+x;
180         switch(vol->type){
181         case Absolute:
182                 if(x == Vdelay){
183                         adev->delay = a[0];
184                         return 0;
185                 }
186                 m->wr(adev, vol->reg, a[0]);            
187                 if(x == Vspeed){
188                         m->wr(adev, 0x32, a[0]);        /* adc rate */
189                         adev->speed = m->rr(adev, vol->reg);
190                 }
191                 break;
192         case Left:
193                 v = a[0] & 0x7f;
194                 w = m->rr(adev, vol->reg) & 0x7f;
195                 m->wr(adev, vol->reg, (v<<8)|w);
196                 break;
197         case Right:
198                 v = m->rr(adev, vol->reg) & 0x7f00;
199                 w = a[1] & 0x7f;
200                 m->wr(adev, vol->reg, v|w);
201                 break;
202         case Stereo:
203                 v = a[0] & 0x7f;
204                 w = a[1] & 0x7f;
205                 m->wr(adev, vol->reg, (v<<8)|w);
206                 break;
207         }
208         return 0;
209 }
210
211
212 static long
213 ac97mixread(Audio *adev, void *a, long n, vlong)
214 {
215         Mixer *m = adev->mixer;
216         ulong caps;
217
218         caps = m->rr(adev, Reset);
219         caps |= m->rr(adev, Extid) << 16;
220         return genaudiovolread(adev, a, n, 0, voltab, ac97volget, caps);
221 }
222
223 static long
224 ac97mixwrite(Audio *adev, void *a, long n, vlong)
225 {
226         Mixer *m = adev->mixer;
227         ulong caps;
228
229         caps = m->rr(adev, Reset);
230         caps |= m->rr(adev, Extid) << 16;
231         return genaudiovolwrite(adev, a, n, 0, voltab, ac97volset, caps);
232 }
233
234 void
235 ac97mixreset(Audio *adev, void (*wr)(Audio*,int,ushort), ushort (*rr)(Audio*,int))
236 {
237         Mixer *m;
238         ushort t;
239         int i;
240
241         m = malloc(sizeof(Mixer));
242         if(m == nil){
243                 print("ac97mix: no memory for Mixer\n");
244                 return;
245         }
246         m->wr = wr;
247         m->rr = rr;
248         m->wr(adev, Reset, 0);
249         m->wr(adev, Powerdowncsr, 0);
250
251         t = (Adcpower | Dacpower | Anlpower | Refpower);
252         for(i = 0; i < Maxbusywait; i++){
253                 if((m->rr(adev, Powerdowncsr) & t) == t)
254                         break;
255                 microdelay(1);
256         }
257         if(i == Maxbusywait)
258                 print("#A%d: ac97 exhausted waiting powerup\n", adev->ctlrno);
259
260         t = m->rr(adev, Extid);
261         print("#A%d: ac97 codec ext:%s%s%s%s%s%s%s\n", adev->ctlrno,
262                 (t & Extvra) ? " vra" : "",
263                 (t & Extdra) ? " dra" : "",
264                 (t & Extspdif) ? " spdif" : "",
265                 (t & Extvrm) ? " vrm" : "",
266                 (t & Extcdac) ? " cdac" : "",
267                 (t & Extsdac) ? " sdac" : "",
268                 (t & Extldac) ? " ldac" : "");
269
270         if(t & Extvra){
271                 m->wr(adev, Extcsr, Extvra);
272                 m->vra = 1;
273         } else {
274                 print("#A%d: ac97 vra extension not supported\n", adev->ctlrno);
275                 m->vra = 0;
276         }
277
278         adev->mixer = m;
279         adev->volread = ac97mixread;
280         adev->volwrite = ac97mixwrite;
281 }