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

📄 ymfpci_main.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  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 * */#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/vmalloc.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>#include <asm/io.h>/* *  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){	unsigned long end_time;	u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;		end_time = jiffies + msecs_to_jiffies(750);	do {		if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)			return 0;		set_current_state(TASK_UNINTERRUPTIBLE);		schedule_timeout_uninterruptible(1);	} while (time_before(jiffies, end_time));	snd_printk(KERN_ERR "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 = ac97->private_data;	u32 cmd;		snd_ymfpci_codec_ready(chip, 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 = ac97->private_data;	if (snd_ymfpci_codec_ready(chip, 0))		return ~0;	snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);	if (snd_ymfpci_codec_ready(chip, 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;}static 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;		}static 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);		}		if (unlikely(ypcm->update_pcm_vol)) {			unsigned int subs = ypcm->substream->number;			unsigned int next_bank = 1 - chip->active_bank;			snd_ymfpci_playback_bank_t *bank;			u32 volume;						bank = &voice->bank[next_bank];			volume = cpu_to_le32(chip->pcm_mixer[subs].left << 15);			bank->left_gain_end = volume;			if (ypcm->output_rear)				bank->eff2_gain_end = volume;			if (ypcm->voices[1])				bank = &ypcm->voices[1]->bank[next_bank];			volume = cpu_to_le32(chip->pcm_mixer[subs].right << 15);			bank->right_gain_end = volume;			if (ypcm->output_rear)				bank->eff3_gain_end = volume;			ypcm->update_pcm_vol--;		}	}	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 = runtime->private_data;	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 = substream->runtime->private_data;	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 = substream->runtime->private_data;	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)

⌨️ 快捷键说明

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