]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/audioac97mix.c
audio: add delay control
[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] = 0;
164                         a[1] = 0;
165                 } else {
166                         a[0] = vol->range - ((v>>8) & 0x7f);
167                         a[1] = vol->range - (v & 0x7f);
168                 }
169         }
170         return 0;
171 }
172
173 static int
174 ac97volset(Audio *adev, int x, int a[2])
175 {
176         Mixer *m = adev->mixer;
177         Volume *vol;
178         ushort v, w;
179
180         vol = voltab+x;
181         switch(vol->type){
182         case Absolute:
183                 if(x == Vdelay){
184                         adev->delay = a[0];
185                         return 0;
186                 }
187                 m->wr(adev, vol->reg, a[0]);            
188                 if(x == Vspeed)
189                         adev->speed = m->rr(adev, vol->reg);
190                 break;
191         case Left:
192                 v = (vol->range - a[0]) & 0x7f;
193                 w = m->rr(adev, vol->reg) & 0x7f;
194                 m->wr(adev, vol->reg, (v<<8)|w);
195                 break;
196         case Right:
197                 v = m->rr(adev, vol->reg) & 0x7f00;
198                 w = (vol->range - a[1]) & 0x7f;
199                 m->wr(adev, vol->reg, v|w);
200                 break;
201         case Stereo:
202                 v = (vol->range - a[0]) & 0x7f;
203                 w = (vol->range - a[1]) & 0x7f;
204                 m->wr(adev, vol->reg, (v<<8)|w);
205                 break;
206         }
207         return 0;
208 }
209
210
211 static long
212 ac97mixread(Audio *adev, void *a, long n, vlong)
213 {
214         Mixer *m = adev->mixer;
215         ulong caps;
216
217         caps = m->rr(adev, Reset);
218         caps |= m->rr(adev, Extid) << 16;
219         return genaudiovolread(adev, a, n, 0, voltab, ac97volget, caps);
220 }
221
222 static long
223 ac97mixwrite(Audio *adev, void *a, long n, vlong)
224 {
225         Mixer *m = adev->mixer;
226         ulong caps;
227
228         caps = m->rr(adev, Reset);
229         caps |= m->rr(adev, Extid) << 16;
230         return genaudiovolwrite(adev, a, n, 0, voltab, ac97volset, caps);
231 }
232
233 void
234 ac97mixreset(Audio *adev, void (*wr)(Audio*,int,ushort), ushort (*rr)(Audio*,int))
235 {
236         Mixer *m;
237         ushort t;
238         int i;
239
240         m = malloc(sizeof(Mixer));
241         m->wr = wr;
242         m->rr = rr;
243         m->wr(adev, Reset, 0);
244         m->wr(adev, Powerdowncsr, 0);
245
246         t = (Adcpower | Dacpower | Anlpower | Refpower);
247         for(i = 0; i < Maxbusywait; i++){
248                 if((m->rr(adev, Powerdowncsr) & t) == t)
249                         break;
250                 microdelay(1);
251         }
252         if(i == Maxbusywait)
253                 print("#A%d: ac97 exhausted waiting powerup\n", adev->ctlrno);
254
255         t = m->rr(adev, Extid);
256         print("#A%d: ac97 codec ext:%s%s%s%s%s%s%s\n", adev->ctlrno,
257                 (t & Extvra) ? " vra" : "",
258                 (t & Extdra) ? " dra" : "",
259                 (t & Extspdif) ? " spdif" : "",
260                 (t & Extvrm) ? " vrm" : "",
261                 (t & Extcdac) ? " cdac" : "",
262                 (t & Extsdac) ? " sdac" : "",
263                 (t & Extldac) ? " ldac" : "");
264
265         if(t & Extvra){
266                 m->wr(adev, Extcsr, Extvra);
267                 m->vra = 1;
268         } else {
269                 print("#A%d: ac97 vra extension not supported\n", adev->ctlrno);
270                 m->vra = 0;
271         }
272
273         adev->mixer = m;
274         adev->volread = ac97mixread;
275         adev->volwrite = ac97mixwrite;
276 }