18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> 48c2ecf20Sopenharmony_ci * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit 58c2ecf20Sopenharmony_ci * Version: 0.0.18 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * FEATURES currently supported: 88c2ecf20Sopenharmony_ci * See ca0106_main.c for features. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Changelog: 118c2ecf20Sopenharmony_ci * Support interrupts per period. 128c2ecf20Sopenharmony_ci * Removed noise from Center/LFE channel when in Analog mode. 138c2ecf20Sopenharmony_ci * Rename and remove mixer controls. 148c2ecf20Sopenharmony_ci * 0.0.6 158c2ecf20Sopenharmony_ci * Use separate card based DMA buffer for periods table list. 168c2ecf20Sopenharmony_ci * 0.0.7 178c2ecf20Sopenharmony_ci * Change remove and rename ctrls into lists. 188c2ecf20Sopenharmony_ci * 0.0.8 198c2ecf20Sopenharmony_ci * Try to fix capture sources. 208c2ecf20Sopenharmony_ci * 0.0.9 218c2ecf20Sopenharmony_ci * Fix AC3 output. 228c2ecf20Sopenharmony_ci * Enable S32_LE format support. 238c2ecf20Sopenharmony_ci * 0.0.10 248c2ecf20Sopenharmony_ci * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) 258c2ecf20Sopenharmony_ci * 0.0.11 268c2ecf20Sopenharmony_ci * Add Model name recognition. 278c2ecf20Sopenharmony_ci * 0.0.12 288c2ecf20Sopenharmony_ci * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. 298c2ecf20Sopenharmony_ci * Remove redundent "voice" handling. 308c2ecf20Sopenharmony_ci * 0.0.13 318c2ecf20Sopenharmony_ci * Single trigger call for multi channels. 328c2ecf20Sopenharmony_ci * 0.0.14 338c2ecf20Sopenharmony_ci * Set limits based on what the sound card hardware can do. 348c2ecf20Sopenharmony_ci * playback periods_min=2, periods_max=8 358c2ecf20Sopenharmony_ci * capture hw constraints require period_size = n * 64 bytes. 368c2ecf20Sopenharmony_ci * playback hw constraints require period_size = n * 64 bytes. 378c2ecf20Sopenharmony_ci * 0.0.15 388c2ecf20Sopenharmony_ci * Separate ca0106.c into separate functional .c files. 398c2ecf20Sopenharmony_ci * 0.0.16 408c2ecf20Sopenharmony_ci * Modified Copyright message. 418c2ecf20Sopenharmony_ci * 0.0.17 428c2ecf20Sopenharmony_ci * Add iec958 file in proc file system to show status of SPDIF in. 438c2ecf20Sopenharmony_ci * 0.0.18 448c2ecf20Sopenharmony_ci * Implement support for Line-in capture on SB Live 24bit. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * This code was initially based on code from ALSA's emu10k1x.c which is: 478c2ecf20Sopenharmony_ci * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci#include <linux/delay.h> 508c2ecf20Sopenharmony_ci#include <linux/init.h> 518c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 528c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 538c2ecf20Sopenharmony_ci#include <linux/io.h> 548c2ecf20Sopenharmony_ci#include <sound/core.h> 558c2ecf20Sopenharmony_ci#include <sound/initval.h> 568c2ecf20Sopenharmony_ci#include <sound/pcm.h> 578c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 588c2ecf20Sopenharmony_ci#include <sound/info.h> 598c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#include "ca0106.h" 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct snd_ca0106_category_str { 658c2ecf20Sopenharmony_ci int val; 668c2ecf20Sopenharmony_ci const char *name; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic const struct snd_ca0106_category_str snd_ca0106_con_category[] = { 708c2ecf20Sopenharmony_ci { IEC958_AES1_CON_DAT, "DAT" }, 718c2ecf20Sopenharmony_ci { IEC958_AES1_CON_VCR, "VCR" }, 728c2ecf20Sopenharmony_ci { IEC958_AES1_CON_MICROPHONE, "microphone" }, 738c2ecf20Sopenharmony_ci { IEC958_AES1_CON_SYNTHESIZER, "synthesizer" }, 748c2ecf20Sopenharmony_ci { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" }, 758c2ecf20Sopenharmony_ci { IEC958_AES1_CON_MIXER, "mixer" }, 768c2ecf20Sopenharmony_ci { IEC958_AES1_CON_SAMPLER, "sampler" }, 778c2ecf20Sopenharmony_ci { IEC958_AES1_CON_PCM_CODER, "PCM coder" }, 788c2ecf20Sopenharmony_ci { IEC958_AES1_CON_IEC908_CD, "CD" }, 798c2ecf20Sopenharmony_ci { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" }, 808c2ecf20Sopenharmony_ci { IEC958_AES1_CON_GENERAL, "general" }, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 value) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int i; 878c2ecf20Sopenharmony_ci u32 status[4]; 888c2ecf20Sopenharmony_ci status[0] = value & 0xff; 898c2ecf20Sopenharmony_ci status[1] = (value >> 8) & 0xff; 908c2ecf20Sopenharmony_ci status[2] = (value >> 16) & 0xff; 918c2ecf20Sopenharmony_ci status[3] = (value >> 24) & 0xff; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (! (status[0] & IEC958_AES0_PROFESSIONAL)) { 948c2ecf20Sopenharmony_ci /* consumer */ 958c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Mode: consumer\n"); 968c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Data: "); 978c2ecf20Sopenharmony_ci if (!(status[0] & IEC958_AES0_NONAUDIO)) { 988c2ecf20Sopenharmony_ci snd_iprintf(buffer, "audio\n"); 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci snd_iprintf(buffer, "non-audio\n"); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Rate: "); 1038c2ecf20Sopenharmony_ci switch (status[3] & IEC958_AES3_CON_FS) { 1048c2ecf20Sopenharmony_ci case IEC958_AES3_CON_FS_44100: 1058c2ecf20Sopenharmony_ci snd_iprintf(buffer, "44100 Hz\n"); 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci case IEC958_AES3_CON_FS_48000: 1088c2ecf20Sopenharmony_ci snd_iprintf(buffer, "48000 Hz\n"); 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci case IEC958_AES3_CON_FS_32000: 1118c2ecf20Sopenharmony_ci snd_iprintf(buffer, "32000 Hz\n"); 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci default: 1148c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown\n"); 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Copyright: "); 1188c2ecf20Sopenharmony_ci if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) { 1198c2ecf20Sopenharmony_ci snd_iprintf(buffer, "permitted\n"); 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci snd_iprintf(buffer, "protected\n"); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Emphasis: "); 1248c2ecf20Sopenharmony_ci if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) { 1258c2ecf20Sopenharmony_ci snd_iprintf(buffer, "none\n"); 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci snd_iprintf(buffer, "50/15us\n"); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Category: "); 1308c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) { 1318c2ecf20Sopenharmony_ci if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) { 1328c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(snd_ca0106_con_category)) { 1378c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Original: "); 1408c2ecf20Sopenharmony_ci if (status[1] & IEC958_AES1_CON_ORIGINAL) { 1418c2ecf20Sopenharmony_ci snd_iprintf(buffer, "original\n"); 1428c2ecf20Sopenharmony_ci } else { 1438c2ecf20Sopenharmony_ci snd_iprintf(buffer, "1st generation\n"); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Clock: "); 1468c2ecf20Sopenharmony_ci switch (status[3] & IEC958_AES3_CON_CLOCK) { 1478c2ecf20Sopenharmony_ci case IEC958_AES3_CON_CLOCK_1000PPM: 1488c2ecf20Sopenharmony_ci snd_iprintf(buffer, "1000 ppm\n"); 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case IEC958_AES3_CON_CLOCK_50PPM: 1518c2ecf20Sopenharmony_ci snd_iprintf(buffer, "50 ppm\n"); 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case IEC958_AES3_CON_CLOCK_VARIABLE: 1548c2ecf20Sopenharmony_ci snd_iprintf(buffer, "variable pitch\n"); 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown\n"); 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } else { 1618c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Mode: professional\n"); 1628c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Data: "); 1638c2ecf20Sopenharmony_ci if (!(status[0] & IEC958_AES0_NONAUDIO)) { 1648c2ecf20Sopenharmony_ci snd_iprintf(buffer, "audio\n"); 1658c2ecf20Sopenharmony_ci } else { 1668c2ecf20Sopenharmony_ci snd_iprintf(buffer, "non-audio\n"); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Rate: "); 1698c2ecf20Sopenharmony_ci switch (status[0] & IEC958_AES0_PRO_FS) { 1708c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_FS_44100: 1718c2ecf20Sopenharmony_ci snd_iprintf(buffer, "44100 Hz\n"); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_FS_48000: 1748c2ecf20Sopenharmony_ci snd_iprintf(buffer, "48000 Hz\n"); 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_FS_32000: 1778c2ecf20Sopenharmony_ci snd_iprintf(buffer, "32000 Hz\n"); 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci default: 1808c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown\n"); 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Rate Locked: "); 1848c2ecf20Sopenharmony_ci if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED) 1858c2ecf20Sopenharmony_ci snd_iprintf(buffer, "no\n"); 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci snd_iprintf(buffer, "yes\n"); 1888c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Emphasis: "); 1898c2ecf20Sopenharmony_ci switch (status[0] & IEC958_AES0_PRO_EMPHASIS) { 1908c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_EMPHASIS_CCITT: 1918c2ecf20Sopenharmony_ci snd_iprintf(buffer, "CCITT J.17\n"); 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_EMPHASIS_NONE: 1948c2ecf20Sopenharmony_ci snd_iprintf(buffer, "none\n"); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_EMPHASIS_5015: 1978c2ecf20Sopenharmony_ci snd_iprintf(buffer, "50/15us\n"); 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_EMPHASIS_NOTID: 2008c2ecf20Sopenharmony_ci default: 2018c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown\n"); 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Stereophonic: "); 2058c2ecf20Sopenharmony_ci if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) { 2068c2ecf20Sopenharmony_ci snd_iprintf(buffer, "stereo\n"); 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci snd_iprintf(buffer, "not indicated\n"); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Userbits: "); 2118c2ecf20Sopenharmony_ci switch (status[1] & IEC958_AES1_PRO_USERBITS) { 2128c2ecf20Sopenharmony_ci case IEC958_AES1_PRO_USERBITS_192: 2138c2ecf20Sopenharmony_ci snd_iprintf(buffer, "192bit\n"); 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case IEC958_AES1_PRO_USERBITS_UDEF: 2168c2ecf20Sopenharmony_ci snd_iprintf(buffer, "user-defined\n"); 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci default: 2198c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown\n"); 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Sample Bits: "); 2238c2ecf20Sopenharmony_ci switch (status[2] & IEC958_AES2_PRO_SBITS) { 2248c2ecf20Sopenharmony_ci case IEC958_AES2_PRO_SBITS_20: 2258c2ecf20Sopenharmony_ci snd_iprintf(buffer, "20 bit\n"); 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci case IEC958_AES2_PRO_SBITS_24: 2288c2ecf20Sopenharmony_ci snd_iprintf(buffer, "24 bit\n"); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case IEC958_AES2_PRO_SBITS_UDEF: 2318c2ecf20Sopenharmony_ci snd_iprintf(buffer, "user defined\n"); 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci default: 2348c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown\n"); 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Word Length: "); 2388c2ecf20Sopenharmony_ci switch (status[2] & IEC958_AES2_PRO_WORDLEN) { 2398c2ecf20Sopenharmony_ci case IEC958_AES2_PRO_WORDLEN_22_18: 2408c2ecf20Sopenharmony_ci snd_iprintf(buffer, "22 bit or 18 bit\n"); 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case IEC958_AES2_PRO_WORDLEN_23_19: 2438c2ecf20Sopenharmony_ci snd_iprintf(buffer, "23 bit or 19 bit\n"); 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case IEC958_AES2_PRO_WORDLEN_24_20: 2468c2ecf20Sopenharmony_ci snd_iprintf(buffer, "24 bit or 20 bit\n"); 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci case IEC958_AES2_PRO_WORDLEN_20_16: 2498c2ecf20Sopenharmony_ci snd_iprintf(buffer, "20 bit or 16 bit\n"); 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown\n"); 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_iec958(struct snd_info_entry *entry, 2598c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 2628c2ecf20Sopenharmony_ci u32 value; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0); 2658c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Status: %s, %s, %s\n", 2668c2ecf20Sopenharmony_ci (value & 0x100000) ? "Rate Locked" : "Not Rate Locked", 2678c2ecf20Sopenharmony_ci (value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock", 2688c2ecf20Sopenharmony_ci (value & 0x400000) ? "Audio Valid" : "No valid audio" ); 2698c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Estimated sample rate: %u\n", 2708c2ecf20Sopenharmony_ci ((value & 0xfffff) * 48000) / 0x8000 ); 2718c2ecf20Sopenharmony_ci if (value & 0x200000) { 2728c2ecf20Sopenharmony_ci snd_iprintf(buffer, "IEC958/SPDIF input status:\n"); 2738c2ecf20Sopenharmony_ci value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0); 2748c2ecf20Sopenharmony_ci snd_ca0106_proc_dump_iec958(buffer, value); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry, 2818c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 2848c2ecf20Sopenharmony_ci unsigned long flags; 2858c2ecf20Sopenharmony_ci char line[64]; 2868c2ecf20Sopenharmony_ci u32 reg, val; 2878c2ecf20Sopenharmony_ci while (!snd_info_get_line(buffer, line, sizeof(line))) { 2888c2ecf20Sopenharmony_ci if (sscanf(line, "%x %x", ®, &val) != 2) 2898c2ecf20Sopenharmony_ci continue; 2908c2ecf20Sopenharmony_ci if (reg < 0x40 && val <= 0xffffffff) { 2918c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 2928c2ecf20Sopenharmony_ci outl(val, emu->port + (reg & 0xfffffffc)); 2938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_reg_read32(struct snd_info_entry *entry, 2998c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 3028c2ecf20Sopenharmony_ci unsigned long value; 3038c2ecf20Sopenharmony_ci unsigned long flags; 3048c2ecf20Sopenharmony_ci int i; 3058c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Registers:\n\n"); 3068c2ecf20Sopenharmony_ci for(i = 0; i < 0x20; i+=4) { 3078c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3088c2ecf20Sopenharmony_ci value = inl(emu->port + i); 3098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3108c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Register %02X: %08lX\n", i, value); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_reg_read16(struct snd_info_entry *entry, 3158c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 3188c2ecf20Sopenharmony_ci unsigned int value; 3198c2ecf20Sopenharmony_ci unsigned long flags; 3208c2ecf20Sopenharmony_ci int i; 3218c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Registers:\n\n"); 3228c2ecf20Sopenharmony_ci for(i = 0; i < 0x20; i+=2) { 3238c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3248c2ecf20Sopenharmony_ci value = inw(emu->port + i); 3258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3268c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Register %02X: %04X\n", i, value); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_reg_read8(struct snd_info_entry *entry, 3318c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 3348c2ecf20Sopenharmony_ci unsigned int value; 3358c2ecf20Sopenharmony_ci unsigned long flags; 3368c2ecf20Sopenharmony_ci int i; 3378c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Registers:\n\n"); 3388c2ecf20Sopenharmony_ci for(i = 0; i < 0x20; i+=1) { 3398c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3408c2ecf20Sopenharmony_ci value = inb(emu->port + i); 3418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3428c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Register %02X: %02X\n", i, value); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_reg_read1(struct snd_info_entry *entry, 3478c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 3508c2ecf20Sopenharmony_ci unsigned long value; 3518c2ecf20Sopenharmony_ci int i,j; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Registers\n"); 3548c2ecf20Sopenharmony_ci for(i = 0; i < 0x40; i++) { 3558c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%02X: ",i); 3568c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 3578c2ecf20Sopenharmony_ci value = snd_ca0106_ptr_read(emu, i, j); 3588c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%08lX ", value); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_reg_read2(struct snd_info_entry *entry, 3658c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 3688c2ecf20Sopenharmony_ci unsigned long value; 3698c2ecf20Sopenharmony_ci int i,j; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Registers\n"); 3728c2ecf20Sopenharmony_ci for(i = 0x40; i < 0x80; i++) { 3738c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%02X: ",i); 3748c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 3758c2ecf20Sopenharmony_ci value = snd_ca0106_ptr_read(emu, i, j); 3768c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%08lX ", value); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_reg_write(struct snd_info_entry *entry, 3838c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 3868c2ecf20Sopenharmony_ci char line[64]; 3878c2ecf20Sopenharmony_ci unsigned int reg, channel_id , val; 3888c2ecf20Sopenharmony_ci while (!snd_info_get_line(buffer, line, sizeof(line))) { 3898c2ecf20Sopenharmony_ci if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) 3908c2ecf20Sopenharmony_ci continue; 3918c2ecf20Sopenharmony_ci if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3) 3928c2ecf20Sopenharmony_ci snd_ca0106_ptr_write(emu, reg, channel_id, val); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry, 3978c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct snd_ca0106 *emu = entry->private_data; 4008c2ecf20Sopenharmony_ci char line[64]; 4018c2ecf20Sopenharmony_ci unsigned int reg, val; 4028c2ecf20Sopenharmony_ci while (!snd_info_get_line(buffer, line, sizeof(line))) { 4038c2ecf20Sopenharmony_ci if (sscanf(line, "%x %x", ®, &val) != 2) 4048c2ecf20Sopenharmony_ci continue; 4058c2ecf20Sopenharmony_ci if ((reg <= 0x7f) || (val <= 0x1ff)) { 4068c2ecf20Sopenharmony_ci snd_ca0106_i2c_write(emu, reg, val); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ciint snd_ca0106_proc_init(struct snd_ca0106 *emu) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci snd_card_ro_proc_new(emu->card, "iec958", emu, snd_ca0106_proc_iec958); 4148c2ecf20Sopenharmony_ci snd_card_rw_proc_new(emu->card, "ca0106_reg32", emu, 4158c2ecf20Sopenharmony_ci snd_ca0106_proc_reg_read32, 4168c2ecf20Sopenharmony_ci snd_ca0106_proc_reg_write32); 4178c2ecf20Sopenharmony_ci snd_card_ro_proc_new(emu->card, "ca0106_reg16", emu, 4188c2ecf20Sopenharmony_ci snd_ca0106_proc_reg_read16); 4198c2ecf20Sopenharmony_ci snd_card_ro_proc_new(emu->card, "ca0106_reg8", emu, 4208c2ecf20Sopenharmony_ci snd_ca0106_proc_reg_read8); 4218c2ecf20Sopenharmony_ci snd_card_rw_proc_new(emu->card, "ca0106_regs1", emu, 4228c2ecf20Sopenharmony_ci snd_ca0106_proc_reg_read1, 4238c2ecf20Sopenharmony_ci snd_ca0106_proc_reg_write); 4248c2ecf20Sopenharmony_ci snd_card_rw_proc_new(emu->card, "ca0106_i2c", emu, NULL, 4258c2ecf20Sopenharmony_ci snd_ca0106_proc_i2c_write); 4268c2ecf20Sopenharmony_ci snd_card_ro_proc_new(emu->card, "ca0106_regs2", emu, 4278c2ecf20Sopenharmony_ci snd_ca0106_proc_reg_read2); 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 430