Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/pci/ca0106/ca0106_proc.c
10820 views
1
/*
2
* Copyright (c) 2004 James Courtier-Dutton <[email protected]>
3
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
4
* Version: 0.0.18
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
* 0.0.18
43
* Implement support for Line-in capture on SB Live 24bit.
44
*
45
* This code was initially based on code from ALSA's emu10k1x.c which is:
46
* Copyright (c) by Francisco Moraes <[email protected]>
47
*
48
* This program is free software; you can redistribute it and/or modify
49
* it under the terms of the GNU General Public License as published by
50
* the Free Software Foundation; either version 2 of the License, or
51
* (at your option) any later version.
52
*
53
* This program is distributed in the hope that it will be useful,
54
* but WITHOUT ANY WARRANTY; without even the implied warranty of
55
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56
* GNU General Public License for more details.
57
*
58
* You should have received a copy of the GNU General Public License
59
* along with this program; if not, write to the Free Software
60
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
61
*
62
*/
63
#include <linux/delay.h>
64
#include <linux/init.h>
65
#include <linux/interrupt.h>
66
#include <linux/moduleparam.h>
67
#include <sound/core.h>
68
#include <sound/initval.h>
69
#include <sound/pcm.h>
70
#include <sound/ac97_codec.h>
71
#include <sound/info.h>
72
#include <sound/asoundef.h>
73
#include <asm/io.h>
74
75
#include "ca0106.h"
76
77
78
#ifdef CONFIG_PROC_FS
79
80
struct snd_ca0106_category_str {
81
int val;
82
const char *name;
83
};
84
85
static struct snd_ca0106_category_str snd_ca0106_con_category[] = {
86
{ IEC958_AES1_CON_DAT, "DAT" },
87
{ IEC958_AES1_CON_VCR, "VCR" },
88
{ IEC958_AES1_CON_MICROPHONE, "microphone" },
89
{ IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },
90
{ IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
91
{ IEC958_AES1_CON_MIXER, "mixer" },
92
{ IEC958_AES1_CON_SAMPLER, "sampler" },
93
{ IEC958_AES1_CON_PCM_CODER, "PCM coder" },
94
{ IEC958_AES1_CON_IEC908_CD, "CD" },
95
{ IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
96
{ IEC958_AES1_CON_GENERAL, "general" },
97
};
98
99
100
static void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 value)
101
{
102
int i;
103
u32 status[4];
104
status[0] = value & 0xff;
105
status[1] = (value >> 8) & 0xff;
106
status[2] = (value >> 16) & 0xff;
107
status[3] = (value >> 24) & 0xff;
108
109
if (! (status[0] & IEC958_AES0_PROFESSIONAL)) {
110
/* consumer */
111
snd_iprintf(buffer, "Mode: consumer\n");
112
snd_iprintf(buffer, "Data: ");
113
if (!(status[0] & IEC958_AES0_NONAUDIO)) {
114
snd_iprintf(buffer, "audio\n");
115
} else {
116
snd_iprintf(buffer, "non-audio\n");
117
}
118
snd_iprintf(buffer, "Rate: ");
119
switch (status[3] & IEC958_AES3_CON_FS) {
120
case IEC958_AES3_CON_FS_44100:
121
snd_iprintf(buffer, "44100 Hz\n");
122
break;
123
case IEC958_AES3_CON_FS_48000:
124
snd_iprintf(buffer, "48000 Hz\n");
125
break;
126
case IEC958_AES3_CON_FS_32000:
127
snd_iprintf(buffer, "32000 Hz\n");
128
break;
129
default:
130
snd_iprintf(buffer, "unknown\n");
131
break;
132
}
133
snd_iprintf(buffer, "Copyright: ");
134
if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) {
135
snd_iprintf(buffer, "permitted\n");
136
} else {
137
snd_iprintf(buffer, "protected\n");
138
}
139
snd_iprintf(buffer, "Emphasis: ");
140
if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) {
141
snd_iprintf(buffer, "none\n");
142
} else {
143
snd_iprintf(buffer, "50/15us\n");
144
}
145
snd_iprintf(buffer, "Category: ");
146
for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) {
147
if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) {
148
snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name);
149
break;
150
}
151
}
152
if (i >= ARRAY_SIZE(snd_ca0106_con_category)) {
153
snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY);
154
}
155
snd_iprintf(buffer, "Original: ");
156
if (status[1] & IEC958_AES1_CON_ORIGINAL) {
157
snd_iprintf(buffer, "original\n");
158
} else {
159
snd_iprintf(buffer, "1st generation\n");
160
}
161
snd_iprintf(buffer, "Clock: ");
162
switch (status[3] & IEC958_AES3_CON_CLOCK) {
163
case IEC958_AES3_CON_CLOCK_1000PPM:
164
snd_iprintf(buffer, "1000 ppm\n");
165
break;
166
case IEC958_AES3_CON_CLOCK_50PPM:
167
snd_iprintf(buffer, "50 ppm\n");
168
break;
169
case IEC958_AES3_CON_CLOCK_VARIABLE:
170
snd_iprintf(buffer, "variable pitch\n");
171
break;
172
default:
173
snd_iprintf(buffer, "unknown\n");
174
break;
175
}
176
} else {
177
snd_iprintf(buffer, "Mode: professional\n");
178
snd_iprintf(buffer, "Data: ");
179
if (!(status[0] & IEC958_AES0_NONAUDIO)) {
180
snd_iprintf(buffer, "audio\n");
181
} else {
182
snd_iprintf(buffer, "non-audio\n");
183
}
184
snd_iprintf(buffer, "Rate: ");
185
switch (status[0] & IEC958_AES0_PRO_FS) {
186
case IEC958_AES0_PRO_FS_44100:
187
snd_iprintf(buffer, "44100 Hz\n");
188
break;
189
case IEC958_AES0_PRO_FS_48000:
190
snd_iprintf(buffer, "48000 Hz\n");
191
break;
192
case IEC958_AES0_PRO_FS_32000:
193
snd_iprintf(buffer, "32000 Hz\n");
194
break;
195
default:
196
snd_iprintf(buffer, "unknown\n");
197
break;
198
}
199
snd_iprintf(buffer, "Rate Locked: ");
200
if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED)
201
snd_iprintf(buffer, "no\n");
202
else
203
snd_iprintf(buffer, "yes\n");
204
snd_iprintf(buffer, "Emphasis: ");
205
switch (status[0] & IEC958_AES0_PRO_EMPHASIS) {
206
case IEC958_AES0_PRO_EMPHASIS_CCITT:
207
snd_iprintf(buffer, "CCITT J.17\n");
208
break;
209
case IEC958_AES0_PRO_EMPHASIS_NONE:
210
snd_iprintf(buffer, "none\n");
211
break;
212
case IEC958_AES0_PRO_EMPHASIS_5015:
213
snd_iprintf(buffer, "50/15us\n");
214
break;
215
case IEC958_AES0_PRO_EMPHASIS_NOTID:
216
default:
217
snd_iprintf(buffer, "unknown\n");
218
break;
219
}
220
snd_iprintf(buffer, "Stereophonic: ");
221
if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) {
222
snd_iprintf(buffer, "stereo\n");
223
} else {
224
snd_iprintf(buffer, "not indicated\n");
225
}
226
snd_iprintf(buffer, "Userbits: ");
227
switch (status[1] & IEC958_AES1_PRO_USERBITS) {
228
case IEC958_AES1_PRO_USERBITS_192:
229
snd_iprintf(buffer, "192bit\n");
230
break;
231
case IEC958_AES1_PRO_USERBITS_UDEF:
232
snd_iprintf(buffer, "user-defined\n");
233
break;
234
default:
235
snd_iprintf(buffer, "unknown\n");
236
break;
237
}
238
snd_iprintf(buffer, "Sample Bits: ");
239
switch (status[2] & IEC958_AES2_PRO_SBITS) {
240
case IEC958_AES2_PRO_SBITS_20:
241
snd_iprintf(buffer, "20 bit\n");
242
break;
243
case IEC958_AES2_PRO_SBITS_24:
244
snd_iprintf(buffer, "24 bit\n");
245
break;
246
case IEC958_AES2_PRO_SBITS_UDEF:
247
snd_iprintf(buffer, "user defined\n");
248
break;
249
default:
250
snd_iprintf(buffer, "unknown\n");
251
break;
252
}
253
snd_iprintf(buffer, "Word Length: ");
254
switch (status[2] & IEC958_AES2_PRO_WORDLEN) {
255
case IEC958_AES2_PRO_WORDLEN_22_18:
256
snd_iprintf(buffer, "22 bit or 18 bit\n");
257
break;
258
case IEC958_AES2_PRO_WORDLEN_23_19:
259
snd_iprintf(buffer, "23 bit or 19 bit\n");
260
break;
261
case IEC958_AES2_PRO_WORDLEN_24_20:
262
snd_iprintf(buffer, "24 bit or 20 bit\n");
263
break;
264
case IEC958_AES2_PRO_WORDLEN_20_16:
265
snd_iprintf(buffer, "20 bit or 16 bit\n");
266
break;
267
default:
268
snd_iprintf(buffer, "unknown\n");
269
break;
270
}
271
}
272
}
273
274
static void snd_ca0106_proc_iec958(struct snd_info_entry *entry,
275
struct snd_info_buffer *buffer)
276
{
277
struct snd_ca0106 *emu = entry->private_data;
278
u32 value;
279
280
value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0);
281
snd_iprintf(buffer, "Status: %s, %s, %s\n",
282
(value & 0x100000) ? "Rate Locked" : "Not Rate Locked",
283
(value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock",
284
(value & 0x400000) ? "Audio Valid" : "No valid audio" );
285
snd_iprintf(buffer, "Estimated sample rate: %u\n",
286
((value & 0xfffff) * 48000) / 0x8000 );
287
if (value & 0x200000) {
288
snd_iprintf(buffer, "IEC958/SPDIF input status:\n");
289
value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0);
290
snd_ca0106_proc_dump_iec958(buffer, value);
291
}
292
293
snd_iprintf(buffer, "\n");
294
}
295
296
static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry,
297
struct snd_info_buffer *buffer)
298
{
299
struct snd_ca0106 *emu = entry->private_data;
300
unsigned long flags;
301
char line[64];
302
u32 reg, val;
303
while (!snd_info_get_line(buffer, line, sizeof(line))) {
304
if (sscanf(line, "%x %x", &reg, &val) != 2)
305
continue;
306
if (reg < 0x40 && val <= 0xffffffff) {
307
spin_lock_irqsave(&emu->emu_lock, flags);
308
outl(val, emu->port + (reg & 0xfffffffc));
309
spin_unlock_irqrestore(&emu->emu_lock, flags);
310
}
311
}
312
}
313
314
static void snd_ca0106_proc_reg_read32(struct snd_info_entry *entry,
315
struct snd_info_buffer *buffer)
316
{
317
struct snd_ca0106 *emu = entry->private_data;
318
unsigned long value;
319
unsigned long flags;
320
int i;
321
snd_iprintf(buffer, "Registers:\n\n");
322
for(i = 0; i < 0x20; i+=4) {
323
spin_lock_irqsave(&emu->emu_lock, flags);
324
value = inl(emu->port + i);
325
spin_unlock_irqrestore(&emu->emu_lock, flags);
326
snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
327
}
328
}
329
330
static void snd_ca0106_proc_reg_read16(struct snd_info_entry *entry,
331
struct snd_info_buffer *buffer)
332
{
333
struct snd_ca0106 *emu = entry->private_data;
334
unsigned int value;
335
unsigned long flags;
336
int i;
337
snd_iprintf(buffer, "Registers:\n\n");
338
for(i = 0; i < 0x20; i+=2) {
339
spin_lock_irqsave(&emu->emu_lock, flags);
340
value = inw(emu->port + i);
341
spin_unlock_irqrestore(&emu->emu_lock, flags);
342
snd_iprintf(buffer, "Register %02X: %04X\n", i, value);
343
}
344
}
345
346
static void snd_ca0106_proc_reg_read8(struct snd_info_entry *entry,
347
struct snd_info_buffer *buffer)
348
{
349
struct snd_ca0106 *emu = entry->private_data;
350
unsigned int value;
351
unsigned long flags;
352
int i;
353
snd_iprintf(buffer, "Registers:\n\n");
354
for(i = 0; i < 0x20; i+=1) {
355
spin_lock_irqsave(&emu->emu_lock, flags);
356
value = inb(emu->port + i);
357
spin_unlock_irqrestore(&emu->emu_lock, flags);
358
snd_iprintf(buffer, "Register %02X: %02X\n", i, value);
359
}
360
}
361
362
static void snd_ca0106_proc_reg_read1(struct snd_info_entry *entry,
363
struct snd_info_buffer *buffer)
364
{
365
struct snd_ca0106 *emu = entry->private_data;
366
unsigned long value;
367
int i,j;
368
369
snd_iprintf(buffer, "Registers\n");
370
for(i = 0; i < 0x40; i++) {
371
snd_iprintf(buffer, "%02X: ",i);
372
for (j = 0; j < 4; j++) {
373
value = snd_ca0106_ptr_read(emu, i, j);
374
snd_iprintf(buffer, "%08lX ", value);
375
}
376
snd_iprintf(buffer, "\n");
377
}
378
}
379
380
static void snd_ca0106_proc_reg_read2(struct snd_info_entry *entry,
381
struct snd_info_buffer *buffer)
382
{
383
struct snd_ca0106 *emu = entry->private_data;
384
unsigned long value;
385
int i,j;
386
387
snd_iprintf(buffer, "Registers\n");
388
for(i = 0x40; i < 0x80; i++) {
389
snd_iprintf(buffer, "%02X: ",i);
390
for (j = 0; j < 4; j++) {
391
value = snd_ca0106_ptr_read(emu, i, j);
392
snd_iprintf(buffer, "%08lX ", value);
393
}
394
snd_iprintf(buffer, "\n");
395
}
396
}
397
398
static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry,
399
struct snd_info_buffer *buffer)
400
{
401
struct snd_ca0106 *emu = entry->private_data;
402
char line[64];
403
unsigned int reg, channel_id , val;
404
while (!snd_info_get_line(buffer, line, sizeof(line))) {
405
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
406
continue;
407
if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3)
408
snd_ca0106_ptr_write(emu, reg, channel_id, val);
409
}
410
}
411
412
static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry,
413
struct snd_info_buffer *buffer)
414
{
415
struct snd_ca0106 *emu = entry->private_data;
416
char line[64];
417
unsigned int reg, val;
418
while (!snd_info_get_line(buffer, line, sizeof(line))) {
419
if (sscanf(line, "%x %x", &reg, &val) != 2)
420
continue;
421
if ((reg <= 0x7f) || (val <= 0x1ff)) {
422
snd_ca0106_i2c_write(emu, reg, val);
423
}
424
}
425
}
426
427
int __devinit snd_ca0106_proc_init(struct snd_ca0106 * emu)
428
{
429
struct snd_info_entry *entry;
430
431
if(! snd_card_proc_new(emu->card, "iec958", &entry))
432
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_iec958);
433
if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
434
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32);
435
entry->c.text.write = snd_ca0106_proc_reg_write32;
436
entry->mode |= S_IWUSR;
437
}
438
if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
439
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16);
440
if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry))
441
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read8);
442
if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
443
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1);
444
entry->c.text.write = snd_ca0106_proc_reg_write;
445
entry->mode |= S_IWUSR;
446
}
447
if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
448
entry->c.text.write = snd_ca0106_proc_i2c_write;
449
entry->private_data = emu;
450
entry->mode |= S_IWUSR;
451
}
452
if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry))
453
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2);
454
return 0;
455
}
456
457
#endif /* CONFIG_PROC_FS */
458
459