⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ca0106_main.c

📁 鼎力推荐!本程序是基于嵌入式LUNUX系统开发的源程序代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  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.21 * *  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.) * *  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 <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"typedef struct {	u32 serial;	char * name;} ca0106_names_t;static ca0106_names_t ca0106_chip_names[] = {	 { 0x10021102, "AudigyLS [SB0310]"} , 	 { 0x10051102, "AudigyLS [SB0310b]"} , /* Unknown AudigyLS that also says SB0310 on it */	 { 0x10061102, "Live! 7.1 24bit [SB0410]"} , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */	 { 0x10071102, "Live! 7.1 24bit [SB0413]"} , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */	 { 0, "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 =	(32*1024),	.period_bytes_min =	64,	.period_bytes_max =	(16*1024),	.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,	.rates =		SNDRV_PCM_RATE_48000,	.rate_min =		48000,	.rate_max =		48000,	.channels_min =		2,	.channels_max =		2,	.buffer_bytes_max =	(32*1024),	.period_bytes_min =	64,	.period_bytes_max =	(16*1024),	.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);}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_pcm_free_substream(snd_pcm_runtime_t *runtime){	ca0106_pcm_t *epcm = runtime->private_data;  	if (epcm) {		kfree(epcm);	}}/* 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 = kcalloc(1, 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;        channel->use=1;        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;        channel->epcm=epcm;	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)                return err;	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)                return err;	return 0;}/* close callback */static int snd_ca0106_pcm_close_playback(snd_pcm_substream_t *substream){	ca0106_t *chip = snd_pcm_substream_chip(substream);	snd_pcm_runtime_t *runtime = substream->runtime;        ca0106_pcm_t *epcm = runtime->private_data;        chip->playback_channels[epcm->channel_id].use=0;/* FIXME: maybe zero others */	return 0;}static int snd_ca0106_pcm_open_playback_front(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);}static int snd_ca0106_pcm_open_playback_center_lfe(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_playback_channel(substream, PCM_CENTER_LFE_CHANNEL);}static int snd_ca0106_pcm_open_playback_unknown(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_playback_channel(substream, PCM_UNKNOWN_CHANNEL);}static int snd_ca0106_pcm_open_playback_rear(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_playback_channel(substream, PCM_REAR_CHANNEL);}/* open_capture callback */static int snd_ca0106_pcm_open_capture_channel(snd_pcm_substream_t *substream, int channel_id){	ca0106_t *chip = snd_pcm_substream_chip(substream);        ca0106_channel_t *channel = &(chip->capture_channels[channel_id]);	ca0106_pcm_t *epcm;	snd_pcm_runtime_t *runtime = substream->runtime;	int err;	epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);	if (epcm == NULL) {                snd_printk("open_capture_channel: failed epcm alloc\n");		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_capture_hw;        channel->emu = chip;        channel->number = channel_id;        channel->use=1;        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;        channel->epcm=epcm;	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)                return err;	//snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)                return err;	return 0;}/* close callback */static int snd_ca0106_pcm_close_capture(snd_pcm_substream_t *substream){	ca0106_t *chip = snd_pcm_substream_chip(substream);	snd_pcm_runtime_t *runtime = substream->runtime;        ca0106_pcm_t *epcm = runtime->private_data;        chip->capture_channels[epcm->channel_id].use=0;/* FIXME: maybe zero others */	return 0;}static int snd_ca0106_pcm_open_0_capture(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_capture_channel(substream, 0);}static int snd_ca0106_pcm_open_1_capture(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_capture_channel(substream, 1);}static int snd_ca0106_pcm_open_2_capture(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_capture_channel(substream, 2);}static int snd_ca0106_pcm_open_3_capture(snd_pcm_substream_t *substream){	return snd_ca0106_pcm_open_capture_channel(substream, 3);}/* hw_params callback */static int snd_ca0106_pcm_hw_params_playback(snd_pcm_substream_t *substream,				      snd_pcm_hw_params_t * hw_params){	return snd_pcm_lib_malloc_pages(substream,					params_buffer_bytes(hw_params));}/* hw_free callback */static int snd_ca0106_pcm_hw_free_playback(snd_pcm_substream_t *substream){	return snd_pcm_lib_free_pages(substream);}/* hw_params callback */static int snd_ca0106_pcm_hw_params_capture(snd_pcm_substream_t *substream,				      snd_pcm_hw_params_t * hw_params){	return snd_pcm_lib_malloc_pages(substream,					params_buffer_bytes(hw_params));}/* hw_free callback */static int snd_ca0106_pcm_hw_free_capture(snd_pcm_substream_t *substream)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -