brintos

brintos / linux-shallow public Read only

0
0
Text · 12.9 KiB · c99603e Raw
430 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>4 *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit5 *  Version: 0.0.186 *7 *  FEATURES currently supported:8 *    See ca0106_main.c for features.9 * 10 *  Changelog:11 *    Support interrupts per period.12 *    Removed noise from Center/LFE channel when in Analog mode.13 *    Rename and remove mixer controls.14 *  0.0.615 *    Use separate card based DMA buffer for periods table list.16 *  0.0.717 *    Change remove and rename ctrls into lists.18 *  0.0.819 *    Try to fix capture sources.20 *  0.0.921 *    Fix AC3 output.22 *    Enable S32_LE format support.23 *  0.0.1024 *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)25 *  0.0.1126 *    Add Model name recognition.27 *  0.0.1228 *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.29 *    Remove redundent "voice" handling.30 *  0.0.1331 *    Single trigger call for multi channels.32 *  0.0.1433 *    Set limits based on what the sound card hardware can do.34 *    playback periods_min=2, periods_max=835 *    capture hw constraints require period_size = n * 64 bytes.36 *    playback hw constraints require period_size = n * 64 bytes.37 *  0.0.1538 *    Separate ca0106.c into separate functional .c files.39 *  0.0.1640 *    Modified Copyright message.41 *  0.0.1742 *    Add iec958 file in proc file system to show status of SPDIF in.43 *  0.0.1844 *    Implement support for Line-in capture on SB Live 24bit.45 *46 *  This code was initially based on code from ALSA's emu10k1x.c which is:47 *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>48 */49#include <linux/delay.h>50#include <linux/init.h>51#include <linux/interrupt.h>52#include <linux/moduleparam.h>53#include <linux/io.h>54#include <sound/core.h>55#include <sound/initval.h>56#include <sound/pcm.h>57#include <sound/ac97_codec.h>58#include <sound/info.h>59#include <sound/asoundef.h>60 61#include "ca0106.h"62 63 64struct snd_ca0106_category_str {65	int val;66	const char *name;67};68 69static const struct snd_ca0106_category_str snd_ca0106_con_category[] = {70	{ IEC958_AES1_CON_DAT, "DAT" },71	{ IEC958_AES1_CON_VCR, "VCR" },72	{ IEC958_AES1_CON_MICROPHONE, "microphone" },73	{ IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },74	{ IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },75	{ IEC958_AES1_CON_MIXER, "mixer" },76	{ IEC958_AES1_CON_SAMPLER, "sampler" },77	{ IEC958_AES1_CON_PCM_CODER, "PCM coder" },78	{ IEC958_AES1_CON_IEC908_CD, "CD" },79	{ IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },80	{ IEC958_AES1_CON_GENERAL, "general" },81};82 83 84static void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 value)85{86	int i;87	u32 status[4];88	status[0] = value & 0xff;89	status[1] = (value >> 8) & 0xff;90	status[2] = (value >> 16)  & 0xff;91	status[3] = (value >> 24)  & 0xff;92	93	if (! (status[0] & IEC958_AES0_PROFESSIONAL)) {94		/* consumer */95		snd_iprintf(buffer, "Mode: consumer\n");96		snd_iprintf(buffer, "Data: ");97		if (!(status[0] & IEC958_AES0_NONAUDIO)) {98			snd_iprintf(buffer, "audio\n");99		} else {100			snd_iprintf(buffer, "non-audio\n");101		}102		snd_iprintf(buffer, "Rate: ");103		switch (status[3] & IEC958_AES3_CON_FS) {104		case IEC958_AES3_CON_FS_44100:105			snd_iprintf(buffer, "44100 Hz\n");106			break;107		case IEC958_AES3_CON_FS_48000:108			snd_iprintf(buffer, "48000 Hz\n");109			break;110		case IEC958_AES3_CON_FS_32000:111			snd_iprintf(buffer, "32000 Hz\n");112			break;113		default:114			snd_iprintf(buffer, "unknown\n");115			break;116		}117		snd_iprintf(buffer, "Copyright: ");118		if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) {119			snd_iprintf(buffer, "permitted\n");120		} else {121			snd_iprintf(buffer, "protected\n");122		}123		snd_iprintf(buffer, "Emphasis: ");124		if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) {125			snd_iprintf(buffer, "none\n");126		} else {127			snd_iprintf(buffer, "50/15us\n");128		}129		snd_iprintf(buffer, "Category: ");130		for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) {131			if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) {132				snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name);133				break;134			}135		}136		if (i >= ARRAY_SIZE(snd_ca0106_con_category)) {137			snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY);138		}139		snd_iprintf(buffer, "Original: ");140		if (status[1] & IEC958_AES1_CON_ORIGINAL) {141			snd_iprintf(buffer, "original\n");142		} else {143			snd_iprintf(buffer, "1st generation\n");144		}145		snd_iprintf(buffer, "Clock: ");146		switch (status[3] & IEC958_AES3_CON_CLOCK) {147		case IEC958_AES3_CON_CLOCK_1000PPM:148			snd_iprintf(buffer, "1000 ppm\n");149			break;150		case IEC958_AES3_CON_CLOCK_50PPM:151			snd_iprintf(buffer, "50 ppm\n");152			break;153		case IEC958_AES3_CON_CLOCK_VARIABLE:154			snd_iprintf(buffer, "variable pitch\n");155			break;156		default:157			snd_iprintf(buffer, "unknown\n");158			break;159		}160	} else {161		snd_iprintf(buffer, "Mode: professional\n");162		snd_iprintf(buffer, "Data: ");163		if (!(status[0] & IEC958_AES0_NONAUDIO)) {164			snd_iprintf(buffer, "audio\n");165		} else {166			snd_iprintf(buffer, "non-audio\n");167		}168		snd_iprintf(buffer, "Rate: ");169		switch (status[0] & IEC958_AES0_PRO_FS) {170		case IEC958_AES0_PRO_FS_44100:171			snd_iprintf(buffer, "44100 Hz\n");172			break;173		case IEC958_AES0_PRO_FS_48000:174			snd_iprintf(buffer, "48000 Hz\n");175			break;176		case IEC958_AES0_PRO_FS_32000:177			snd_iprintf(buffer, "32000 Hz\n");178			break;179		default:180			snd_iprintf(buffer, "unknown\n");181			break;182		}183		snd_iprintf(buffer, "Rate Locked: ");184		if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED)185			snd_iprintf(buffer, "no\n");186		else187			snd_iprintf(buffer, "yes\n");188		snd_iprintf(buffer, "Emphasis: ");189		switch (status[0] & IEC958_AES0_PRO_EMPHASIS) {190		case IEC958_AES0_PRO_EMPHASIS_CCITT:191			snd_iprintf(buffer, "CCITT J.17\n");192			break;193		case IEC958_AES0_PRO_EMPHASIS_NONE:194			snd_iprintf(buffer, "none\n");195			break;196		case IEC958_AES0_PRO_EMPHASIS_5015:197			snd_iprintf(buffer, "50/15us\n");198			break;199		case IEC958_AES0_PRO_EMPHASIS_NOTID:200		default:201			snd_iprintf(buffer, "unknown\n");202			break;203		}204		snd_iprintf(buffer, "Stereophonic: ");205		if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) {206			snd_iprintf(buffer, "stereo\n");207		} else {208			snd_iprintf(buffer, "not indicated\n");209		}210		snd_iprintf(buffer, "Userbits: ");211		switch (status[1] & IEC958_AES1_PRO_USERBITS) {212		case IEC958_AES1_PRO_USERBITS_192:213			snd_iprintf(buffer, "192bit\n");214			break;215		case IEC958_AES1_PRO_USERBITS_UDEF:216			snd_iprintf(buffer, "user-defined\n");217			break;218		default:219			snd_iprintf(buffer, "unknown\n");220			break;221		}222		snd_iprintf(buffer, "Sample Bits: ");223		switch (status[2] & IEC958_AES2_PRO_SBITS) {224		case IEC958_AES2_PRO_SBITS_20:225			snd_iprintf(buffer, "20 bit\n");226			break;227		case IEC958_AES2_PRO_SBITS_24:228			snd_iprintf(buffer, "24 bit\n");229			break;230		case IEC958_AES2_PRO_SBITS_UDEF:231			snd_iprintf(buffer, "user defined\n");232			break;233		default:234			snd_iprintf(buffer, "unknown\n");235			break;236		}237		snd_iprintf(buffer, "Word Length: ");238		switch (status[2] & IEC958_AES2_PRO_WORDLEN) {239		case IEC958_AES2_PRO_WORDLEN_22_18:240			snd_iprintf(buffer, "22 bit or 18 bit\n");241			break;242		case IEC958_AES2_PRO_WORDLEN_23_19:243			snd_iprintf(buffer, "23 bit or 19 bit\n");244			break;245		case IEC958_AES2_PRO_WORDLEN_24_20:246			snd_iprintf(buffer, "24 bit or 20 bit\n");247			break;248		case IEC958_AES2_PRO_WORDLEN_20_16:249			snd_iprintf(buffer, "20 bit or 16 bit\n");250			break;251		default:252			snd_iprintf(buffer, "unknown\n");253			break;254		}255	}256}257 258static void snd_ca0106_proc_iec958(struct snd_info_entry *entry, 259				       struct snd_info_buffer *buffer)260{261	struct snd_ca0106 *emu = entry->private_data;262	u32 value;263 264        value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0);265	snd_iprintf(buffer, "Status: %s, %s, %s\n",266		  (value & 0x100000) ? "Rate Locked" : "Not Rate Locked",267		  (value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock",268		  (value & 0x400000) ? "Audio Valid" : "No valid audio" );269	snd_iprintf(buffer, "Estimated sample rate: %u\n", 270		  ((value & 0xfffff) * 48000) / 0x8000 );271	if (value & 0x200000) {272		snd_iprintf(buffer, "IEC958/SPDIF input status:\n");273        	value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0);274		snd_ca0106_proc_dump_iec958(buffer, value);275	}276 277	snd_iprintf(buffer, "\n");278}279 280static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry, 281				       struct snd_info_buffer *buffer)282{283	struct snd_ca0106 *emu = entry->private_data;284	unsigned long flags;285        char line[64];286        u32 reg, val;287        while (!snd_info_get_line(buffer, line, sizeof(line))) {288                if (sscanf(line, "%x %x", &reg, &val) != 2)289                        continue;290		if (reg < 0x40 && val <= 0xffffffff) {291			spin_lock_irqsave(&emu->emu_lock, flags);292			outl(val, emu->port + (reg & 0xfffffffc));293			spin_unlock_irqrestore(&emu->emu_lock, flags);294		}295        }296}297 298static void snd_ca0106_proc_reg_read32(struct snd_info_entry *entry, 299				       struct snd_info_buffer *buffer)300{301	struct snd_ca0106 *emu = entry->private_data;302	unsigned long value;303	unsigned long flags;304	int i;305	snd_iprintf(buffer, "Registers:\n\n");306	for(i = 0; i < 0x20; i+=4) {307		spin_lock_irqsave(&emu->emu_lock, flags);308		value = inl(emu->port + i);309		spin_unlock_irqrestore(&emu->emu_lock, flags);310		snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);311	}312}313 314static void snd_ca0106_proc_reg_read16(struct snd_info_entry *entry, 315				       struct snd_info_buffer *buffer)316{317	struct snd_ca0106 *emu = entry->private_data;318        unsigned int value;319	unsigned long flags;320	int i;321	snd_iprintf(buffer, "Registers:\n\n");322	for(i = 0; i < 0x20; i+=2) {323		spin_lock_irqsave(&emu->emu_lock, flags);324		value = inw(emu->port + i);325		spin_unlock_irqrestore(&emu->emu_lock, flags);326		snd_iprintf(buffer, "Register %02X: %04X\n", i, value);327	}328}329 330static void snd_ca0106_proc_reg_read8(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+=1) {339		spin_lock_irqsave(&emu->emu_lock, flags);340		value = inb(emu->port + i);341		spin_unlock_irqrestore(&emu->emu_lock, flags);342		snd_iprintf(buffer, "Register %02X: %02X\n", i, value);343	}344}345 346static void snd_ca0106_proc_reg_read1(struct snd_info_entry *entry, 347				       struct snd_info_buffer *buffer)348{349	struct snd_ca0106 *emu = entry->private_data;350	unsigned long value;351	int i,j;352 353	snd_iprintf(buffer, "Registers\n");354	for(i = 0; i < 0x40; i++) {355		snd_iprintf(buffer, "%02X: ",i);356		for (j = 0; j < 4; j++) {357                  value = snd_ca0106_ptr_read(emu, i, j);358		  snd_iprintf(buffer, "%08lX ", value);359                }360	        snd_iprintf(buffer, "\n");361	}362}363 364static void snd_ca0106_proc_reg_read2(struct snd_info_entry *entry, 365				       struct snd_info_buffer *buffer)366{367	struct snd_ca0106 *emu = entry->private_data;368	unsigned long value;369	int i,j;370 371	snd_iprintf(buffer, "Registers\n");372	for(i = 0x40; i < 0x80; i++) {373		snd_iprintf(buffer, "%02X: ",i);374		for (j = 0; j < 4; j++) {375                  value = snd_ca0106_ptr_read(emu, i, j);376		  snd_iprintf(buffer, "%08lX ", value);377                }378	        snd_iprintf(buffer, "\n");379	}380}381 382static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry, 383				       struct snd_info_buffer *buffer)384{385	struct snd_ca0106 *emu = entry->private_data;386        char line[64];387        unsigned int reg, channel_id , val;388        while (!snd_info_get_line(buffer, line, sizeof(line))) {389                if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)390                        continue;391		if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3)392                        snd_ca0106_ptr_write(emu, reg, channel_id, val);393        }394}395 396static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry, 397				       struct snd_info_buffer *buffer)398{399	struct snd_ca0106 *emu = entry->private_data;400        char line[64];401        unsigned int reg, val;402        while (!snd_info_get_line(buffer, line, sizeof(line))) {403                if (sscanf(line, "%x %x", &reg, &val) != 2)404                        continue;405                if ((reg <= 0x7f) || (val <= 0x1ff)) {406                        snd_ca0106_i2c_write(emu, reg, val);407		}408        }409}410 411int snd_ca0106_proc_init(struct snd_ca0106 *emu)412{413	snd_card_ro_proc_new(emu->card, "iec958", emu, snd_ca0106_proc_iec958);414	snd_card_rw_proc_new(emu->card, "ca0106_reg32", emu,415			     snd_ca0106_proc_reg_read32,416			     snd_ca0106_proc_reg_write32);417	snd_card_ro_proc_new(emu->card, "ca0106_reg16", emu,418			     snd_ca0106_proc_reg_read16);419	snd_card_ro_proc_new(emu->card, "ca0106_reg8", emu,420			     snd_ca0106_proc_reg_read8);421	snd_card_rw_proc_new(emu->card, "ca0106_regs1", emu,422			     snd_ca0106_proc_reg_read1,423			     snd_ca0106_proc_reg_write);424	snd_card_rw_proc_new(emu->card, "ca0106_i2c", emu, NULL,425			     snd_ca0106_proc_i2c_write);426	snd_card_ro_proc_new(emu->card, "ca0106_regs2", emu,427			     snd_ca0106_proc_reg_read2);428	return 0;429}430