📄 via82cxxx_audio.c
字号:
/* * 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 + -