xref: /linux/sound/pci/ca0106/ca0106_proc.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
41da177e4SLinus Torvalds  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
57199acdcSJames Courtier-Dutton  *  Version: 0.0.18
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  FEATURES currently supported:
81da177e4SLinus Torvalds  *    See ca0106_main.c for features.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  Changelog:
111da177e4SLinus Torvalds  *    Support interrupts per period.
121da177e4SLinus Torvalds  *    Removed noise from Center/LFE channel when in Analog mode.
131da177e4SLinus Torvalds  *    Rename and remove mixer controls.
141da177e4SLinus Torvalds  *  0.0.6
151da177e4SLinus Torvalds  *    Use separate card based DMA buffer for periods table list.
161da177e4SLinus Torvalds  *  0.0.7
171da177e4SLinus Torvalds  *    Change remove and rename ctrls into lists.
181da177e4SLinus Torvalds  *  0.0.8
191da177e4SLinus Torvalds  *    Try to fix capture sources.
201da177e4SLinus Torvalds  *  0.0.9
211da177e4SLinus Torvalds  *    Fix AC3 output.
221da177e4SLinus Torvalds  *    Enable S32_LE format support.
231da177e4SLinus Torvalds  *  0.0.10
241da177e4SLinus Torvalds  *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
251da177e4SLinus Torvalds  *  0.0.11
261da177e4SLinus Torvalds  *    Add Model name recognition.
271da177e4SLinus Torvalds  *  0.0.12
281da177e4SLinus Torvalds  *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
291da177e4SLinus Torvalds  *    Remove redundent "voice" handling.
301da177e4SLinus Torvalds  *  0.0.13
311da177e4SLinus Torvalds  *    Single trigger call for multi channels.
321da177e4SLinus Torvalds  *  0.0.14
331da177e4SLinus Torvalds  *    Set limits based on what the sound card hardware can do.
341da177e4SLinus Torvalds  *    playback periods_min=2, periods_max=8
351da177e4SLinus Torvalds  *    capture hw constraints require period_size = n * 64 bytes.
361da177e4SLinus Torvalds  *    playback hw constraints require period_size = n * 64 bytes.
371da177e4SLinus Torvalds  *  0.0.15
381da177e4SLinus Torvalds  *    Separate ca0106.c into separate functional .c files.
391da177e4SLinus Torvalds  *  0.0.16
401da177e4SLinus Torvalds  *    Modified Copyright message.
411da177e4SLinus Torvalds  *  0.0.17
421da177e4SLinus Torvalds  *    Add iec958 file in proc file system to show status of SPDIF in.
437199acdcSJames Courtier-Dutton  *  0.0.18
447199acdcSJames Courtier-Dutton  *    Implement support for Line-in capture on SB Live 24bit.
451da177e4SLinus Torvalds  *
4625985edcSLucas De Marchi  *  This code was initially based on code from ALSA's emu10k1x.c which is:
471da177e4SLinus Torvalds  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
481da177e4SLinus Torvalds  */
491da177e4SLinus Torvalds #include <linux/delay.h>
501da177e4SLinus Torvalds #include <linux/init.h>
511da177e4SLinus Torvalds #include <linux/interrupt.h>
521da177e4SLinus Torvalds #include <linux/moduleparam.h>
536cbbfe1cSTakashi Iwai #include <linux/io.h>
541da177e4SLinus Torvalds #include <sound/core.h>
551da177e4SLinus Torvalds #include <sound/initval.h>
561da177e4SLinus Torvalds #include <sound/pcm.h>
571da177e4SLinus Torvalds #include <sound/ac97_codec.h>
581da177e4SLinus Torvalds #include <sound/info.h>
591da177e4SLinus Torvalds #include <sound/asoundef.h>
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #include "ca0106.h"
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds struct snd_ca0106_category_str {
651da177e4SLinus Torvalds 	int val;
661da177e4SLinus Torvalds 	const char *name;
671da177e4SLinus Torvalds };
681da177e4SLinus Torvalds 
69*311840d9STakashi Iwai static const struct snd_ca0106_category_str snd_ca0106_con_category[] = {
701da177e4SLinus Torvalds 	{ IEC958_AES1_CON_DAT, "DAT" },
711da177e4SLinus Torvalds 	{ IEC958_AES1_CON_VCR, "VCR" },
721da177e4SLinus Torvalds 	{ IEC958_AES1_CON_MICROPHONE, "microphone" },
731da177e4SLinus Torvalds 	{ IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },
741da177e4SLinus Torvalds 	{ IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
751da177e4SLinus Torvalds 	{ IEC958_AES1_CON_MIXER, "mixer" },
761da177e4SLinus Torvalds 	{ IEC958_AES1_CON_SAMPLER, "sampler" },
771da177e4SLinus Torvalds 	{ IEC958_AES1_CON_PCM_CODER, "PCM coder" },
781da177e4SLinus Torvalds 	{ IEC958_AES1_CON_IEC908_CD, "CD" },
791da177e4SLinus Torvalds 	{ IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
801da177e4SLinus Torvalds 	{ IEC958_AES1_CON_GENERAL, "general" },
811da177e4SLinus Torvalds };
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 
84e4a3d145STakashi Iwai static void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 value)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	int i;
871da177e4SLinus Torvalds 	u32 status[4];
881da177e4SLinus Torvalds 	status[0] = value & 0xff;
891da177e4SLinus Torvalds 	status[1] = (value >> 8) & 0xff;
901da177e4SLinus Torvalds 	status[2] = (value >> 16)  & 0xff;
911da177e4SLinus Torvalds 	status[3] = (value >> 24)  & 0xff;
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	if (! (status[0] & IEC958_AES0_PROFESSIONAL)) {
941da177e4SLinus Torvalds 		/* consumer */
951da177e4SLinus Torvalds 		snd_iprintf(buffer, "Mode: consumer\n");
961da177e4SLinus Torvalds 		snd_iprintf(buffer, "Data: ");
971da177e4SLinus Torvalds 		if (!(status[0] & IEC958_AES0_NONAUDIO)) {
981da177e4SLinus Torvalds 			snd_iprintf(buffer, "audio\n");
991da177e4SLinus Torvalds 		} else {
1001da177e4SLinus Torvalds 			snd_iprintf(buffer, "non-audio\n");
1011da177e4SLinus Torvalds 		}
1021da177e4SLinus Torvalds 		snd_iprintf(buffer, "Rate: ");
1031da177e4SLinus Torvalds 		switch (status[3] & IEC958_AES3_CON_FS) {
1041da177e4SLinus Torvalds 		case IEC958_AES3_CON_FS_44100:
1051da177e4SLinus Torvalds 			snd_iprintf(buffer, "44100 Hz\n");
1061da177e4SLinus Torvalds 			break;
1071da177e4SLinus Torvalds 		case IEC958_AES3_CON_FS_48000:
1081da177e4SLinus Torvalds 			snd_iprintf(buffer, "48000 Hz\n");
1091da177e4SLinus Torvalds 			break;
1101da177e4SLinus Torvalds 		case IEC958_AES3_CON_FS_32000:
1111da177e4SLinus Torvalds 			snd_iprintf(buffer, "32000 Hz\n");
1121da177e4SLinus Torvalds 			break;
1131da177e4SLinus Torvalds 		default:
1141da177e4SLinus Torvalds 			snd_iprintf(buffer, "unknown\n");
1151da177e4SLinus Torvalds 			break;
1161da177e4SLinus Torvalds 		}
1171da177e4SLinus Torvalds 		snd_iprintf(buffer, "Copyright: ");
1181da177e4SLinus Torvalds 		if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) {
1191da177e4SLinus Torvalds 			snd_iprintf(buffer, "permitted\n");
1201da177e4SLinus Torvalds 		} else {
1211da177e4SLinus Torvalds 			snd_iprintf(buffer, "protected\n");
1221da177e4SLinus Torvalds 		}
1231da177e4SLinus Torvalds 		snd_iprintf(buffer, "Emphasis: ");
1241da177e4SLinus Torvalds 		if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) {
1251da177e4SLinus Torvalds 			snd_iprintf(buffer, "none\n");
1261da177e4SLinus Torvalds 		} else {
1271da177e4SLinus Torvalds 			snd_iprintf(buffer, "50/15us\n");
1281da177e4SLinus Torvalds 		}
1291da177e4SLinus Torvalds 		snd_iprintf(buffer, "Category: ");
1301da177e4SLinus Torvalds 		for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) {
1311da177e4SLinus Torvalds 			if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) {
1321da177e4SLinus Torvalds 				snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name);
1331da177e4SLinus Torvalds 				break;
1341da177e4SLinus Torvalds 			}
1351da177e4SLinus Torvalds 		}
1361da177e4SLinus Torvalds 		if (i >= ARRAY_SIZE(snd_ca0106_con_category)) {
1371da177e4SLinus Torvalds 			snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY);
1381da177e4SLinus Torvalds 		}
1391da177e4SLinus Torvalds 		snd_iprintf(buffer, "Original: ");
1401da177e4SLinus Torvalds 		if (status[1] & IEC958_AES1_CON_ORIGINAL) {
1411da177e4SLinus Torvalds 			snd_iprintf(buffer, "original\n");
1421da177e4SLinus Torvalds 		} else {
1431da177e4SLinus Torvalds 			snd_iprintf(buffer, "1st generation\n");
1441da177e4SLinus Torvalds 		}
1451da177e4SLinus Torvalds 		snd_iprintf(buffer, "Clock: ");
1461da177e4SLinus Torvalds 		switch (status[3] & IEC958_AES3_CON_CLOCK) {
1471da177e4SLinus Torvalds 		case IEC958_AES3_CON_CLOCK_1000PPM:
1481da177e4SLinus Torvalds 			snd_iprintf(buffer, "1000 ppm\n");
1491da177e4SLinus Torvalds 			break;
1501da177e4SLinus Torvalds 		case IEC958_AES3_CON_CLOCK_50PPM:
1511da177e4SLinus Torvalds 			snd_iprintf(buffer, "50 ppm\n");
1521da177e4SLinus Torvalds 			break;
1531da177e4SLinus Torvalds 		case IEC958_AES3_CON_CLOCK_VARIABLE:
1541da177e4SLinus Torvalds 			snd_iprintf(buffer, "variable pitch\n");
1551da177e4SLinus Torvalds 			break;
1561da177e4SLinus Torvalds 		default:
1571da177e4SLinus Torvalds 			snd_iprintf(buffer, "unknown\n");
1581da177e4SLinus Torvalds 			break;
1591da177e4SLinus Torvalds 		}
1601da177e4SLinus Torvalds 	} else {
1611da177e4SLinus Torvalds 		snd_iprintf(buffer, "Mode: professional\n");
1621da177e4SLinus Torvalds 		snd_iprintf(buffer, "Data: ");
1631da177e4SLinus Torvalds 		if (!(status[0] & IEC958_AES0_NONAUDIO)) {
1641da177e4SLinus Torvalds 			snd_iprintf(buffer, "audio\n");
1651da177e4SLinus Torvalds 		} else {
1661da177e4SLinus Torvalds 			snd_iprintf(buffer, "non-audio\n");
1671da177e4SLinus Torvalds 		}
1681da177e4SLinus Torvalds 		snd_iprintf(buffer, "Rate: ");
1691da177e4SLinus Torvalds 		switch (status[0] & IEC958_AES0_PRO_FS) {
1701da177e4SLinus Torvalds 		case IEC958_AES0_PRO_FS_44100:
1711da177e4SLinus Torvalds 			snd_iprintf(buffer, "44100 Hz\n");
1721da177e4SLinus Torvalds 			break;
1731da177e4SLinus Torvalds 		case IEC958_AES0_PRO_FS_48000:
1741da177e4SLinus Torvalds 			snd_iprintf(buffer, "48000 Hz\n");
1751da177e4SLinus Torvalds 			break;
1761da177e4SLinus Torvalds 		case IEC958_AES0_PRO_FS_32000:
1771da177e4SLinus Torvalds 			snd_iprintf(buffer, "32000 Hz\n");
1781da177e4SLinus Torvalds 			break;
1791da177e4SLinus Torvalds 		default:
1801da177e4SLinus Torvalds 			snd_iprintf(buffer, "unknown\n");
1811da177e4SLinus Torvalds 			break;
1821da177e4SLinus Torvalds 		}
1831da177e4SLinus Torvalds 		snd_iprintf(buffer, "Rate Locked: ");
1841da177e4SLinus Torvalds 		if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED)
1851da177e4SLinus Torvalds 			snd_iprintf(buffer, "no\n");
1861da177e4SLinus Torvalds 		else
1871da177e4SLinus Torvalds 			snd_iprintf(buffer, "yes\n");
1881da177e4SLinus Torvalds 		snd_iprintf(buffer, "Emphasis: ");
1891da177e4SLinus Torvalds 		switch (status[0] & IEC958_AES0_PRO_EMPHASIS) {
1901da177e4SLinus Torvalds 		case IEC958_AES0_PRO_EMPHASIS_CCITT:
1911da177e4SLinus Torvalds 			snd_iprintf(buffer, "CCITT J.17\n");
1921da177e4SLinus Torvalds 			break;
1931da177e4SLinus Torvalds 		case IEC958_AES0_PRO_EMPHASIS_NONE:
1941da177e4SLinus Torvalds 			snd_iprintf(buffer, "none\n");
1951da177e4SLinus Torvalds 			break;
1961da177e4SLinus Torvalds 		case IEC958_AES0_PRO_EMPHASIS_5015:
1971da177e4SLinus Torvalds 			snd_iprintf(buffer, "50/15us\n");
1981da177e4SLinus Torvalds 			break;
1991da177e4SLinus Torvalds 		case IEC958_AES0_PRO_EMPHASIS_NOTID:
2001da177e4SLinus Torvalds 		default:
2011da177e4SLinus Torvalds 			snd_iprintf(buffer, "unknown\n");
2021da177e4SLinus Torvalds 			break;
2031da177e4SLinus Torvalds 		}
2041da177e4SLinus Torvalds 		snd_iprintf(buffer, "Stereophonic: ");
2051da177e4SLinus Torvalds 		if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) {
2061da177e4SLinus Torvalds 			snd_iprintf(buffer, "stereo\n");
2071da177e4SLinus Torvalds 		} else {
2081da177e4SLinus Torvalds 			snd_iprintf(buffer, "not indicated\n");
2091da177e4SLinus Torvalds 		}
2101da177e4SLinus Torvalds 		snd_iprintf(buffer, "Userbits: ");
2111da177e4SLinus Torvalds 		switch (status[1] & IEC958_AES1_PRO_USERBITS) {
2121da177e4SLinus Torvalds 		case IEC958_AES1_PRO_USERBITS_192:
2131da177e4SLinus Torvalds 			snd_iprintf(buffer, "192bit\n");
2141da177e4SLinus Torvalds 			break;
2151da177e4SLinus Torvalds 		case IEC958_AES1_PRO_USERBITS_UDEF:
2161da177e4SLinus Torvalds 			snd_iprintf(buffer, "user-defined\n");
2171da177e4SLinus Torvalds 			break;
2181da177e4SLinus Torvalds 		default:
219af901ca1SAndré Goddard Rosa 			snd_iprintf(buffer, "unknown\n");
2201da177e4SLinus Torvalds 			break;
2211da177e4SLinus Torvalds 		}
2221da177e4SLinus Torvalds 		snd_iprintf(buffer, "Sample Bits: ");
2231da177e4SLinus Torvalds 		switch (status[2] & IEC958_AES2_PRO_SBITS) {
2241da177e4SLinus Torvalds 		case IEC958_AES2_PRO_SBITS_20:
2251da177e4SLinus Torvalds 			snd_iprintf(buffer, "20 bit\n");
2261da177e4SLinus Torvalds 			break;
2271da177e4SLinus Torvalds 		case IEC958_AES2_PRO_SBITS_24:
2281da177e4SLinus Torvalds 			snd_iprintf(buffer, "24 bit\n");
2291da177e4SLinus Torvalds 			break;
2301da177e4SLinus Torvalds 		case IEC958_AES2_PRO_SBITS_UDEF:
2311da177e4SLinus Torvalds 			snd_iprintf(buffer, "user defined\n");
2321da177e4SLinus Torvalds 			break;
2331da177e4SLinus Torvalds 		default:
2341da177e4SLinus Torvalds 			snd_iprintf(buffer, "unknown\n");
2351da177e4SLinus Torvalds 			break;
2361da177e4SLinus Torvalds 		}
2371da177e4SLinus Torvalds 		snd_iprintf(buffer, "Word Length: ");
2381da177e4SLinus Torvalds 		switch (status[2] & IEC958_AES2_PRO_WORDLEN) {
2391da177e4SLinus Torvalds 		case IEC958_AES2_PRO_WORDLEN_22_18:
2401da177e4SLinus Torvalds 			snd_iprintf(buffer, "22 bit or 18 bit\n");
2411da177e4SLinus Torvalds 			break;
2421da177e4SLinus Torvalds 		case IEC958_AES2_PRO_WORDLEN_23_19:
2431da177e4SLinus Torvalds 			snd_iprintf(buffer, "23 bit or 19 bit\n");
2441da177e4SLinus Torvalds 			break;
2451da177e4SLinus Torvalds 		case IEC958_AES2_PRO_WORDLEN_24_20:
2461da177e4SLinus Torvalds 			snd_iprintf(buffer, "24 bit or 20 bit\n");
2471da177e4SLinus Torvalds 			break;
2481da177e4SLinus Torvalds 		case IEC958_AES2_PRO_WORDLEN_20_16:
2491da177e4SLinus Torvalds 			snd_iprintf(buffer, "20 bit or 16 bit\n");
2501da177e4SLinus Torvalds 			break;
2511da177e4SLinus Torvalds 		default:
2521da177e4SLinus Torvalds 			snd_iprintf(buffer, "unknown\n");
2531da177e4SLinus Torvalds 			break;
2541da177e4SLinus Torvalds 		}
2551da177e4SLinus Torvalds 	}
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
258e4a3d145STakashi Iwai static void snd_ca0106_proc_iec958(struct snd_info_entry *entry,
259e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
2601da177e4SLinus Torvalds {
261e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
2621da177e4SLinus Torvalds 	u32 value;
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds         value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0);
2651da177e4SLinus Torvalds 	snd_iprintf(buffer, "Status: %s, %s, %s\n",
2661da177e4SLinus Torvalds 		  (value & 0x100000) ? "Rate Locked" : "Not Rate Locked",
2671da177e4SLinus Torvalds 		  (value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock",
2681da177e4SLinus Torvalds 		  (value & 0x400000) ? "Audio Valid" : "No valid audio" );
2691da177e4SLinus Torvalds 	snd_iprintf(buffer, "Estimated sample rate: %u\n",
2701da177e4SLinus Torvalds 		  ((value & 0xfffff) * 48000) / 0x8000 );
2711da177e4SLinus Torvalds 	if (value & 0x200000) {
2721da177e4SLinus Torvalds 		snd_iprintf(buffer, "IEC958/SPDIF input status:\n");
2731da177e4SLinus Torvalds         	value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0);
2741da177e4SLinus Torvalds 		snd_ca0106_proc_dump_iec958(buffer, value);
2751da177e4SLinus Torvalds 	}
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	snd_iprintf(buffer, "\n");
2781da177e4SLinus Torvalds }
2791da177e4SLinus Torvalds 
280e4a3d145STakashi Iwai static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry,
281e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
2821da177e4SLinus Torvalds {
283e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
2841da177e4SLinus Torvalds 	unsigned long flags;
2851da177e4SLinus Torvalds         char line[64];
2861da177e4SLinus Torvalds         u32 reg, val;
2871da177e4SLinus Torvalds         while (!snd_info_get_line(buffer, line, sizeof(line))) {
2881da177e4SLinus Torvalds                 if (sscanf(line, "%x %x", &reg, &val) != 2)
2891da177e4SLinus Torvalds                         continue;
29084ed1a19SRoel Kluin 		if (reg < 0x40 && val <= 0xffffffff) {
2911da177e4SLinus Torvalds 			spin_lock_irqsave(&emu->emu_lock, flags);
2921da177e4SLinus Torvalds 			outl(val, emu->port + (reg & 0xfffffffc));
2931da177e4SLinus Torvalds 			spin_unlock_irqrestore(&emu->emu_lock, flags);
2941da177e4SLinus Torvalds 		}
2951da177e4SLinus Torvalds         }
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
298e4a3d145STakashi Iwai static void snd_ca0106_proc_reg_read32(struct snd_info_entry *entry,
299e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
3001da177e4SLinus Torvalds {
301e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
3021da177e4SLinus Torvalds 	unsigned long value;
3031da177e4SLinus Torvalds 	unsigned long flags;
3041da177e4SLinus Torvalds 	int i;
3051da177e4SLinus Torvalds 	snd_iprintf(buffer, "Registers:\n\n");
3061da177e4SLinus Torvalds 	for(i = 0; i < 0x20; i+=4) {
3071da177e4SLinus Torvalds 		spin_lock_irqsave(&emu->emu_lock, flags);
3081da177e4SLinus Torvalds 		value = inl(emu->port + i);
3091da177e4SLinus Torvalds 		spin_unlock_irqrestore(&emu->emu_lock, flags);
3101da177e4SLinus Torvalds 		snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
3111da177e4SLinus Torvalds 	}
3121da177e4SLinus Torvalds }
3131da177e4SLinus Torvalds 
314e4a3d145STakashi Iwai static void snd_ca0106_proc_reg_read16(struct snd_info_entry *entry,
315e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
3161da177e4SLinus Torvalds {
317e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
3181da177e4SLinus Torvalds         unsigned int value;
3191da177e4SLinus Torvalds 	unsigned long flags;
3201da177e4SLinus Torvalds 	int i;
3211da177e4SLinus Torvalds 	snd_iprintf(buffer, "Registers:\n\n");
3221da177e4SLinus Torvalds 	for(i = 0; i < 0x20; i+=2) {
3231da177e4SLinus Torvalds 		spin_lock_irqsave(&emu->emu_lock, flags);
3241da177e4SLinus Torvalds 		value = inw(emu->port + i);
3251da177e4SLinus Torvalds 		spin_unlock_irqrestore(&emu->emu_lock, flags);
3261da177e4SLinus Torvalds 		snd_iprintf(buffer, "Register %02X: %04X\n", i, value);
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds 
330e4a3d145STakashi Iwai static void snd_ca0106_proc_reg_read8(struct snd_info_entry *entry,
331e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
3321da177e4SLinus Torvalds {
333e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
3341da177e4SLinus Torvalds 	unsigned int value;
3351da177e4SLinus Torvalds 	unsigned long flags;
3361da177e4SLinus Torvalds 	int i;
3371da177e4SLinus Torvalds 	snd_iprintf(buffer, "Registers:\n\n");
3381da177e4SLinus Torvalds 	for(i = 0; i < 0x20; i+=1) {
3391da177e4SLinus Torvalds 		spin_lock_irqsave(&emu->emu_lock, flags);
3401da177e4SLinus Torvalds 		value = inb(emu->port + i);
3411da177e4SLinus Torvalds 		spin_unlock_irqrestore(&emu->emu_lock, flags);
3421da177e4SLinus Torvalds 		snd_iprintf(buffer, "Register %02X: %02X\n", i, value);
3431da177e4SLinus Torvalds 	}
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds 
346e4a3d145STakashi Iwai static void snd_ca0106_proc_reg_read1(struct snd_info_entry *entry,
347e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
3481da177e4SLinus Torvalds {
349e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
3501da177e4SLinus Torvalds 	unsigned long value;
3511da177e4SLinus Torvalds 	int i,j;
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	snd_iprintf(buffer, "Registers\n");
3541da177e4SLinus Torvalds 	for(i = 0; i < 0x40; i++) {
3551da177e4SLinus Torvalds 		snd_iprintf(buffer, "%02X: ",i);
3561da177e4SLinus Torvalds 		for (j = 0; j < 4; j++) {
3571da177e4SLinus Torvalds                   value = snd_ca0106_ptr_read(emu, i, j);
3581da177e4SLinus Torvalds 		  snd_iprintf(buffer, "%08lX ", value);
3591da177e4SLinus Torvalds                 }
3601da177e4SLinus Torvalds 	        snd_iprintf(buffer, "\n");
3611da177e4SLinus Torvalds 	}
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds 
364e4a3d145STakashi Iwai static void snd_ca0106_proc_reg_read2(struct snd_info_entry *entry,
365e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
3661da177e4SLinus Torvalds {
367e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
3681da177e4SLinus Torvalds 	unsigned long value;
3691da177e4SLinus Torvalds 	int i,j;
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	snd_iprintf(buffer, "Registers\n");
3721da177e4SLinus Torvalds 	for(i = 0x40; i < 0x80; i++) {
3731da177e4SLinus Torvalds 		snd_iprintf(buffer, "%02X: ",i);
3741da177e4SLinus Torvalds 		for (j = 0; j < 4; j++) {
3751da177e4SLinus Torvalds                   value = snd_ca0106_ptr_read(emu, i, j);
3761da177e4SLinus Torvalds 		  snd_iprintf(buffer, "%08lX ", value);
3771da177e4SLinus Torvalds                 }
3781da177e4SLinus Torvalds 	        snd_iprintf(buffer, "\n");
3791da177e4SLinus Torvalds 	}
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds 
382e4a3d145STakashi Iwai static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry,
383e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
3841da177e4SLinus Torvalds {
385e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
3861da177e4SLinus Torvalds         char line[64];
3871da177e4SLinus Torvalds         unsigned int reg, channel_id , val;
3881da177e4SLinus Torvalds         while (!snd_info_get_line(buffer, line, sizeof(line))) {
3891da177e4SLinus Torvalds                 if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
3901da177e4SLinus Torvalds                         continue;
39184ed1a19SRoel Kluin 		if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3)
3921da177e4SLinus Torvalds                         snd_ca0106_ptr_write(emu, reg, channel_id, val);
3931da177e4SLinus Torvalds         }
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds 
396e4a3d145STakashi Iwai static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry,
397e4a3d145STakashi Iwai 				       struct snd_info_buffer *buffer)
3987199acdcSJames Courtier-Dutton {
399e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = entry->private_data;
4007199acdcSJames Courtier-Dutton         char line[64];
4017199acdcSJames Courtier-Dutton         unsigned int reg, val;
4027199acdcSJames Courtier-Dutton         while (!snd_info_get_line(buffer, line, sizeof(line))) {
4037199acdcSJames Courtier-Dutton                 if (sscanf(line, "%x %x", &reg, &val) != 2)
4047199acdcSJames Courtier-Dutton                         continue;
4057199acdcSJames Courtier-Dutton                 if ((reg <= 0x7f) || (val <= 0x1ff)) {
4067199acdcSJames Courtier-Dutton                         snd_ca0106_i2c_write(emu, reg, val);
4077199acdcSJames Courtier-Dutton 		}
4087199acdcSJames Courtier-Dutton         }
4097199acdcSJames Courtier-Dutton }
4101da177e4SLinus Torvalds 
411e23e7a14SBill Pemberton int snd_ca0106_proc_init(struct snd_ca0106 *emu)
4121da177e4SLinus Torvalds {
41347f2769bSTakashi Iwai 	snd_card_ro_proc_new(emu->card, "iec958", emu, snd_ca0106_proc_iec958);
41447f2769bSTakashi Iwai 	snd_card_rw_proc_new(emu->card, "ca0106_reg32", emu,
41547f2769bSTakashi Iwai 			     snd_ca0106_proc_reg_read32,
41647f2769bSTakashi Iwai 			     snd_ca0106_proc_reg_write32);
41747f2769bSTakashi Iwai 	snd_card_ro_proc_new(emu->card, "ca0106_reg16", emu,
41847f2769bSTakashi Iwai 			     snd_ca0106_proc_reg_read16);
41947f2769bSTakashi Iwai 	snd_card_ro_proc_new(emu->card, "ca0106_reg8", emu,
42047f2769bSTakashi Iwai 			     snd_ca0106_proc_reg_read8);
42147f2769bSTakashi Iwai 	snd_card_rw_proc_new(emu->card, "ca0106_regs1", emu,
42247f2769bSTakashi Iwai 			     snd_ca0106_proc_reg_read1,
42347f2769bSTakashi Iwai 			     snd_ca0106_proc_reg_write);
42447f2769bSTakashi Iwai 	snd_card_rw_proc_new(emu->card, "ca0106_i2c", emu, NULL,
42547f2769bSTakashi Iwai 			     snd_ca0106_proc_i2c_write);
42647f2769bSTakashi Iwai 	snd_card_ro_proc_new(emu->card, "ca0106_regs2", emu,
42747f2769bSTakashi Iwai 			     snd_ca0106_proc_reg_read2);
4281da177e4SLinus Torvalds 	return 0;
4291da177e4SLinus Torvalds }
430