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

📄 via82cxxx_audio.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * Support for VIA 82Cxxx Audio Codecs * Copyright 1999,2000 Jeff Garzik * * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2. * See the "COPYING" file distributed with this software for more info. * * For a list of known bugs (errata) and documentation, * see via-audio.pdf in linux/Documentation/DocBook. * If this documentation does not exist, run "make pdfdocs". * */#define VIA_VERSION	"1.9.1"#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <linux/sound.h>#include <linux/poll.h>#include <linux/soundcard.h>#include <linux/ac97_codec.h>#include <linux/smp_lock.h>#include <linux/ioport.h>#include <linux/wrapper.h>#include <linux/delay.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/hardirq.h>#include <asm/semaphore.h>#include "sound_config.h"#include "dev_table.h"#include "mpu401.h"#undef VIA_DEBUG	/* define to enable debugging output and checks */#ifdef VIA_DEBUG/* note: prints function name for you */#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)#else#define DPRINTK(fmt, args...)#endif#undef VIA_NDEBUG	/* define to disable lightweight runtime checks */#ifdef VIA_NDEBUG#define assert(expr)#else#define assert(expr) \        if(!(expr)) {					\        printk( "Assertion failed! %s,%s,%s,line=%d\n",	\        #expr,__FILE__,__FUNCTION__,__LINE__);		\        }#endif#if defined(CONFIG_PROC_FS) && \    defined(CONFIG_SOUND_VIA82CXXX_PROCFS)#define VIA_PROC_FS 1#endif#define VIA_SUPPORT_MMAP 1 /* buggy, for now... */#define MAX_CARDS	1#define VIA_CARD_NAME	"VIA 82Cxxx Audio driver " VIA_VERSION#define VIA_MODULE_NAME "via82cxxx"#define PFX		VIA_MODULE_NAME ": "#define VIA_COUNTER_LIMIT	100000/* size of DMA buffers */#define VIA_MAX_BUFFER_DMA_PAGES	32/* buffering default values in ms */#define VIA_DEFAULT_FRAG_TIME		20#define VIA_DEFAULT_BUFFER_TIME		500#define VIA_MAX_FRAG_SIZE		PAGE_SIZE#define VIA_MIN_FRAG_SIZE		64#define VIA_MIN_FRAG_NUMBER		2/* 82C686 function 5 (audio codec) PCI configuration registers */#define VIA_ACLINK_STATUS	0x40#define VIA_ACLINK_CTRL		0x41#define VIA_FUNC_ENABLE		0x42#define VIA_PNP_CONTROL		0x43#define VIA_FM_NMI_CTRL		0x48/* * controller base 0 (scatter-gather) registers * * NOTE: Via datasheet lists first channel as "read" * channel and second channel as "write" channel. * I changed the naming of the constants to be more * clear than I felt the datasheet to be. */#define VIA_BASE0_PCM_OUT_CHAN	0x00 /* output PCM to user */#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00#define VIA_BASE0_PCM_OUT_CHAN_CTRL	0x01#define VIA_BASE0_PCM_OUT_CHAN_TYPE	0x02#define VIA_BASE0_PCM_IN_CHAN		0x10 /* input PCM from user */#define VIA_BASE0_PCM_IN_CHAN_STATUS	0x10#define VIA_BASE0_PCM_IN_CHAN_CTRL	0x11#define VIA_BASE0_PCM_IN_CHAN_TYPE	0x12/* offsets from base */#define VIA_PCM_STATUS			0x00#define VIA_PCM_CONTROL			0x01#define VIA_PCM_TYPE			0x02#define VIA_PCM_TABLE_ADDR		0x04#define VIA_PCM_BLOCK_COUNT		0x0C/* XXX unused DMA channel for FM PCM data */#define VIA_BASE0_FM_OUT_CHAN		0x20#define VIA_BASE0_FM_OUT_CHAN_STATUS	0x20#define VIA_BASE0_FM_OUT_CHAN_CTRL	0x21#define VIA_BASE0_FM_OUT_CHAN_TYPE	0x22#define VIA_BASE0_AC97_CTRL		0x80#define VIA_BASE0_SGD_STATUS_SHADOW	0x84#define VIA_BASE0_GPI_INT_ENABLE	0x8C#define VIA_INTR_OUT			((1<<0) |  (1<<4) |  (1<<8))#define VIA_INTR_IN			((1<<1) |  (1<<5) |  (1<<9))#define VIA_INTR_FM			((1<<2) |  (1<<6) | (1<<10))#define VIA_INTR_MASK		(VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM)/* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */#define VIA_IRQ_ON_FLAG			(1<<0)	/* int on each flagged scatter block */#define VIA_IRQ_ON_EOL			(1<<1)	/* int at end of scatter list */#define VIA_INT_SEL_PCI_LAST_LINE_READ	(0)	/* int at PCI read of last line */#define VIA_INT_SEL_LAST_SAMPLE_SENT	(1<<2)	/* int at last sample sent */#define VIA_INT_SEL_ONE_LINE_LEFT	(1<<3)	/* int at less than one line to send */#define VIA_PCM_FMT_STEREO		(1<<4)	/* PCM stereo format (bit clear == mono) */#define VIA_PCM_FMT_16BIT		(1<<5)	/* PCM 16-bit format (bit clear == 8-bit) */#define VIA_PCM_REC_FIFO		(1<<6)	/* PCM Recording FIFO */#define VIA_RESTART_SGD_ON_EOL		(1<<7)	/* restart scatter-gather at EOL */#define VIA_PCM_FMT_MASK		(VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT)#define VIA_CHAN_TYPE_MASK		(VIA_RESTART_SGD_ON_EOL | \					 VIA_IRQ_ON_FLAG | \					 VIA_IRQ_ON_EOL)#define VIA_CHAN_TYPE_INT_SELECT	(VIA_INT_SEL_LAST_SAMPLE_SENT)/* PCI configuration register bits and masks */#define VIA_CR40_AC97_READY	0x01#define VIA_CR40_AC97_LOW_POWER	0x02#define VIA_CR40_SECONDARY_READY 0x04#define VIA_CR41_AC97_ENABLE	0x80 /* enable AC97 codec */#define VIA_CR41_AC97_RESET	0x40 /* clear bit to reset AC97 */#define VIA_CR41_AC97_WAKEUP	0x20 /* wake up from power-down mode */#define VIA_CR41_AC97_SDO	0x10 /* force Serial Data Out (SDO) high */#define VIA_CR41_VRA		0x08 /* enable variable sample rate */#define VIA_CR41_PCM_ENABLE	0x04 /* AC Link SGD Read Channel PCM Data Output */#define VIA_CR41_FM_PCM_ENABLE	0x02 /* AC Link FM Channel PCM Data Out */#define VIA_CR41_SB_PCM_ENABLE	0x01 /* AC Link SB PCM Data Output */#define VIA_CR41_BOOT_MASK	(VIA_CR41_AC97_ENABLE | \				 VIA_CR41_AC97_WAKEUP | \				 VIA_CR41_AC97_SDO)#define VIA_CR41_RUN_MASK	(VIA_CR41_AC97_ENABLE | \				 VIA_CR41_AC97_RESET | \				 VIA_CR41_VRA | \				 VIA_CR41_PCM_ENABLE)#define VIA_CR42_SB_ENABLE	0x01#define VIA_CR42_MIDI_ENABLE	0x02#define VIA_CR42_FM_ENABLE	0x04#define VIA_CR42_GAME_ENABLE	0x08#define VIA_CR42_MIDI_IRQMASK   0x40#define VIA_CR42_MIDI_PNP	0x80#define VIA_CR44_SECOND_CODEC_SUPPORT	(1 << 6)#define VIA_CR44_AC_LINK_ACCESS		(1 << 7)#define VIA_CR48_FM_TRAP_TO_NMI		(1 << 2)/* controller base 0 register bitmasks */#define VIA_INT_DISABLE_MASK		(~(0x01|0x02))#define VIA_SGD_STOPPED			(1 << 2)#define VIA_SGD_PAUSED			(1 << 6)#define VIA_SGD_ACTIVE			(1 << 7)#define VIA_SGD_TERMINATE		(1 << 6)#define VIA_SGD_FLAG			(1 << 0)#define VIA_SGD_EOL			(1 << 1)#define VIA_SGD_START			(1 << 7)#define VIA_CR80_FIRST_CODEC		0#define VIA_CR80_SECOND_CODEC		(1 << 30)#define VIA_CR80_FIRST_CODEC_VALID	(1 << 25)#define VIA_CR80_VALID			(1 << 25)#define VIA_CR80_SECOND_CODEC_VALID	(1 << 27)#define VIA_CR80_BUSY			(1 << 24)#define VIA_CR83_BUSY			(1)#define VIA_CR83_FIRST_CODEC_VALID	(1 << 1)#define VIA_CR80_READ			(1 << 23)#define VIA_CR80_WRITE_MODE		0#define VIA_CR80_REG_IDX(idx)		((((idx) & 0xFF) >> 1) << 16)/* capabilities we announce */#ifdef VIA_SUPPORT_MMAP#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | DSP_CAP_MMAP | \		     DSP_CAP_TRIGGER | DSP_CAP_REALTIME)#else#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | \		     DSP_CAP_TRIGGER | DSP_CAP_REALTIME)#endif/* scatter-gather DMA table entry, exactly as passed to hardware */struct via_sgd_table {	u32 addr;	u32 count;	/* includes additional VIA_xxx bits also */};#define VIA_EOL (1 << 31)#define VIA_FLAG (1 << 30)#define VIA_STOP (1 << 29)enum via_channel_states {	sgd_stopped = 0,	sgd_in_progress = 1,};struct via_buffer_pgtbl {	dma_addr_t handle;	void *cpuaddr;};struct via_channel {	atomic_t n_frags;	atomic_t hw_ptr;	wait_queue_head_t wait;	unsigned int sw_ptr;	unsigned int slop_len;	unsigned int n_irqs;	int bytes;	unsigned is_active : 1;	unsigned is_record : 1;	unsigned is_mapped : 1;	unsigned is_enabled : 1;	u8 pcm_fmt;		/* VIA_PCM_FMT_xxx */	unsigned rate;		/* sample rate */	unsigned int frag_size;	unsigned int frag_number;	volatile struct via_sgd_table *sgtable;	dma_addr_t sgt_handle;	unsigned int page_number;	struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES];	long iobase;	const char *name;};/* data stored for each chip */struct via_info {	struct pci_dev *pdev;	long baseaddr;	struct ac97_codec ac97;	spinlock_t lock;	int card_num;		/* unique card number, from 0 */	int dev_dsp;		/* /dev/dsp index from register_sound_dsp() */	unsigned rev_h : 1;	int locked_rate : 1;	struct semaphore syscall_sem;	struct semaphore open_sem;	struct via_channel ch_in;	struct via_channel ch_out;	struct via_channel ch_fm;#ifdef CONFIG_MIDI_VIA82CXXX        void *midi_devc;        struct address_info midi_info;#endif};/* number of cards, used for assigning unique numbers to cards */static unsigned via_num_cards = 0;/**************************************************************** * * prototypes * * */static int via_init_one (struct pci_dev *dev, const struct pci_device_id *id);static void __devexit via_remove_one (struct pci_dev *pdev);static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos);static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos);static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait);static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int via_dsp_open (struct inode *inode, struct file *file);static int via_dsp_release(struct inode *inode, struct file *file);static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma);static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg);static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value);static u8 via_ac97_wait_idle (struct via_info *card);static void via_chan_free (struct via_info *card, struct via_channel *chan);static void via_chan_clear (struct via_info *card, struct via_channel *chan);static void via_chan_pcm_fmt (struct via_channel *chan, int reset);static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan);#ifdef VIA_PROC_FSstatic int via_init_proc (void);static void via_cleanup_proc (void);static int via_card_init_proc (struct via_info *card);static void via_card_cleanup_proc (struct via_info *card);#elsestatic inline int via_init_proc (void) { return 0; }static inline void via_cleanup_proc (void) {}static inline int via_card_init_proc (struct via_info *card) { return 0; }static inline void via_card_cleanup_proc (struct via_info *card) {}#endif/**************************************************************** * * Various data the driver needs * * */static struct pci_device_id via_pci_tbl[] __initdata = {	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5,	  PCI_ANY_ID, PCI_ANY_ID, },	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5,	  PCI_ANY_ID, PCI_ANY_ID, },	{ 0, }};MODULE_DEVICE_TABLE(pci,via_pci_tbl);static struct pci_driver via_driver = {	name:		VIA_MODULE_NAME,	id_table:	via_pci_tbl,	probe:		via_init_one,	remove:		__devexit_p(via_remove_one),};/**************************************************************** * * Low-level base 0 register read/write helpers * * *//** *	via_chan_stop - Terminate DMA on specified PCM channel *	@iobase: PCI base address for SGD channel registers * *	Terminate scatter-gather DMA operation for given *	channel (derived from @iobase), if DMA is active. * *	Note that @iobase is not the PCI base address, *	but the PCI base address plus an offset to *	one of three PCM channels supported by the chip. * */static inline void via_chan_stop (long iobase){	if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE)		outb (VIA_SGD_TERMINATE, iobase + VIA_PCM_CONTROL);}/** *	via_chan_status_clear - Clear status flags on specified DMA channel *	@iobase: PCI base address for SGD channel registers * *	Clear any pending status flags for the given *	DMA channel (derived from @iobase), if any *	flags are asserted. * *	Note that @iobase is not the PCI base address, *	but the PCI base address plus an offset to *	one of three PCM channels supported by the chip. * */static inline void via_chan_status_clear (long iobase){	u8 tmp = inb (iobase + VIA_PCM_STATUS);	if (tmp != 0)		outb (tmp, iobase + VIA_PCM_STATUS);}/** *	sg_begin - Begin recording or playback on a PCM channel *	@chan: Channel for which DMA operation shall begin * *	Start scatter-gather DMA for the given channel. * */static inline void sg_begin (struct via_channel *chan){	outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL);}static int sg_active (long iobase){	u8 tmp = inb (iobase + VIA_PCM_STATUS);	if ((tmp & VIA_SGD_STOPPED) || (tmp & VIA_SGD_PAUSED)) {		printk(KERN_WARNING "via82cxxx warning: SG stopped or paused\n");		return 0;	}	if (tmp & VIA_SGD_ACTIVE)		return 1;	return 0;}/**************************************************************** * * Miscellaneous debris * * *//** *	via_syscall_down - down the card-specific syscell semaphore *	@card: Private info for specified board *	@nonblock: boolean, non-zero if O_NONBLOCK is set * *	Encapsulates standard method of acquiring the syscall sem. * *	Returns negative errno on error, or zero for success. */static inline int via_syscall_down (struct via_info *card, int nonblock){	/* Thomas Sailer:	 * EAGAIN is supposed to be used if IO is pending,	 * not if there is contention on some internal	 * synchronization primitive which should be	 * held only for a short time anyway	 */	nonblock = 0;	if (nonblock) {		if (down_trylock (&card->syscall_sem))			return -EAGAIN;	} else {		if (down_interruptible (&card->syscall_sem))			return -ERESTARTSYS;	}	return 0;}/** *	via_stop_everything - Stop all audio operations *	@card: Private info for specified board * *	Stops all DMA operations and interrupts, and clear *	any pending status bits resulting from those operations. */static void via_stop_everything (struct via_info *card){	u8 tmp, new_tmp;	DPRINTK ("ENTER\n");	assert (card != NULL);	/*	 * terminate any existing operations on audio read/write channels	 */	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);	via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);

⌨️ 快捷键说明

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