📄 ca0106_main.c
字号:
/* * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit * Version: 0.0.23 * * FEATURES currently supported: * Front, Rear and Center/LFE. * Surround40 and Surround51. * Capture from MIC an LINE IN input. * SPDIF digital playback of PCM stereo and AC3/DTS works. * (One can use a standard mono mini-jack to one RCA plugs cable. * or one can use a standard stereo mini-jack to two RCA plugs cable. * Plug one of the RCA plugs into the Coax input of the external decoder/receiver.) * ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. ) * Notes on how to capture sound: * The AC97 is used in the PLAYBACK direction. * The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC. * So, to record from the MIC, set the MIC Playback volume to max, * unmute the MIC and turn up the MASTER Playback volume. * So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume. * * The only playback controls that currently do anything are: - * Analog Front * Analog Rear * Analog Center/LFE * SPDIF Front * SPDIF Rear * SPDIF Center/LFE * * For capture from Mic in or Line in. * Digital/Analog ( switch must be in Analog mode for CAPTURE. ) * * CAPTURE feedback into PLAYBACK * * Changelog: * Support interrupts per period. * Removed noise from Center/LFE channel when in Analog mode. * Rename and remove mixer controls. * 0.0.6 * Use separate card based DMA buffer for periods table list. * 0.0.7 * Change remove and rename ctrls into lists. * 0.0.8 * Try to fix capture sources. * 0.0.9 * Fix AC3 output. * Enable S32_LE format support. * 0.0.10 * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) * 0.0.11 * Add Model name recognition. * 0.0.12 * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. * Remove redundent "voice" handling. * 0.0.13 * Single trigger call for multi channels. * 0.0.14 * Set limits based on what the sound card hardware can do. * playback periods_min=2, periods_max=8 * capture hw constraints require period_size = n * 64 bytes. * playback hw constraints require period_size = n * 64 bytes. * 0.0.15 * Minor updates. * 0.0.16 * Implement 192000 sample rate. * 0.0.17 * Add support for SB0410 and SB0413. * 0.0.18 * Modified Copyright message. * 0.0.19 * Finally fix support for SB Live 24 bit. SB0410 and SB0413. * The output codec needs resetting, otherwise all output is muted. * 0.0.20 * Merge "pci_disable_device(pci);" fixes. * 0.0.21 * Add 4 capture channels. (SPDIF only comes in on channel 0. ) * Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.) * 0.0.22 * Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901 * 0.0.23 * Implement support for Line-in capture on SB Live 24bit. * * BUGS: * Some stability problems when unloading the snd-ca0106 kernel module. * -- * * TODO: * 4 Capture channels, only one implemented so far. * Other capture rates apart from 48khz not implemented. * MIDI * -- * GENERAL INFO: * Model: SB0310 * P17 Chip: CA0106-DAT * AC97 Codec: STAC 9721 * ADC: Philips 1361T (Stereo 24bit) * DAC: WM8746EDS (6-channel, 24bit, 192Khz) * * GENERAL INFO: * Model: SB0410 * P17 Chip: CA0106-DAT * AC97 Codec: None * ADC: WM8775EDS (4 Channel) * DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support) * SPDIF Out control switches between Mic in and SPDIF out. * No sound out or mic input working yet. * * GENERAL INFO: * Model: SB0413 * P17 Chip: CA0106-DAT * AC97 Codec: None. * ADC: Unknown * DAC: Unknown * Trying to handle it like the SB0410. * * This code was initally based on code from ALSA's emu10k1x.c which is: * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/moduleparam.h>#include <linux/dma-mapping.h>#include <sound/core.h>#include <sound/initval.h>#include <sound/pcm.h>#include <sound/ac97_codec.h>#include <sound/info.h>MODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");MODULE_DESCRIPTION("CA0106");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}");// module parameters (see "Module Parameters")static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for the CA0106 soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for the CA0106 soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");#include "ca0106.h"static ca0106_details_t ca0106_chip_details[] = { /* AudigyLS[SB0310] */ { .serial = 0x10021102, .name = "AudigyLS [SB0310]", .ac97 = 1 } , /* Unknown AudigyLS that also says SB0310 on it */ { .serial = 0x10051102, .name = "AudigyLS [SB0310b]", .ac97 = 1 } , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */ { .serial = 0x10061102, .name = "Live! 7.1 24bit [SB0410]", .gpio_type = 1, .i2c_adc = 1 } , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */ { .serial = 0x10071102, .name = "Live! 7.1 24bit [SB0413]", .gpio_type = 1, .i2c_adc = 1 } , /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ { .serial = 0x10091462, .name = "MSI K8N Diamond MB [SB0438]", .gpio_type = 1, .i2c_adc = 1 } , /* Shuttle XPC SD31P which has an onboard Creative Labs Sound Blaster Live! 24-bit EAX * high-definition 7.1 audio processor". * Added using info from andrewvegan in alsa bug #1298 */ { .serial = 0x30381297, .name = "Shuttle XPC SD31P [SD31P]", .gpio_type = 1, .i2c_adc = 1 } , { .serial = 0, .name = "AudigyLS [Unknown]" }};/* hardware definition */static snd_pcm_hardware_t snd_ca0106_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, .rate_min = 48000, .rate_max = 192000, .channels_min = 2, //1, .channels_max = 2, //6, .buffer_bytes_max = ((65536 - 64) * 8), .period_bytes_min = 64, .period_bytes_max = (65536 - 64), .periods_min = 2, .periods_max = 8, .fifo_size = 0,};static snd_pcm_hardware_t snd_ca0106_capture_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, .rate_min = 44100, .rate_max = 192000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = ((65536 - 64) * 8), .period_bytes_min = 64, .period_bytes_max = (65536 - 64), .periods_min = 2, .periods_max = 2, .fifo_size = 0,};unsigned int snd_ca0106_ptr_read(ca0106_t * emu, unsigned int reg, unsigned int chn){ unsigned long flags; unsigned int regptr, val; regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); outl(regptr, emu->port + PTR); val = inl(emu->port + DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val;}void snd_ca0106_ptr_write(ca0106_t *emu, unsigned int reg, unsigned int chn, unsigned int data){ unsigned int regptr; unsigned long flags; regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); outl(regptr, emu->port + PTR); outl(data, emu->port + DATA); spin_unlock_irqrestore(&emu->emu_lock, flags);}int snd_ca0106_i2c_write(ca0106_t *emu, u32 reg, u32 value){ u32 tmp; int timeout=0; int status; int retry; if ((reg > 0x7f) || (value > 0x1ff)) { snd_printk(KERN_ERR "i2c_write: invalid values.\n"); return -EINVAL; } tmp = reg << 25 | value << 16; /* Not sure what this I2C channel controls. */ /* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */ /* This controls the I2C connected to the WM8775 ADC Codec */ snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp); for(retry=0;retry<10;retry++) { /* Send the data to i2c */ tmp = snd_ca0106_ptr_read(emu, I2C_A, 0); tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); snd_ca0106_ptr_write(emu, I2C_A, 0, tmp); /* Wait till the transaction ends */ while(1) { status = snd_ca0106_ptr_read(emu, I2C_A, 0); //snd_printk("I2C:status=0x%x\n", status); timeout++; if((status & I2C_A_ADC_START)==0) break; if(timeout>1000) break; } //Read back and see if the transaction is successful if((status & I2C_A_ADC_ABORT)==0) break; } if(retry==10) { snd_printk(KERN_ERR "Writing to ADC failed!\n"); return -EINVAL; } return 0;}static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb){ unsigned long flags; unsigned int enable; spin_lock_irqsave(&emu->emu_lock, flags); enable = inl(emu->port + INTE) | intrenb; outl(enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags);}static void snd_ca0106_intr_disable(ca0106_t *emu, unsigned int intrenb){ unsigned long flags; unsigned int enable; spin_lock_irqsave(&emu->emu_lock, flags); enable = inl(emu->port + INTE) & ~intrenb; outl(enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags);}static void snd_ca0106_pcm_free_substream(snd_pcm_runtime_t *runtime){ kfree(runtime->private_data);}/* open_playback callback */static int snd_ca0106_pcm_open_playback_channel(snd_pcm_substream_t *substream, int channel_id){ ca0106_t *chip = snd_pcm_substream_chip(substream); ca0106_channel_t *channel = &(chip->playback_channels[channel_id]); ca0106_pcm_t *epcm; snd_pcm_runtime_t *runtime = substream->runtime; int err; epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = chip; epcm->substream = substream; epcm->channel_id=channel_id; runtime->private_data = epcm; runtime->private_free = snd_ca0106_pcm_free_substream; runtime->hw = snd_ca0106_playback_hw; channel->emu = chip; channel->number = channel_id;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -