📄 p16v.c
字号:
/* * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> * Driver p16v chips * Version: 0.25 * * FEATURES currently supported: * Output fixed at S32_LE, 2 channel to hw:0,0 * Rates: 44.1, 48, 96, 192. * * Changelog: * 0.8 * Use separate card based buffer for periods table. * 0.9 * Use 2 channel output streams instead of 8 channel. * (8 channel output streams might be good for ASIO type output) * Corrected speaker output, so Front -> Front etc. * 0.10 * Fixed missed interrupts. * 0.11 * Add Sound card model number and names. * Add Analog volume controls. * 0.12 * Corrected playback interrupts. Now interrupt per period, instead of half period. * 0.13 * Use single trigger for multichannel. * 0.14 * Mic capture now works at fixed: S32_LE, 96000Hz, Stereo. * 0.15 * Force buffer_size / period_size == INTEGER. * 0.16 * Update p16v.c to work with changed alsa api. * 0.17 * Update p16v.c to work with changed alsa api. Removed boot_devs. * 0.18 * Merging with snd-emu10k1 driver. * 0.19 * One stereo channel at 24bit now works. * 0.20 * Added better register defines. * 0.21 * Integrated with snd-emu10k1 driver. * 0.22 * Removed #if 0 ... #endif * 0.23 * Implement different capture rates. * 0.24 * Implement different capture source channels. * e.g. When HD Capture source is set to SPDIF, * setting HD Capture channel to 0 captures from CDROM digital input. * setting HD Capture channel to 1 captures from SPDIF in. * 0.25 * Include capture buffer sizes. * * BUGS: * Some stability problems when unloading the snd-p16v kernel module. * -- * * TODO: * SPDIF out. * Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz. * Currently capture fixed at 48000Hz. * * -- * GENERAL INFO: * Model: SB0240 * P16V Chip: CA0151-DBS * Audigy 2 Chip: CA0102-IAT * AC97 Codec: STAC 9721 * ADC: Philips 1361T (Stereo 24bit) * DAC: CS4382-K (8-channel, 24bit, 192Khz) * * 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/vmalloc.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>#include <sound/emu10k1.h>#include "p16v.h"#define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */#define PCM_FRONT_CHANNEL 0#define PCM_REAR_CHANNEL 1#define PCM_CENTER_LFE_CHANNEL 2#define PCM_SIDE_CHANNEL 3#define CONTROL_FRONT_CHANNEL 0#define CONTROL_REAR_CHANNEL 3#define CONTROL_CENTER_LFE_CHANNEL 1#define CONTROL_SIDE_CHANNEL 2/* Card IDs: * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2002 -> Audigy2 ZS 7.1 Model:SB0350 * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1007 -> Audigy2 6.1 Model:SB0240 * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1002 -> Audigy2 Platinum Model:SB msb0240230009266 * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2007 -> Audigy4 Pro Model:SB0380 M1SB0380472001901E * */ /* hardware definition */static struct snd_pcm_hardware snd_p16v_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */ .rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, .rate_min = 44100, .rate_max = 192000, .channels_min = 8, .channels_max = 8, .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 struct snd_pcm_hardware snd_p16v_capture_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, .rate_min = 44100, .rate_max = 192000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = (65536 - 64), .period_bytes_min = 64, .period_bytes_max = (65536 - 128) >> 1, /* size has to be N*64 bytes */ .periods_min = 2, .periods_max = 2, .fifo_size = 0,};static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime){ struct snd_emu10k1_pcm *epcm = runtime->private_data; if (epcm) { //snd_printk("epcm free: %p\n", epcm); kfree(epcm); }}/* open_playback callback */static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id){ struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]); struct snd_emu10k1_pcm *epcm; struct snd_pcm_runtime *runtime = substream->runtime; int err; epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); //snd_printk("epcm kcalloc: %p\n", epcm); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; epcm->substream = substream; //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id); runtime->private_data = epcm; runtime->private_free = snd_p16v_pcm_free_substream; runtime->hw = snd_p16v_playback_hw; channel->emu = emu; channel->number = channel_id; channel->use=1; //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use); //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel); //channel->interrupt = snd_p16v_pcm_channel_interrupt; channel->epcm=epcm; if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; return 0;}/* open_capture callback */static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id){ struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice); struct snd_emu10k1_pcm *epcm; struct snd_pcm_runtime *runtime = substream->runtime; int err; epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); //snd_printk("epcm kcalloc: %p\n", epcm); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; epcm->substream = substream; //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id); runtime->private_data = epcm; runtime->private_free = snd_p16v_pcm_free_substream; runtime->hw = snd_p16v_capture_hw; channel->emu = emu; channel->number = channel_id; channel->use=1; //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use); //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel); //channel->interrupt = snd_p16v_pcm_channel_interrupt; channel->epcm=epcm; if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; return 0;}/* close callback */static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream){ struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); //struct snd_pcm_runtime *runtime = substream->runtime; //struct snd_emu10k1_pcm *epcm = runtime->private_data; emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0; /* FIXME: maybe zero others */ return 0;}/* close callback */static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream){ struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); //struct snd_pcm_runtime *runtime = substream->runtime; //struct snd_emu10k1_pcm *epcm = runtime->private_data; emu->p16v_capture_voice.use=0; /* FIXME: maybe zero others */ return 0;}static int snd_p16v_pcm_open_playback_front(struct snd_pcm_substream *substream){ return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);}static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream){ // Only using channel 0 for now, but the card has 2 channels. return snd_p16v_pcm_open_capture_channel(substream, 0);}/* hw_params callback */static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ int result; result = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); return result;}/* hw_params callback */static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ int result; result = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); return result;}/* hw_free callback */static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream){ int result; result = snd_pcm_lib_free_pages(substream); return result;}/* hw_free callback */static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream){ int result; result = snd_pcm_lib_free_pages(substream); return result;}/* prepare playback callback */static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream){ struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int channel = substream->pcm->device - emu->p16v_device_offset; u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel)); u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size); int i; u32 tmp; //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1)); //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base); //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->p16v_buffer.addr, emu->p16v_buffer.area, emu->p16v_buffer.bytes); tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel); switch (runtime->rate) { case 44100: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080); break; case 96000: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040); break; case 192000: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020); break; case 48000: default: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000); break; } /* FIXME: Check emu->buffer.size before actually writing to it. */ for(i=0; i < runtime->periods; i++) { table_base[i*2]=runtime->dma_addr+(i*period_size_bytes); table_base[(i*2)+1]=period_size_bytes<<16; } snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel)); snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19); snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0); snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr); //snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0); snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0); snd_emu10k1_ptr20_write(emu, 0x08, channel, 0); return 0;}/* prepare capture callback */static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream){ struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int channel = substream->pcm->device - emu->p16v_device_offset; u32 tmp; //printk("prepare capture:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1)); tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel); switch (runtime->rate) { case 44100: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800); break; case 96000: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400); break; case 192000: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200); break; case 48000: default: snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000); break; } /* FIXME: Check emu->buffer.size before actually writing to it. */ snd_emu10k1_ptr20_write(emu, 0x13, channel, 0); snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr); snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0); //snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */ //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel)); return 0;}static void snd_p16v_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb){ unsigned long flags; unsigned int enable; spin_lock_irqsave(&emu->emu_lock, flags); enable = inl(emu->port + INTE2) | intrenb; outl(enable, emu->port + INTE2); spin_unlock_irqrestore(&emu->emu_lock, flags);}static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb){ unsigned long flags; unsigned int disable; spin_lock_irqsave(&emu->emu_lock, flags); disable = inl(emu->port + INTE2) & (~intrenb); outl(disable, emu->port + INTE2); spin_unlock_irqrestore(&emu->emu_lock, flags);}/* trigger_playback callback */static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream, int cmd){ struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime; struct snd_emu10k1_pcm *epcm; int channel; int result = 0; struct list_head *pos; struct snd_pcm_substream *s; u32 basic = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -