]> 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/audio.h"
9
10 typedef ushort (*ac97rdfn)(Audio *, int);
11 typedef void (*ac97wrfn)(Audio *, int, ushort);
12
13 typedef struct Mixer Mixer;
14 typedef struct Volume Volume;
15
16 struct Mixer {
17         QLock;
18         ac97wrfn wr;
19         ac97rdfn rr;
20         int vra;
21 };
22
23 enum { Maxbusywait = 500000 };
24
25 enum {
26         Reset = 0x0,
27                 Capmic = 0x1,
28                 Captonectl = 0x4,
29                 Capsimstereo = 0x8,
30                 Capheadphones = 0x10,
31                 Caploudness = 0x20,
32                 Capdac18 = 0x40,
33                 Capdac20 = 0x80,
34                 Capadc18 = 0x100,
35                 Capadc20 = 0x200,
36                 Capenh = 0xfc00,
37         Master = 0x02,
38         Headphone = 0x04,
39         Monomaster = 0x06,
40         Mastertone = 0x08,
41         Pcbeep = 0x0A,
42         Phone = 0x0C,
43         Mic = 0x0E,
44         Line = 0x10,
45         Cd = 0x12,
46         Video = 0x14,
47         Aux = 0x16,
48         Pcmout = 0x18,
49                 Mute = 0x8000,
50         Recsel = 0x1A,
51         Recgain = 0x1C,
52         Micgain = 0x1E,
53         General = 0x20,
54         ThreeDctl = 0x22,
55         Ac97RESER = 0x24,
56         Powerdowncsr = 0x26,
57                 Adcpower = 0x1,
58                 Dacpower = 0x2,
59                 Anlpower = 0x4,
60                 Refpower = 0x8,
61                 Inpower = 0x100,
62                 Outpower = 0x200,
63                 Mixpower = 0x400,
64                 Mixvrefpower = 0x800,
65                 Aclinkpower = 0x1000,
66                 Clkpower = 0x2000,
67                 Auxpower = 0x4000,
68                 Eamppower = 0x8000,
69         Extid = 0x28,
70         Extcsr = 0x2A,
71                 Extvra = 1<<0,
72                 Extdra = 1<<1,
73                 Extspdif = 1<<2,
74                 Extvrm = 1<<3,
75                 Extiddsa0 = 0<<4,       /* extid only */
76                 Extiddsa1 = 1<<4,       /* extid only */
77                 Extiddsa2 = 2<<4,       /* extid only */
78                 Extiddsa3 = 3<<4,       /* extid only */
79                 Extcsrspsa34 = 0<<4,    /* extcsr only */
80                 Extcsrspsa78 = 1<<4,    /* extcsr only */
81                 Extcsrspsa69 = 2<<4,    /* extcsr only */
82                 ExtcsrspsaAB = 3<<4,    /* extcsr only */
83                 Extcdac = 1<<6,
84                 Extsdac = 1<<7,
85                 Extldac = 1<<8,
86                 Extidamap = 1<<9,       /* extid only */
87                 Extidrev11 = 0<<10,     /* extid only */
88                 Extidrev22 = 1<<10,     /* extid only */
89                 Extidrev23 = 2<<10,     /* extid only */
90                 Extidprim = 0<<14,      /* extid only */
91                 Extidsec0 = 1<<14,      /* extid only */
92                 Extidsec1 = 2<<14,      /* extid only */
93                 Extidsec2 = 3<<14,      /* extid only */
94                 Extcsrmadc = 1<<9,      /* extcsr only */
95                 Extcsrspcv = 1<<10,     /* extcsr only */
96                 Extcsrpri = 1<<11,      /* extcsr only */
97                 Extcsrprj = 1<<12,      /* extcsr only */
98                 Extcsrprk = 1<<13,      /* extcsr only */
99                 Extcsrprl = 1<<14,      /* extcsr only */
100                 Extcsrvcfg = 1<<15,     /* extcsr only */
101         Pcmfrontdacrate = 0x2C,
102         Pcmsurrounddacrate = 0x2E,
103         Pcmlfedacrate = 0x30,
104         Pcmadcrate = 0x32,
105         Pcmmicadcrate = 0x34,
106         CenterLfe = 0x36,
107         LrSurround = 0x38,
108         Spdifcsr = 0x3a,
109                 Spdifpro = 1<<0,
110                 Spdifnonaudio = 1<<1,
111                 Spdifcopy = 1<<2,
112                 Spdifpre = 1<<3,
113                 SpdifCC = 0x7f<<4,
114                 Spdifl = 1<<11,
115                 Spdif44k1 = 0<<12,
116                 Spdif32k = 1<<12,
117                 Spdif48k = 2<<12,
118                 Spdifdsr = 1<<14,
119                 Spdifv = 1<<15,
120         VID1 = 0x7c,
121         VID2 = 0x7e,
122         Speed = 0x1234567,
123 };
124
125 enum {
126         Left,
127         Right,
128         Stereo,
129         Absolute,
130 };
131
132 enum {
133         Vmaster,
134         Vhead,
135         Vaudio,
136         Vcd,
137         Vbass,
138         Vtreb,
139         Vbeep,
140         Vphone,
141         Vmic,
142         Vline,
143         Vvideo,
144         Vaux,
145         Vrecgain,
146         Vmicgain,
147 };
148
149 struct Volume {
150         int reg;
151         int range;
152         int type;
153         int cap;
154         char *name;
155 };
156
157 struct Topology {
158         Volume *this;
159         Volume *next[2];
160 };
161
162 Volume vol[] = {
163 [Vmaster]       {Master, 63, Stereo, 0, "master"},
164 [Vaudio]        {Pcmout, 31, Stereo,    0, "audio"},
165 [Vhead] {Headphone, 31, Stereo, Capheadphones, "head"},
166 [Vbass] {Mastertone, 15, Left, Captonectl, "bass"},
167 [Vtreb] {Mastertone, 15, Right, Captonectl, "treb"},
168 [Vbeep] {Pcbeep, 31, Right, 0, "beep"},
169 [Vphone]        {Phone, 31, Right, 0, "phone"},
170 [Vmic]  {Mic, 31, Right, Capmic, "mic"},
171 [Vline] {Line, 31, Stereo, 0, "line"},
172 [Vcd]   {Cd, 31, Stereo,        0, "cd"},
173 [Vvideo]        {Video, 31, Stereo, 0, "video"},
174 [Vaux]  {Aux, 63, Stereo, 0, "aux"},
175 [Vrecgain]      {Recgain, 15, Stereo, 0, "recgain"},
176 [Vmicgain]      {Micgain, 15, Right, Capmic, "micgain"},
177         {0, 0, 0, 0, 0},
178 };
179
180 long
181 ac97mixtopology(Audio *adev, void *a, long n, vlong off)
182 {
183         Mixer *m;
184         char *buf;
185         long l;
186         ulong caps;
187         m = adev->mixer;
188         qlock(m);
189         caps = m->rr(adev, Reset);
190         caps |= m->rr(adev, Extid) << 16;
191         l = 0;
192         buf = malloc(READSTR);
193         l += snprint(buf+l, READSTR-l, "not implemented. have fun.\n");
194         USED(caps);
195         USED(l);
196         qunlock(m);
197         n = readstr(off, a, n, buf);
198         free(buf);
199         return n;
200 }
201
202 long
203 ac97mixread(Audio *adev, void *a, long n, vlong off)
204 {
205         Mixer *m;
206         char *nam, *buf;
207         long l;
208         ushort v;
209         ulong caps;
210         int i, rang, le, ri;
211         buf = malloc(READSTR);
212         m = adev->mixer;
213         qlock(m);
214         l = 0;
215         caps = m->rr(adev, Reset);
216         caps |= m->rr(adev, Extid) << 16;
217         for(i = 0; vol[i].name != 0; ++i){
218                 if(vol[i].cap && ((vol[i].cap & caps) == 0))
219                         continue;
220                 v = m->rr(adev, vol[i].reg);
221                 nam = vol[i].name;
222                 rang = vol[i].range;
223                 if(vol[i].type == Absolute){
224                         l += snprint(buf+l, READSTR-l, "%s %d", nam, v);
225                 } else {
226                         ri = ((rang-(v&rang)) * 100) / rang;
227                         le = ((rang-((v>>8)&rang)) * 100) / rang;
228                         if(vol[i].type == Stereo)
229                                 l += snprint(buf+l, READSTR-l, "%s %d %d", nam, le, ri);
230                         if(vol[i].type == Left)
231                                 l += snprint(buf+l, READSTR-l, "%s %d", nam, le);
232                         if(vol[i].type == Right)
233                                 l += snprint(buf+l, READSTR-l, "%s %d", nam, ri);
234                         if(v&Mute)
235                                 l += snprint(buf+l, READSTR-l, " mute");
236                 }
237                 l += snprint(buf+l, READSTR-l, "\n");
238         }
239         qunlock(m);
240         n = readstr(off, a, n, buf);
241         free(buf);
242         return n;
243 }
244
245 long
246 ac97mixwrite(Audio *adev, void *a, long n, vlong)
247 {
248         Mixer *m;
249         char *tok[4];
250         int ntok, i, left, right, rang, reg;
251         ushort v;
252         m = adev->mixer;
253         qlock(m);
254         ntok = tokenize(a, tok, 4);
255         for(i = 0; vol[i].name != 0; ++i){
256                 if(!strcmp(vol[i].name, tok[0])){
257                         rang = vol[i].range;
258                         reg = vol[i].reg;
259                         left = right = 0;
260                         if(ntok > 1)
261                                 left = right = atoi(tok[1]);
262                         if(ntok > 2)
263                                 right = atoi(tok[2]);
264
265                         if(vol[i].type == Absolute){
266                                 m->wr(adev, reg, left);
267                         } else {
268                                 left = rang - ((left*rang)) / 100;
269                                 right = rang - ((right*rang)) / 100;
270                                 switch(vol[i].type){
271                                 default:
272                                         break;
273                                 case Left:
274                                         v = m->rr(adev, reg);
275                                         v = (v & 0x007f) | (left << 8);
276                                         m->wr(adev, reg, v);
277                                         break;
278                                 case Right:
279                                         v = m->rr(adev, reg);
280                                         v = (v & 0x7f00) | right;
281                                         m->wr(adev, reg, v);
282                                         break;
283                                 case Stereo:
284                                         v = (left<<8) | right;
285                                         m->wr(adev, reg, v);
286                                         break;
287                                 }
288                         }
289                         qunlock(m);
290                         return n;
291                 }
292         }
293         if(vol[i].name == nil){
294                 char *p;
295                 for(p = tok[0]; *p; ++p)
296                         if(*p < '0' || *p > '9') {
297                                 qunlock(m);
298                                 error("no such volume setting");
299                         }
300                 rang = vol[0].range;
301                 reg = vol[0].reg;
302                 left = right = rang - ((atoi(tok[0])*rang)) / 100;
303                 v = (left<<8) | right;
304                 m->wr(adev, reg, v);
305         }
306         qunlock(m);
307
308         return n;
309 }
310
311 int
312 ac97hardrate(Audio *adev, int rate)
313 {
314         Mixer *m;
315         int oldrate;
316         m = adev->mixer;
317         oldrate = m->rr(adev, Pcmfrontdacrate);
318         if(rate > 0)
319                 m->wr(adev, Pcmfrontdacrate, rate);
320         return oldrate;
321 }
322
323 void
324 ac97mixreset(Audio *adev, ac97wrfn wr, ac97rdfn rr)
325 {
326         Mixer *m;
327         int i;
328         ushort t;
329         if(adev->mixer == nil)
330                 adev->mixer = malloc(sizeof(Mixer));
331         m = adev->mixer;
332         m->wr = wr;
333         m->rr = rr;
334         adev->volread = ac97mixread;
335         adev->volwrite = ac97mixwrite;
336         m->wr(adev, Reset, 0);
337         m->wr(adev, Powerdowncsr, 0);
338
339         t = (Adcpower | Dacpower | Anlpower | Refpower);
340         for(i = 0; i < Maxbusywait; i++){
341                 if((m->rr(adev, Powerdowncsr) & t) == t)
342                         break;
343                 microdelay(1);
344         }
345         if(i == Maxbusywait)
346                 print("#A%d: ac97 exhausted waiting powerup\n", adev->ctlrno);
347
348         t = m->rr(adev, Extid);
349         print("#A%d: ac97 codec ext:%s%s%s%s%s%s%s\n", adev->ctlrno,
350                 (t & Extvra) ? " vra" : "",
351                 (t & Extdra) ? " dra" : "",
352                 (t & Extspdif) ? " spdif" : "",
353                 (t & Extvrm) ? " vrm" : "",
354                 (t & Extcdac) ? " cdac" : "",
355                 (t & Extsdac) ? " sdac" : "",
356                 (t & Extldac) ? " ldac" : "");
357
358         if(t & Extvra){
359                 m->wr(adev, Extcsr, Extvra);
360                 m->vra = 1;
361         } else {
362                 print("#A%d: ac97 vra extension not supported\n", adev->ctlrno);
363                 m->vra = 0;
364         }
365 }