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

📄 ymfpci.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  Copyright 1999 Jaroslav Kysela <perex@suse.cz> *  Copyright 2000 Alan Cox <alan@redhat.com> * *  Yamaha YMF7xx driver. * *  This code is a result of high-speed collision *  between ymfpci.c of ALSA and cs46xx.c of Linux. *  -- Pete Zaitcev <zaitcev@yahoo.com>; 2000/09/18 * *   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., 675 Mass Ave, Cambridge, MA 02139, USA. * * TODO: *  - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). *  - 96KHz playback for DVD - use pitch of 2.0. *  - Retain DMA buffer on close, do not wait the end of frame. *  - Resolve XXX tagged questions. *  - Cannot play 5133Hz. *  - 2001/01/07 Consider if we can remove voice_lock, like so: *     : Allocate/deallocate voices in open/close under semafore. *     : We access voices in interrupt, that only for pcms that open. *    voice_lock around playback_prepare closes interrupts for insane duration. *  - Revisit the way voice_alloc is done - too confusing, overcomplicated. *    Should support various channel types, however. *  - Remove prog_dmabuf from read/write, leave it in open. *  - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_YMFPCI_LEGACY code with *    native synthesizer through a playback slot. *  - Use new 2.3.x cache coherent PCI DMA routines instead of virt_to_bus. *  - Make the thing big endian compatible. ALSA has it done. *  - 2001/11/29 ac97_save_state */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/soundcard.h>#include <linux/ac97_codec.h>#include <linux/sound.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>#ifdef CONFIG_SOUND_YMFPCI_LEGACY# include "sound_config.h"# include "mpu401.h"#endif#include "ymfpci.h"/* * I do not believe in debug levels as I never can guess what * part of the code is going to be problematic in the future. * Don't forget to run your klogd with -c 8. * * Example (do not remove): * #define YMFDBG(fmt, arg...)  do{ printk(KERN_DEBUG fmt, ##arg); }while(0) */#define YMFDBGW(fmt, arg...)  /* */	/* write counts */#define YMFDBGI(fmt, arg...)  /* */	/* interrupts */#define YMFDBGX(fmt, arg...)  /* */	/* ioctl */static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice);static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank);static int ymf_playback_prepare(struct ymf_state *state);static int ymf_capture_prepare(struct ymf_state *state);static struct ymf_state *ymf_state_alloc(ymfpci_t *unit);static void ymfpci_aclink_reset(struct pci_dev * pci);static void ymfpci_disable_dsp(ymfpci_t *unit);static void ymfpci_download_image(ymfpci_t *codec);static void ymf_memload(ymfpci_t *unit);static LIST_HEAD(ymf_devs);/* *  constants */static struct pci_device_id ymf_id_tbl[] __devinitdata = {#define DEV(v, d, data) \  { PCI_VENDOR_ID_##v, PCI_DEVICE_ID_##v##_##d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)data }	DEV (YAMAHA, 724,  "YMF724"),	DEV (YAMAHA, 724F, "YMF724F"),	DEV (YAMAHA, 740,  "YMF740"),	DEV (YAMAHA, 740C, "YMF740C"),	DEV (YAMAHA, 744,  "YMF744"),	DEV (YAMAHA, 754,  "YMF754"),#undef DEV	{ }};MODULE_DEVICE_TABLE(pci, ymf_id_tbl);/* *  common I/O routines */static inline u8 ymfpci_readb(ymfpci_t *codec, u32 offset){	return readb(codec->reg_area_virt + offset);}static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val){	writeb(val, codec->reg_area_virt + offset);}static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset){	return readw(codec->reg_area_virt + offset);}static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val){	writew(val, codec->reg_area_virt + offset);}static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset){	return readl(codec->reg_area_virt + offset);}static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val){	writel(val, codec->reg_area_virt + offset);}static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched){	signed long end_time;	u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;		end_time = jiffies + 3 * (HZ / 4);	do {		if ((ymfpci_readw(codec, reg) & 0x8000) == 0)			return 0;		if (sched) {			set_current_state(TASK_UNINTERRUPTIBLE);			schedule_timeout(1);		}	} while (end_time - (signed long)jiffies >= 0);	printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n",	    secondary, ymfpci_readw(codec, reg));	return -EBUSY;}static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val){	ymfpci_t *codec = dev->private_data;	u32 cmd;	/* XXX Do make use of dev->id */	ymfpci_codec_ready(codec, 0, 0);	cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;	ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd);}static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg){	ymfpci_t *unit = dev->private_data;	int i;	if (ymfpci_codec_ready(unit, 0, 0))		return ~0;	ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);	if (ymfpci_codec_ready(unit, 0, 0))		return ~0;	if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) {		for (i = 0; i < 600; i++)			ymfpci_readw(unit, YDSXGR_PRISTATUSDATA);	}	return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA);}/* *  Misc routines *//* * Calculate the actual sampling rate relatetively to the base clock (48kHz). */static u32 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) / 48000) << 12;	}}static u32 def_rate[8] = {	100, 2000, 8000, 11025, 16000, 22050, 32000, 48000};static u32 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 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];}static u32 ymf_calc_lend(u32 rate){	return (rate * YMF_SAMPF) / 48000;}/* * We ever allow only a few formats, but let's be generic, for smaller surprise. */static int ymf_pcm_format_width(int format){	static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE;	if ((format & (format-1)) != 0) {		printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format);		return 8;	}	if (format == AFMT_IMA_ADPCM) return 4;	if ((format & mask16) != 0) return 16;	return 8;}static void ymf_pcm_update_shift(struct ymf_pcm_format *f){	f->shift = 0;	if (f->voices == 2)		f->shift++;	if (ymf_pcm_format_width(f->format) == 16)		f->shift++;}/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)#define DMABUF_MINORDER 1/* allocate DMA buffer, playback and recording buffer should be allocated seperately */static int alloc_dmabuf(struct ymf_dmabuf *dmabuf){	void *rawbuf = NULL;	int order;	struct page * map,  * mapend;	/* alloc as big a chunk as we can */	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)		if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))			break;	if (!rawbuf)		return -ENOMEM;#if 0	printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n",	       PAGE_SIZE << order, order, rawbuf);#endif	dmabuf->ready  = dmabuf->mapped = 0;	dmabuf->rawbuf = rawbuf;	dmabuf->buforder = order;	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */	mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);	for (map = virt_to_page(rawbuf); map <= mapend; map++)		set_bit(PG_reserved, &map->flags);	return 0;}/* free DMA buffer */static void dealloc_dmabuf(struct ymf_dmabuf *dmabuf){	struct page *map, *mapend;	if (dmabuf->rawbuf) {		/* undo marking the pages as reserved */		mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);		for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)			clear_bit(PG_reserved, &map->flags);		free_pages((unsigned long)dmabuf->rawbuf,dmabuf->buforder);	}	dmabuf->rawbuf = NULL;	dmabuf->mapped = dmabuf->ready = 0;}static int prog_dmabuf(struct ymf_state *state, int rec){	struct ymf_dmabuf *dmabuf;	int w_16;	unsigned bufsize;	unsigned long flags;	int redzone, redfrags;	int ret;	w_16 = ymf_pcm_format_width(state->format.format) == 16;	dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf;	spin_lock_irqsave(&state->unit->reg_lock, flags);	dmabuf->hwptr = dmabuf->swptr = 0;	dmabuf->total_bytes = 0;	dmabuf->count = 0;	spin_unlock_irqrestore(&state->unit->reg_lock, flags);	/* allocate DMA buffer if not allocated yet */	if (!dmabuf->rawbuf)		if ((ret = alloc_dmabuf(dmabuf)))			return ret;	/*	 * Create fake fragment sizes and numbers for OSS ioctls.	 * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT.	 */	bufsize = PAGE_SIZE << dmabuf->buforder;	/* By default we give 4 big buffers. */	dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2);	if (dmabuf->ossfragshift > 3 &&	    dmabuf->ossfragshift < dmabuf->fragshift) {		/* If OSS set smaller fragments, give more smaller buffers. */		dmabuf->fragshift = dmabuf->ossfragshift;	}	dmabuf->fragsize = 1 << dmabuf->fragshift;	dmabuf->numfrag = bufsize >> dmabuf->fragshift;	dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;	if (dmabuf->ossmaxfrags >= 2) {		redzone = ymf_calc_lend(state->format.rate);		redzone <<= state->format.shift;		redzone *= 3;		redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift;		if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) {			dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags;			dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;		}	}	memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize);	/*	 *	Now set up the ring 	 */	/* XXX   ret = rec? cap_pre(): pbk_pre();  */	spin_lock_irqsave(&state->unit->voice_lock, flags);	if (rec) {		if ((ret = ymf_capture_prepare(state)) != 0) {			spin_unlock_irqrestore(&state->unit->voice_lock, flags);			return ret;		}	} else {		if ((ret = ymf_playback_prepare(state)) != 0) {			spin_unlock_irqrestore(&state->unit->voice_lock, flags);			return ret;		}	}	spin_unlock_irqrestore(&state->unit->voice_lock, flags);	/* set the ready flag for the dma buffer (this comment is not stupid) */	dmabuf->ready = 1;#if 0	printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x,"	    " numfrag %d fragsize %d dmasize %d\n",	       state->format.rate, state->format.format, dmabuf->numfrag,	       dmabuf->fragsize, dmabuf->dmasize);#endif	return 0;}static void ymf_start_dac(struct ymf_state *state){	ymf_playback_trigger(state->unit, &state->wpcm, 1);}// static void ymf_start_adc(struct ymf_state *state)// {// 	ymf_capture_trigger(state->unit, &state->rpcm, 1);// }/* * Wait until output is drained. * This does not kill the hardware for the sake of ioctls. */static void ymf_wait_dac(struct ymf_state *state){	struct ymf_unit *unit = state->unit;	struct ymf_pcm *ypcm = &state->wpcm;	DECLARE_WAITQUEUE(waita, current);	unsigned long flags;	add_wait_queue(&ypcm->dmabuf.wait, &waita);	spin_lock_irqsave(&unit->reg_lock, flags);	if (ypcm->dmabuf.count != 0 && !ypcm->running) {		ymf_playback_trigger(unit, ypcm, 1);	}#if 0	if (file->f_flags & O_NONBLOCK) {		/*		 * XXX Our  mistake is to attach DMA buffer to state		 * rather than to some per-device structure.		 * Cannot skip waiting, can only make it shorter.		 */	}#endif	set_current_state(TASK_UNINTERRUPTIBLE);	while (ypcm->running) {		spin_unlock_irqrestore(&unit->reg_lock, flags);		schedule();		spin_lock_irqsave(&unit->reg_lock, flags);		set_current_state(TASK_UNINTERRUPTIBLE);

⌨️ 快捷键说明

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