diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/pci/ca0106/ca0106_proc.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/pci/ca0106/ca0106_proc.c')
-rw-r--r-- | sound/pci/ca0106/ca0106_proc.c | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c new file mode 100644 index 000000000000..afb711421e47 --- /dev/null +++ b/sound/pci/ca0106/ca0106_proc.c | |||
@@ -0,0 +1,436 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> | ||
3 | * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit | ||
4 | * Version: 0.0.17 | ||
5 | * | ||
6 | * FEATURES currently supported: | ||
7 | * See ca0106_main.c for features. | ||
8 | * | ||
9 | * Changelog: | ||
10 | * Support interrupts per period. | ||
11 | * Removed noise from Center/LFE channel when in Analog mode. | ||
12 | * Rename and remove mixer controls. | ||
13 | * 0.0.6 | ||
14 | * Use separate card based DMA buffer for periods table list. | ||
15 | * 0.0.7 | ||
16 | * Change remove and rename ctrls into lists. | ||
17 | * 0.0.8 | ||
18 | * Try to fix capture sources. | ||
19 | * 0.0.9 | ||
20 | * Fix AC3 output. | ||
21 | * Enable S32_LE format support. | ||
22 | * 0.0.10 | ||
23 | * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) | ||
24 | * 0.0.11 | ||
25 | * Add Model name recognition. | ||
26 | * 0.0.12 | ||
27 | * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. | ||
28 | * Remove redundent "voice" handling. | ||
29 | * 0.0.13 | ||
30 | * Single trigger call for multi channels. | ||
31 | * 0.0.14 | ||
32 | * Set limits based on what the sound card hardware can do. | ||
33 | * playback periods_min=2, periods_max=8 | ||
34 | * capture hw constraints require period_size = n * 64 bytes. | ||
35 | * playback hw constraints require period_size = n * 64 bytes. | ||
36 | * 0.0.15 | ||
37 | * Separate ca0106.c into separate functional .c files. | ||
38 | * 0.0.16 | ||
39 | * Modified Copyright message. | ||
40 | * 0.0.17 | ||
41 | * Add iec958 file in proc file system to show status of SPDIF in. | ||
42 | * | ||
43 | * This code was initally based on code from ALSA's emu10k1x.c which is: | ||
44 | * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> | ||
45 | * | ||
46 | * This program is free software; you can redistribute it and/or modify | ||
47 | * it under the terms of the GNU General Public License as published by | ||
48 | * the Free Software Foundation; either version 2 of the License, or | ||
49 | * (at your option) any later version. | ||
50 | * | ||
51 | * This program is distributed in the hope that it will be useful, | ||
52 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
53 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
54 | * GNU General Public License for more details. | ||
55 | * | ||
56 | * You should have received a copy of the GNU General Public License | ||
57 | * along with this program; if not, write to the Free Software | ||
58 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
59 | * | ||
60 | */ | ||
61 | #include <sound/driver.h> | ||
62 | #include <linux/delay.h> | ||
63 | #include <linux/init.h> | ||
64 | #include <linux/interrupt.h> | ||
65 | #include <linux/pci.h> | ||
66 | #include <linux/slab.h> | ||
67 | #include <linux/moduleparam.h> | ||
68 | #include <sound/core.h> | ||
69 | #include <sound/initval.h> | ||
70 | #include <sound/pcm.h> | ||
71 | #include <sound/ac97_codec.h> | ||
72 | #include <sound/info.h> | ||
73 | #include <sound/asoundef.h> | ||
74 | |||
75 | #include "ca0106.h" | ||
76 | |||
77 | |||
78 | struct snd_ca0106_category_str { | ||
79 | int val; | ||
80 | const char *name; | ||
81 | }; | ||
82 | |||
83 | static struct snd_ca0106_category_str snd_ca0106_con_category[] = { | ||
84 | { IEC958_AES1_CON_DAT, "DAT" }, | ||
85 | { IEC958_AES1_CON_VCR, "VCR" }, | ||
86 | { IEC958_AES1_CON_MICROPHONE, "microphone" }, | ||
87 | { IEC958_AES1_CON_SYNTHESIZER, "synthesizer" }, | ||
88 | { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" }, | ||
89 | { IEC958_AES1_CON_MIXER, "mixer" }, | ||
90 | { IEC958_AES1_CON_SAMPLER, "sampler" }, | ||
91 | { IEC958_AES1_CON_PCM_CODER, "PCM coder" }, | ||
92 | { IEC958_AES1_CON_IEC908_CD, "CD" }, | ||
93 | { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" }, | ||
94 | { IEC958_AES1_CON_GENERAL, "general" }, | ||
95 | }; | ||
96 | |||
97 | |||
98 | void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value) | ||
99 | { | ||
100 | int i; | ||
101 | u32 status[4]; | ||
102 | status[0] = value & 0xff; | ||
103 | status[1] = (value >> 8) & 0xff; | ||
104 | status[2] = (value >> 16) & 0xff; | ||
105 | status[3] = (value >> 24) & 0xff; | ||
106 | |||
107 | if (! (status[0] & IEC958_AES0_PROFESSIONAL)) { | ||
108 | /* consumer */ | ||
109 | snd_iprintf(buffer, "Mode: consumer\n"); | ||
110 | snd_iprintf(buffer, "Data: "); | ||
111 | if (!(status[0] & IEC958_AES0_NONAUDIO)) { | ||
112 | snd_iprintf(buffer, "audio\n"); | ||
113 | } else { | ||
114 | snd_iprintf(buffer, "non-audio\n"); | ||
115 | } | ||
116 | snd_iprintf(buffer, "Rate: "); | ||
117 | switch (status[3] & IEC958_AES3_CON_FS) { | ||
118 | case IEC958_AES3_CON_FS_44100: | ||
119 | snd_iprintf(buffer, "44100 Hz\n"); | ||
120 | break; | ||
121 | case IEC958_AES3_CON_FS_48000: | ||
122 | snd_iprintf(buffer, "48000 Hz\n"); | ||
123 | break; | ||
124 | case IEC958_AES3_CON_FS_32000: | ||
125 | snd_iprintf(buffer, "32000 Hz\n"); | ||
126 | break; | ||
127 | default: | ||
128 | snd_iprintf(buffer, "unknown\n"); | ||
129 | break; | ||
130 | } | ||
131 | snd_iprintf(buffer, "Copyright: "); | ||
132 | if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) { | ||
133 | snd_iprintf(buffer, "permitted\n"); | ||
134 | } else { | ||
135 | snd_iprintf(buffer, "protected\n"); | ||
136 | } | ||
137 | snd_iprintf(buffer, "Emphasis: "); | ||
138 | if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) { | ||
139 | snd_iprintf(buffer, "none\n"); | ||
140 | } else { | ||
141 | snd_iprintf(buffer, "50/15us\n"); | ||
142 | } | ||
143 | snd_iprintf(buffer, "Category: "); | ||
144 | for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) { | ||
145 | if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) { | ||
146 | snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name); | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | if (i >= ARRAY_SIZE(snd_ca0106_con_category)) { | ||
151 | snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY); | ||
152 | } | ||
153 | snd_iprintf(buffer, "Original: "); | ||
154 | if (status[1] & IEC958_AES1_CON_ORIGINAL) { | ||
155 | snd_iprintf(buffer, "original\n"); | ||
156 | } else { | ||
157 | snd_iprintf(buffer, "1st generation\n"); | ||
158 | } | ||
159 | snd_iprintf(buffer, "Clock: "); | ||
160 | switch (status[3] & IEC958_AES3_CON_CLOCK) { | ||
161 | case IEC958_AES3_CON_CLOCK_1000PPM: | ||
162 | snd_iprintf(buffer, "1000 ppm\n"); | ||
163 | break; | ||
164 | case IEC958_AES3_CON_CLOCK_50PPM: | ||
165 | snd_iprintf(buffer, "50 ppm\n"); | ||
166 | break; | ||
167 | case IEC958_AES3_CON_CLOCK_VARIABLE: | ||
168 | snd_iprintf(buffer, "variable pitch\n"); | ||
169 | break; | ||
170 | default: | ||
171 | snd_iprintf(buffer, "unknown\n"); | ||
172 | break; | ||
173 | } | ||
174 | } else { | ||
175 | snd_iprintf(buffer, "Mode: professional\n"); | ||
176 | snd_iprintf(buffer, "Data: "); | ||
177 | if (!(status[0] & IEC958_AES0_NONAUDIO)) { | ||
178 | snd_iprintf(buffer, "audio\n"); | ||
179 | } else { | ||
180 | snd_iprintf(buffer, "non-audio\n"); | ||
181 | } | ||
182 | snd_iprintf(buffer, "Rate: "); | ||
183 | switch (status[0] & IEC958_AES0_PRO_FS) { | ||
184 | case IEC958_AES0_PRO_FS_44100: | ||
185 | snd_iprintf(buffer, "44100 Hz\n"); | ||
186 | break; | ||
187 | case IEC958_AES0_PRO_FS_48000: | ||
188 | snd_iprintf(buffer, "48000 Hz\n"); | ||
189 | break; | ||
190 | case IEC958_AES0_PRO_FS_32000: | ||
191 | snd_iprintf(buffer, "32000 Hz\n"); | ||
192 | break; | ||
193 | default: | ||
194 | snd_iprintf(buffer, "unknown\n"); | ||
195 | break; | ||
196 | } | ||
197 | snd_iprintf(buffer, "Rate Locked: "); | ||
198 | if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED) | ||
199 | snd_iprintf(buffer, "no\n"); | ||
200 | else | ||
201 | snd_iprintf(buffer, "yes\n"); | ||
202 | snd_iprintf(buffer, "Emphasis: "); | ||
203 | switch (status[0] & IEC958_AES0_PRO_EMPHASIS) { | ||
204 | case IEC958_AES0_PRO_EMPHASIS_CCITT: | ||
205 | snd_iprintf(buffer, "CCITT J.17\n"); | ||
206 | break; | ||
207 | case IEC958_AES0_PRO_EMPHASIS_NONE: | ||
208 | snd_iprintf(buffer, "none\n"); | ||
209 | break; | ||
210 | case IEC958_AES0_PRO_EMPHASIS_5015: | ||
211 | snd_iprintf(buffer, "50/15us\n"); | ||
212 | break; | ||
213 | case IEC958_AES0_PRO_EMPHASIS_NOTID: | ||
214 | default: | ||
215 | snd_iprintf(buffer, "unknown\n"); | ||
216 | break; | ||
217 | } | ||
218 | snd_iprintf(buffer, "Stereophonic: "); | ||
219 | if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) { | ||
220 | snd_iprintf(buffer, "stereo\n"); | ||
221 | } else { | ||
222 | snd_iprintf(buffer, "not indicated\n"); | ||
223 | } | ||
224 | snd_iprintf(buffer, "Userbits: "); | ||
225 | switch (status[1] & IEC958_AES1_PRO_USERBITS) { | ||
226 | case IEC958_AES1_PRO_USERBITS_192: | ||
227 | snd_iprintf(buffer, "192bit\n"); | ||
228 | break; | ||
229 | case IEC958_AES1_PRO_USERBITS_UDEF: | ||
230 | snd_iprintf(buffer, "user-defined\n"); | ||
231 | break; | ||
232 | default: | ||
233 | snd_iprintf(buffer, "unkown\n"); | ||
234 | break; | ||
235 | } | ||
236 | snd_iprintf(buffer, "Sample Bits: "); | ||
237 | switch (status[2] & IEC958_AES2_PRO_SBITS) { | ||
238 | case IEC958_AES2_PRO_SBITS_20: | ||
239 | snd_iprintf(buffer, "20 bit\n"); | ||
240 | break; | ||
241 | case IEC958_AES2_PRO_SBITS_24: | ||
242 | snd_iprintf(buffer, "24 bit\n"); | ||
243 | break; | ||
244 | case IEC958_AES2_PRO_SBITS_UDEF: | ||
245 | snd_iprintf(buffer, "user defined\n"); | ||
246 | break; | ||
247 | default: | ||
248 | snd_iprintf(buffer, "unknown\n"); | ||
249 | break; | ||
250 | } | ||
251 | snd_iprintf(buffer, "Word Length: "); | ||
252 | switch (status[2] & IEC958_AES2_PRO_WORDLEN) { | ||
253 | case IEC958_AES2_PRO_WORDLEN_22_18: | ||
254 | snd_iprintf(buffer, "22 bit or 18 bit\n"); | ||
255 | break; | ||
256 | case IEC958_AES2_PRO_WORDLEN_23_19: | ||
257 | snd_iprintf(buffer, "23 bit or 19 bit\n"); | ||
258 | break; | ||
259 | case IEC958_AES2_PRO_WORDLEN_24_20: | ||
260 | snd_iprintf(buffer, "24 bit or 20 bit\n"); | ||
261 | break; | ||
262 | case IEC958_AES2_PRO_WORDLEN_20_16: | ||
263 | snd_iprintf(buffer, "20 bit or 16 bit\n"); | ||
264 | break; | ||
265 | default: | ||
266 | snd_iprintf(buffer, "unknown\n"); | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | static void snd_ca0106_proc_iec958(snd_info_entry_t *entry, | ||
273 | snd_info_buffer_t * buffer) | ||
274 | { | ||
275 | ca0106_t *emu = entry->private_data; | ||
276 | u32 value; | ||
277 | |||
278 | value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0); | ||
279 | snd_iprintf(buffer, "Status: %s, %s, %s\n", | ||
280 | (value & 0x100000) ? "Rate Locked" : "Not Rate Locked", | ||
281 | (value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock", | ||
282 | (value & 0x400000) ? "Audio Valid" : "No valid audio" ); | ||
283 | snd_iprintf(buffer, "Estimated sample rate: %u\n", | ||
284 | ((value & 0xfffff) * 48000) / 0x8000 ); | ||
285 | if (value & 0x200000) { | ||
286 | snd_iprintf(buffer, "IEC958/SPDIF input status:\n"); | ||
287 | value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0); | ||
288 | snd_ca0106_proc_dump_iec958(buffer, value); | ||
289 | } | ||
290 | |||
291 | snd_iprintf(buffer, "\n"); | ||
292 | } | ||
293 | |||
294 | static void snd_ca0106_proc_reg_write32(snd_info_entry_t *entry, | ||
295 | snd_info_buffer_t * buffer) | ||
296 | { | ||
297 | ca0106_t *emu = entry->private_data; | ||
298 | unsigned long flags; | ||
299 | char line[64]; | ||
300 | u32 reg, val; | ||
301 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
302 | if (sscanf(line, "%x %x", ®, &val) != 2) | ||
303 | continue; | ||
304 | if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) { | ||
305 | spin_lock_irqsave(&emu->emu_lock, flags); | ||
306 | outl(val, emu->port + (reg & 0xfffffffc)); | ||
307 | spin_unlock_irqrestore(&emu->emu_lock, flags); | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | |||
312 | static void snd_ca0106_proc_reg_read32(snd_info_entry_t *entry, | ||
313 | snd_info_buffer_t * buffer) | ||
314 | { | ||
315 | ca0106_t *emu = entry->private_data; | ||
316 | unsigned long value; | ||
317 | unsigned long flags; | ||
318 | int i; | ||
319 | snd_iprintf(buffer, "Registers:\n\n"); | ||
320 | for(i = 0; i < 0x20; i+=4) { | ||
321 | spin_lock_irqsave(&emu->emu_lock, flags); | ||
322 | value = inl(emu->port + i); | ||
323 | spin_unlock_irqrestore(&emu->emu_lock, flags); | ||
324 | snd_iprintf(buffer, "Register %02X: %08lX\n", i, value); | ||
325 | } | ||
326 | } | ||
327 | |||
328 | static void snd_ca0106_proc_reg_read16(snd_info_entry_t *entry, | ||
329 | snd_info_buffer_t * buffer) | ||
330 | { | ||
331 | ca0106_t *emu = entry->private_data; | ||
332 | unsigned int value; | ||
333 | unsigned long flags; | ||
334 | int i; | ||
335 | snd_iprintf(buffer, "Registers:\n\n"); | ||
336 | for(i = 0; i < 0x20; i+=2) { | ||
337 | spin_lock_irqsave(&emu->emu_lock, flags); | ||
338 | value = inw(emu->port + i); | ||
339 | spin_unlock_irqrestore(&emu->emu_lock, flags); | ||
340 | snd_iprintf(buffer, "Register %02X: %04X\n", i, value); | ||
341 | } | ||
342 | } | ||
343 | |||
344 | static void snd_ca0106_proc_reg_read8(snd_info_entry_t *entry, | ||
345 | snd_info_buffer_t * buffer) | ||
346 | { | ||
347 | ca0106_t *emu = entry->private_data; | ||
348 | unsigned int value; | ||
349 | unsigned long flags; | ||
350 | int i; | ||
351 | snd_iprintf(buffer, "Registers:\n\n"); | ||
352 | for(i = 0; i < 0x20; i+=1) { | ||
353 | spin_lock_irqsave(&emu->emu_lock, flags); | ||
354 | value = inb(emu->port + i); | ||
355 | spin_unlock_irqrestore(&emu->emu_lock, flags); | ||
356 | snd_iprintf(buffer, "Register %02X: %02X\n", i, value); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | static void snd_ca0106_proc_reg_read1(snd_info_entry_t *entry, | ||
361 | snd_info_buffer_t * buffer) | ||
362 | { | ||
363 | ca0106_t *emu = entry->private_data; | ||
364 | unsigned long value; | ||
365 | int i,j; | ||
366 | |||
367 | snd_iprintf(buffer, "Registers\n"); | ||
368 | for(i = 0; i < 0x40; i++) { | ||
369 | snd_iprintf(buffer, "%02X: ",i); | ||
370 | for (j = 0; j < 4; j++) { | ||
371 | value = snd_ca0106_ptr_read(emu, i, j); | ||
372 | snd_iprintf(buffer, "%08lX ", value); | ||
373 | } | ||
374 | snd_iprintf(buffer, "\n"); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | static void snd_ca0106_proc_reg_read2(snd_info_entry_t *entry, | ||
379 | snd_info_buffer_t * buffer) | ||
380 | { | ||
381 | ca0106_t *emu = entry->private_data; | ||
382 | unsigned long value; | ||
383 | int i,j; | ||
384 | |||
385 | snd_iprintf(buffer, "Registers\n"); | ||
386 | for(i = 0x40; i < 0x80; i++) { | ||
387 | snd_iprintf(buffer, "%02X: ",i); | ||
388 | for (j = 0; j < 4; j++) { | ||
389 | value = snd_ca0106_ptr_read(emu, i, j); | ||
390 | snd_iprintf(buffer, "%08lX ", value); | ||
391 | } | ||
392 | snd_iprintf(buffer, "\n"); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | static void snd_ca0106_proc_reg_write(snd_info_entry_t *entry, | ||
397 | snd_info_buffer_t * buffer) | ||
398 | { | ||
399 | ca0106_t *emu = entry->private_data; | ||
400 | char line[64]; | ||
401 | unsigned int reg, channel_id , val; | ||
402 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
403 | if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) | ||
404 | continue; | ||
405 | if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) ) | ||
406 | snd_ca0106_ptr_write(emu, reg, channel_id, val); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | |||
411 | int __devinit snd_ca0106_proc_init(ca0106_t * emu) | ||
412 | { | ||
413 | snd_info_entry_t *entry; | ||
414 | |||
415 | if(! snd_card_proc_new(emu->card, "iec958", &entry)) | ||
416 | snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_iec958); | ||
417 | if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) { | ||
418 | snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32); | ||
419 | entry->c.text.write_size = 64; | ||
420 | entry->c.text.write = snd_ca0106_proc_reg_write32; | ||
421 | } | ||
422 | if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry)) | ||
423 | snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16); | ||
424 | if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry)) | ||
425 | snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read8); | ||
426 | if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) { | ||
427 | snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1); | ||
428 | entry->c.text.write_size = 64; | ||
429 | entry->c.text.write = snd_ca0106_proc_reg_write; | ||
430 | // entry->private_data = emu; | ||
431 | } | ||
432 | if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) | ||
433 | snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read2); | ||
434 | return 0; | ||
435 | } | ||
436 | |||