📄 emupcm.c
字号:
/* * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * Creative Labs, Inc. * Routines for control of EMU10K1 chips / PCM routines * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com> * * BUGS: * -- * * TODO: * -- * * 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/pci.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/init.h>#include <sound/core.h>#include <sound/emu10k1.h>static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice){ emu10k1_pcm_t *epcm; if ((epcm = voice->epcm) == NULL) return; if (epcm->substream == NULL) return;#if 0 printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", epcm->substream->runtime->hw->pointer(emu, epcm->substream), snd_pcm_lib_period_bytes(epcm->substream), snd_pcm_lib_buffer_bytes(epcm->substream));#endif snd_pcm_period_elapsed(epcm->substream);}static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status){#if 0 if (status & IPR_ADCBUFHALFFULL) { if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) return; }#endif snd_pcm_period_elapsed(emu->pcm_capture_substream);}static void snd_emu10k1_pcm_ac97mic_interrupt(emu10k1_t *emu, unsigned int status){#if 0 if (status & IPR_MICBUFHALFFULL) { if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) return; }#endif snd_pcm_period_elapsed(emu->pcm_capture_mic_substream);}static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status){#if 0 if (status & IPR_EFXBUFHALFFULL) { if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) return; }#endif snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);} static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm = runtime->private_data; unsigned int ptr; if (!epcm->running) return 0; ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; ptr += runtime->buffer_size; ptr -= epcm->ccca_start_addr; ptr %= runtime->buffer_size; return ptr;}static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices){ int err, i; if (epcm->voices[1] != NULL && voices < 2) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); epcm->voices[1] = NULL; } for (i = 0; i < voices; i++) { if (epcm->voices[i] == NULL) break; } if (i == voices) return 0; /* already allocated */ for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) { if (epcm->voices[i]) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); epcm->voices[i] = NULL; } } err = snd_emu10k1_voice_alloc(epcm->emu, epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX, voices, &epcm->voices[0]); if (err < 0) return err; epcm->voices[0]->epcm = epcm; if (voices > 1) { for (i = 1; i < voices; i++) { epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i]; epcm->voices[i]->epcm = epcm; } } if (epcm->extra == NULL) { err = snd_emu10k1_voice_alloc(epcm->emu, epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX, 1, &epcm->extra); if (err < 0) { // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); for (i = 0; i < voices; i++) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); epcm->voices[i] = NULL; } return err; } epcm->extra->epcm = epcm; epcm->extra->interrupt = snd_emu10k1_pcm_interrupt; } return 0;}static unsigned int capture_period_sizes[31] = { 384, 448, 512, 640, 384*2, 448*2, 512*2, 640*2, 384*4, 448*4, 512*4, 640*4, 384*8, 448*8, 512*8, 640*8, 384*16, 448*16, 512*16, 640*16, 384*32, 448*32, 512*32, 640*32, 384*64, 448*64, 512*64, 640*64, 384*128,448*128,512*128};static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = { .count = 31, .list = capture_period_sizes, .mask = 0};static unsigned int capture_rates[8] = { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000};static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = { .count = 8, .list = capture_rates, .mask = 0};static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate){ switch (rate) { case 8000: return ADCCR_SAMPLERATE_8; case 11025: return ADCCR_SAMPLERATE_11; case 16000: return ADCCR_SAMPLERATE_16; case 22050: return ADCCR_SAMPLERATE_22; case 24000: return ADCCR_SAMPLERATE_24; case 32000: return ADCCR_SAMPLERATE_32; case 44100: return ADCCR_SAMPLERATE_44; case 48000: return ADCCR_SAMPLERATE_48; default: snd_BUG(); return ADCCR_SAMPLERATE_8; }}static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate){ switch (rate) { case 8000: return A_ADCCR_SAMPLERATE_8; case 11025: return A_ADCCR_SAMPLERATE_11; case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */ case 16000: return ADCCR_SAMPLERATE_16; case 22050: return ADCCR_SAMPLERATE_22; case 24000: return ADCCR_SAMPLERATE_24; case 32000: return ADCCR_SAMPLERATE_32; case 44100: return ADCCR_SAMPLERATE_44; case 48000: return ADCCR_SAMPLERATE_48; default: snd_BUG(); return A_ADCCR_SAMPLERATE_8; }}static unsigned int emu10k1_calc_pitch_target(unsigned int rate){ unsigned int pitch_target; pitch_target = (rate << 8) / 375; pitch_target = (pitch_target >> 1) + (pitch_target & 1); return pitch_target;}#define PITCH_48000 0x00004000#define PITCH_96000 0x00008000#define PITCH_85000 0x00007155#define PITCH_80726 0x00006ba2#define PITCH_67882 0x00005a82#define PITCH_57081 0x00004c1cstatic unsigned int emu10k1_select_interprom(unsigned int pitch_target){ if (pitch_target == PITCH_48000) return CCCA_INTERPROM_0; else if (pitch_target < PITCH_48000) return CCCA_INTERPROM_1; else if (pitch_target >= PITCH_96000) return CCCA_INTERPROM_0; else if (pitch_target >= PITCH_85000) return CCCA_INTERPROM_6; else if (pitch_target >= PITCH_80726) return CCCA_INTERPROM_5; else if (pitch_target >= PITCH_67882) return CCCA_INTERPROM_4; else if (pitch_target >= PITCH_57081) return CCCA_INTERPROM_3; else return CCCA_INTERPROM_2;}/* * calculate cache invalidate size * * stereo: channel is stereo * w_16: using 16bit samples * * returns: cache invalidate size in samples */static inline int emu10k1_ccis(int stereo, int w_16){ if (w_16) { return stereo ? 24 : 26; } else { return stereo ? 24*2 : 26*2; }}static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, int master, int extra, emu10k1_voice_t *evoice, unsigned int start_addr, unsigned int end_addr, emu10k1_pcm_mixer_t *mix){ snd_pcm_substream_t *substream = evoice->epcm->substream; snd_pcm_runtime_t *runtime = substream->runtime; unsigned int silent_page, tmp; int voice, stereo, w_16; unsigned char attn, send_amount[8]; unsigned char send_routing[8]; unsigned long flags; unsigned int pitch_target; unsigned int ccis; voice = evoice->number; stereo = runtime->channels == 2; w_16 = snd_pcm_format_width(runtime->format) == 16; if (!extra && stereo) { start_addr >>= 1; end_addr >>= 1; } if (w_16) { start_addr >>= 1; end_addr >>= 1; } spin_lock_irqsave(&emu->reg_lock, flags); /* volume parameters */ if (extra) { attn = 0; memset(send_routing, 0, sizeof(send_routing)); send_routing[0] = 0; send_routing[1] = 1; send_routing[2] = 2; send_routing[3] = 3; memset(send_amount, 0, sizeof(send_amount)); } else { /* mono, left, right (master voice = left) */ tmp = stereo ? (master ? 1 : 2) : 0; memcpy(send_routing, &mix->send_routing[tmp][0], 8); memcpy(send_amount, &mix->send_volume[tmp][0], 8); } ccis = emu10k1_ccis(stereo, w_16); if (master) { evoice->epcm->ccca_start_addr = start_addr + ccis; if (extra) { start_addr += ccis; end_addr += ccis; } if (stereo && !extra) { snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK); } else { snd_emu10k1_ptr_write(emu, CPF, voice, 0); } } // setup routing if (emu->audigy) { snd_emu10k1_ptr_write(emu, A_FXRT1, voice, snd_emu10k1_compose_audigy_fxrt1(send_routing)); snd_emu10k1_ptr_write(emu, A_FXRT2, voice, snd_emu10k1_compose_audigy_fxrt2(send_routing)); snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, ((unsigned int)send_amount[4] << 24) | ((unsigned int)send_amount[5] << 16) | ((unsigned int)send_amount[6] << 8) | (unsigned int)send_amount[7]); } else snd_emu10k1_ptr_write(emu, FXRT, voice, snd_emu10k1_compose_send_routing(send_routing)); // Stop CA // Assumption that PT is already 0 so no harm overwriting snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); pitch_target = emu10k1_calc_pitch_target(runtime->rate); if (extra) snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr | emu10k1_select_interprom(pitch_target) | (w_16 ? 0 : CCCA_8BITSELECT)); else snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) | emu10k1_select_interprom(pitch_target) | (w_16 ? 0 : CCCA_8BITSELECT)); // Clear filter delay memory snd_emu10k1_ptr_write(emu, Z1, voice, 0); snd_emu10k1_ptr_write(emu, Z2, voice, 0); // invalidate maps silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK; snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); // modulation envelope snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0); snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f); snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000); snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000); snd_emu10k1_ptr_write(emu, FMMOD, voice, 0); snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0); snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0); snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000); // volume envelope snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f); snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000); // filter envelope snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f); // pitch envelope snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0); spin_unlock_irqrestore(&emu->reg_lock, flags);}static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm = runtime->private_data; int err; if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) return err; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) return err; if (err > 0) { /* change */ snd_util_memblk_t *memblk; if (epcm->memblk != NULL) snd_emu10k1_free_pages(emu, epcm->memblk); memblk = snd_emu10k1_alloc_pages(emu, substream); if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) { epcm->start_addr = 0; return -ENOMEM; } epcm->start_addr = ((emu10k1_memblk_t *)memblk)->mapped_page << PAGE_SHIFT; } return 0;}static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm; if (runtime->private_data == NULL) return 0; epcm = runtime->private_data; if (epcm->extra) { snd_emu10k1_voice_free(epcm->emu, epcm->extra); epcm->extra = NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -