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

📄 emupcm.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  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 + -