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

📄 ymfpci_main.c

📁 是关于linux2.5.1的完全源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> *  Routines for control of YMF724/740/744/754 chips * *  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 * */#define __NO_VERSION__#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/control.h>#include <sound/info.h>#include <sound/ymfpci.h>#include <sound/asoundef.h>#include <sound/mpu401.h>#define chip_t ymfpci_t/* *  constants *//* *  common I/O routines */static void snd_ymfpci_irq_wait(ymfpci_t *chip);static inline u8 snd_ymfpci_readb(ymfpci_t *chip, u32 offset){	return readb(chip->reg_area_virt + offset);}static inline void snd_ymfpci_writeb(ymfpci_t *chip, u32 offset, u8 val){	writeb(val, chip->reg_area_virt + offset);}static inline u16 snd_ymfpci_readw(ymfpci_t *chip, u32 offset){	return readw(chip->reg_area_virt + offset);}static inline void snd_ymfpci_writew(ymfpci_t *chip, u32 offset, u16 val){	writew(val, chip->reg_area_virt + offset);}static inline u32 snd_ymfpci_readl(ymfpci_t *chip, u32 offset){	return readl(chip->reg_area_virt + offset);}static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val){	writel(val, chip->reg_area_virt + offset);}static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary, int sched){	signed long end_time;	u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;		end_time = (jiffies + ((3 * HZ) / 4)) + 1;	do {		if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)			return 0;		if (sched) {			set_current_state(TASK_UNINTERRUPTIBLE);			schedule_timeout(1);		}	} while (end_time - (signed long)jiffies >= 0);	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));	return -EBUSY;}static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val){	ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return);	u32 cmd;		snd_ymfpci_codec_ready(chip, 0, 0);	cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;	snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd);}static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg){	ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return -ENXIO);	if (snd_ymfpci_codec_ready(chip, 0, 0))		return ~0;	snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);	if (snd_ymfpci_codec_ready(chip, 0, 0))		return ~0;	if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) {		int i;		for (i = 0; i < 600; i++)			snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);	}	return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);}/* *  Misc routines */static u32 snd_ymfpci_calc_delta(u32 rate){	switch (rate) {	case 8000:	return 0x02aaab00;	case 11025:	return 0x03accd00;	case 16000:	return 0x05555500;	case 22050:	return 0x07599a00;	case 32000:	return 0x0aaaab00;	case 44100:	return 0x0eb33300;	default:	return ((rate << 16) / 375) << 5;	}}static u32 def_rate[8] = {	100, 2000, 8000, 11025, 16000, 22050, 32000, 48000};static u32 snd_ymfpci_calc_lpfK(u32 rate){	u32 i;	static u32 val[8] = {		0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,		0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000	};		if (rate == 44100)		return 0x40000000;	/* FIXME: What's the right value? */	for (i = 0; i < 8; i++)		if (rate <= def_rate[i])			return val[i];	return val[0];}static u32 snd_ymfpci_calc_lpfQ(u32 rate){	u32 i;	static u32 val[8] = {		0x35280000, 0x34A70000, 0x32020000, 0x31770000,		0x31390000, 0x31C90000, 0x33D00000, 0x40000000	};		if (rate == 44100)		return 0x370A0000;	for (i = 0; i < 8; i++)		if (rate <= def_rate[i])			return val[i];	return val[0];}/* *  Hardware start management */static void snd_ymfpci_hw_start(ymfpci_t *chip){	unsigned long flags;	spin_lock_irqsave(&chip->reg_lock, flags);	if (chip->start_count++ > 0)		goto __end;	snd_ymfpci_writel(chip, YDSXGR_MODE,			  snd_ymfpci_readl(chip, YDSXGR_MODE) | 3);	chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;      __end:      	spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_ymfpci_hw_stop(ymfpci_t *chip){	unsigned long flags;	long timeout = 1000;	spin_lock_irqsave(&chip->reg_lock, flags);	if (--chip->start_count > 0)		goto __end;	snd_ymfpci_writel(chip, YDSXGR_MODE,			  snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3);	while (timeout-- > 0) {		if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0)			break;	}	if (atomic_read(&chip->interrupt_sleep_count)) {		atomic_set(&chip->interrupt_sleep_count, 0);		wake_up(&chip->interrupt_sleep);	}      __end:      	spin_unlock_irqrestore(&chip->reg_lock, flags);}/* *  Playback voice management */static int voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice){	ymfpci_voice_t *voice, *voice2;	int idx;		*rvoice = NULL;	for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) {		voice = &chip->voices[idx];		voice2 = pair ? &chip->voices[idx+1] : NULL;		if (voice->use || (voice2 && voice2->use))			continue;		voice->use = 1;		if (voice2)			voice2->use = 1;		switch (type) {		case YMFPCI_PCM:			voice->pcm = 1;			if (voice2)				voice2->pcm = 1;			break;		case YMFPCI_SYNTH:			voice->synth = 1;			break;		case YMFPCI_MIDI:			voice->midi = 1;			break;		}		snd_ymfpci_hw_start(chip);		if (voice2)			snd_ymfpci_hw_start(chip);		*rvoice = voice;		return 0;	}	return -ENOMEM;}int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice){	unsigned long flags;	int result;		snd_assert(rvoice != NULL, return -EINVAL);	snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL);		spin_lock_irqsave(&chip->voice_lock, flags);	for (;;) {		result = voice_alloc(chip, type, pair, rvoice);		if (result == 0 || type != YMFPCI_PCM)			break;		/* TODO: synth/midi voice deallocation */		break;	}	spin_unlock_irqrestore(&chip->voice_lock, flags);		return result;		}int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice){	unsigned long flags;		snd_assert(pvoice != NULL, return -EINVAL);	snd_ymfpci_hw_stop(chip);	spin_lock_irqsave(&chip->voice_lock, flags);	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;	pvoice->ypcm = NULL;	pvoice->interrupt = NULL;	spin_unlock_irqrestore(&chip->voice_lock, flags);	return 0;}/* *  PCM part */static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice){	ymfpci_pcm_t *ypcm;	u32 pos, delta;		if ((ypcm = voice->ypcm) == NULL)		return;	if (ypcm->substream == NULL)		return;	spin_lock(&chip->reg_lock);	if (ypcm->running) {		pos = le32_to_cpu(voice->bank[chip->active_bank].start);		if (pos < ypcm->last_pos)			delta = pos + (ypcm->buffer_size - ypcm->last_pos);		else			delta = pos - ypcm->last_pos;		ypcm->period_pos += delta;		ypcm->last_pos = pos;		if (ypcm->period_pos >= ypcm->period_size) {			// printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);			ypcm->period_pos %= ypcm->period_size;			spin_unlock(&chip->reg_lock);			snd_pcm_period_elapsed(ypcm->substream);			spin_lock(&chip->reg_lock);		}	}	spin_unlock(&chip->reg_lock);}static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream){	snd_pcm_runtime_t *runtime = substream->runtime;	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return);	ymfpci_t *chip = ypcm->chip;	u32 pos, delta;		spin_lock(&chip->reg_lock);	if (ypcm->running) {		pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;		if (pos < ypcm->last_pos)			delta = pos + (ypcm->buffer_size - ypcm->last_pos);		else			delta = pos - ypcm->last_pos;		ypcm->period_pos += delta;		ypcm->last_pos = pos;		if (ypcm->period_pos >= ypcm->period_size) {			ypcm->period_pos %= ypcm->period_size;			// printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);			spin_unlock(&chip->reg_lock);			snd_pcm_period_elapsed(substream);			spin_lock(&chip->reg_lock);		}	}	spin_unlock(&chip->reg_lock);}static int snd_ymfpci_playback_trigger(snd_pcm_substream_t * substream,				       int cmd){	ymfpci_t *chip = snd_pcm_substream_chip(substream);	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO);	int result = 0;	spin_lock(&chip->reg_lock);	if (ypcm->voices[0] == NULL) {		result = -EINVAL;		goto __unlock;	}	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:	case SNDRV_PCM_TRIGGER_RESUME:		chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr);		if (ypcm->voices[1] != NULL)			chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr);		ypcm->running = 1;		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:	case SNDRV_PCM_TRIGGER_SUSPEND:		chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;		if (ypcm->voices[1] != NULL)			chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0;		ypcm->running = 0;		break;	default:		result = -EINVAL;		break;	}      __unlock:	spin_unlock(&chip->reg_lock);	return result;}static int snd_ymfpci_capture_trigger(snd_pcm_substream_t * substream,				      int cmd){	ymfpci_t *chip = snd_pcm_substream_chip(substream);	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO);	int result = 0;	u32 tmp;	spin_lock(&chip->reg_lock);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:	case SNDRV_PCM_TRIGGER_RESUME:		tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);		snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);		ypcm->running = 1;		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:	case SNDRV_PCM_TRIGGER_SUSPEND:		tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);		snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);		ypcm->running = 0;		break;	default:		result = -EINVAL;		break;	}	spin_unlock(&chip->reg_lock);	return result;}static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices){	int err;	if (ypcm->voices[1] != NULL && voices < 2) {		snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]);		ypcm->voices[1] = NULL;	}	if (voices == 1 && ypcm->voices[0] != NULL)		return 0;		/* already allocated */	if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)		return 0;		/* already allocated */	if (voices > 1) {		if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {			snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]);			ypcm->voices[0] = NULL;		}			}	err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);	if (err < 0)		return err;	ypcm->voices[0]->ypcm = ypcm;	ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt;	if (voices > 1) {		ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1];		ypcm->voices[1]->ypcm = ypcm;	}	return 0;}static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo,				      int rate, int w_16, unsigned long addr,				      unsigned int end, int eff2){	u32 format;	u32 delta = snd_ymfpci_calc_delta(rate);	u32 lpfQ = snd_ymfpci_calc_lpfQ(rate);	u32 lpfK = snd_ymfpci_calc_lpfK(rate);	snd_ymfpci_playback_bank_t *bank;	unsigned int nbank;	snd_assert(voice != NULL, return);	format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000);	for (nbank = 0; nbank < 2; nbank++) {		bank = &voice->bank[nbank];		bank->format = cpu_to_le32(format);		bank->loop_default = 0;		bank->base = cpu_to_le32(addr);		bank->loop_start = 0;		bank->loop_end = cpu_to_le32(end);		bank->loop_frac = 0;		bank->eg_gain_end = cpu_to_le32(0x40000000);		bank->lpfQ = cpu_to_le32(lpfQ);		bank->status = 0;		bank->num_of_frames = 0;		bank->loop_count = 0;		bank->start = 0;		bank->start_frac = 0;		bank->delta =		bank->delta_end = cpu_to_le32(delta);		bank->lpfK =		bank->lpfK_end = cpu_to_le32(lpfK);		bank->eg_gain = cpu_to_le32(0x40000000);		bank->lpfD1 =		bank->lpfD2 = 0;		bank->left_gain = 		bank->right_gain =		bank->left_gain_end =		bank->right_gain_end =		bank->eff1_gain =		bank->eff2_gain =		bank->eff3_gain =		bank->eff1_gain_end =		bank->eff2_gain_end =		bank->eff3_gain_end = 0;		if (!stereo) {			if (!eff2) {				bank->left_gain = 				bank->right_gain =				bank->left_gain_end =				bank->right_gain_end = cpu_to_le32(0x40000000);			} else {				bank->eff2_gain =				bank->eff2_gain_end =				bank->eff3_gain =				bank->eff3_gain_end = cpu_to_le32(0x40000000);			}		} else {			if (!eff2) {				if ((voice->number & 1) == 0) {					bank->left_gain =					bank->left_gain_end = cpu_to_le32(0x40000000);				} else {					bank->format |= cpu_to_le32(1);					bank->right_gain =					bank->right_gain_end = cpu_to_le32(0x40000000);				}			} else {				if ((voice->number & 1) == 0) {

⌨️ 快捷键说明

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