📄 forte.c
字号:
/* * forte.c - ForteMedia FM801 OSS Driver * * Written by Martin K. Petersen <mkp@mkp.net> * Copyright (C) 2002 Hewlett-Packard Company * Portions Copyright (C) 2003 Martin K. Petersen * * Latest version: http://mkp.net/forte/ * * Based upon the ALSA FM801 driver by Jaroslav Kysela and OSS drivers * by Thomas Sailer, Alan Cox, Zach Brown, and Jeff Garzik. Thanks * guys! * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * 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 <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/spinlock.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/sound.h>#include <linux/ac97_codec.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <asm/uaccess.h>#include <asm/io.h>#define DRIVER_NAME "forte"#define DRIVER_VERSION "$Id: forte.c,v 1.63 2003/03/01 05:32:42 mkp Exp $"#define PFX DRIVER_NAME ": "#undef M_DEBUG#ifdef M_DEBUG#define DPRINTK(args...) printk(KERN_WARNING args)#else#define DPRINTK(args...)#endif/* Card capabilities */#define FORTE_CAPS (DSP_CAP_MMAP | DSP_CAP_TRIGGER)/* Supported audio formats */#define FORTE_FMTS (AFMT_U8 | AFMT_S16_LE)/* Buffers */#define FORTE_MIN_FRAG_SIZE 256#define FORTE_MAX_FRAG_SIZE PAGE_SIZE#define FORTE_DEF_FRAG_SIZE 256#define FORTE_MIN_FRAGMENTS 2#define FORTE_MAX_FRAGMENTS 256#define FORTE_DEF_FRAGMENTS 2#define FORTE_MIN_BUF_MSECS 500#define FORTE_MAX_BUF_MSECS 1000/* PCI BARs */#define FORTE_PCM_VOL 0x00 /* PCM Output Volume */#define FORTE_FM_VOL 0x02 /* FM Output Volume */#define FORTE_I2S_VOL 0x04 /* I2S Volume */#define FORTE_REC_SRC 0x06 /* Record Source */#define FORTE_PLY_CTRL 0x08 /* Playback Control */#define FORTE_PLY_COUNT 0x0a /* Playback Count */#define FORTE_PLY_BUF1 0x0c /* Playback Buffer I */#define FORTE_PLY_BUF2 0x10 /* Playback Buffer II */#define FORTE_CAP_CTRL 0x14 /* Capture Control */#define FORTE_CAP_COUNT 0x16 /* Capture Count */#define FORTE_CAP_BUF1 0x18 /* Capture Buffer I */#define FORTE_CAP_BUF2 0x1c /* Capture Buffer II */#define FORTE_CODEC_CTRL 0x22 /* Codec Control */#define FORTE_I2S_MODE 0x24 /* I2S Mode Control */#define FORTE_VOLUME 0x26 /* Volume Up/Down/Mute Status */#define FORTE_I2C_CTRL 0x29 /* I2C Control */#define FORTE_AC97_CMD 0x2a /* AC'97 Command */#define FORTE_AC97_DATA 0x2c /* AC'97 Data */#define FORTE_MPU401_DATA 0x30 /* MPU401 Data */#define FORTE_MPU401_CMD 0x31 /* MPU401 Command */#define FORTE_GPIO_CTRL 0x52 /* General Purpose I/O Control */#define FORTE_GEN_CTRL 0x54 /* General Control */#define FORTE_IRQ_MASK 0x56 /* Interrupt Mask */#define FORTE_IRQ_STATUS 0x5a /* Interrupt Status */#define FORTE_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */#define FORTE_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */#define FORTE_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */#define FORTE_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */#define FORTE_POWERDOWN 0x70 /* Blocks Power Down Control */#define FORTE_CAP_OFFSET FORTE_CAP_CTRL - FORTE_PLY_CTRL#define FORTE_AC97_ADDR_SHIFT 10/* Playback and record control register bits */#define FORTE_BUF1_LAST (1<<1)#define FORTE_BUF2_LAST (1<<2)#define FORTE_START (1<<5)#define FORTE_PAUSE (1<<6)#define FORTE_IMMED_STOP (1<<7)#define FORTE_RATE_SHIFT 8#define FORTE_RATE_MASK (15 << FORTE_RATE_SHIFT)#define FORTE_CHANNELS_4 (1<<12) /* Playback only */#define FORTE_CHANNELS_6 (2<<12) /* Playback only */#define FORTE_CHANNELS_6MS (3<<12) /* Playback only */#define FORTE_CHANNELS_MASK (3<<12)#define FORTE_16BIT (1<<14)#define FORTE_STEREO (1<<15)/* IRQ status bits */#define FORTE_IRQ_PLAYBACK (1<<8)#define FORTE_IRQ_CAPTURE (1<<9)#define FORTE_IRQ_VOLUME (1<<14)#define FORTE_IRQ_MPU (1<<15)/* CODEC control */#define FORTE_CC_CODEC_RESET (1<<5)#define FORTE_CC_AC97_RESET (1<<6)/* AC97 cmd */#define FORTE_AC97_WRITE (0<<7)#define FORTE_AC97_READ (1<<7)#define FORTE_AC97_DP_INVALID (0<<8)#define FORTE_AC97_DP_VALID (1<<8)#define FORTE_AC97_PORT_RDY (0<<9)#define FORTE_AC97_PORT_BSY (1<<9)struct forte_channel { const char *name; unsigned short ctrl; /* Ctrl BAR contents */ unsigned long iobase; /* Ctrl BAR address */ wait_queue_head_t wait; void *buf; /* Buffer */ dma_addr_t buf_handle; /* Buffer handle */ unsigned int record; unsigned int format; unsigned int rate; unsigned int stereo; unsigned int frag_sz; /* Current fragment size */ unsigned int frag_num; /* Current # of fragments */ unsigned int frag_msecs; /* Milliseconds per frag */ unsigned int buf_sz; /* Current buffer size */ unsigned int hwptr; /* Tail */ unsigned int swptr; /* Head */ unsigned int filled_frags; /* Fragments currently full */ unsigned int next_buf; /* Index of next buffer */ unsigned int active; /* Channel currently in use */ unsigned int mapped; /* mmap */ unsigned int buf_pages; /* Real size of buffer */ unsigned int nr_irqs; /* Number of interrupts */ unsigned int bytes; /* Total bytes */ unsigned int residue; /* Partial fragment */};struct forte_chip { struct pci_dev *pci_dev; unsigned long iobase; int irq; struct semaphore open_sem; /* Device access */ spinlock_t lock; /* State */ spinlock_t ac97_lock; struct ac97_codec *ac97; int multichannel; int dsp; /* OSS handle */ int trigger; /* mmap I/O trigger */ struct forte_channel play; struct forte_channel rec;};static int channels[] = { 2, 4, 6, };static int rates[] = { 5500, 8000, 9600, 11025, 16000, 19200, 22050, 32000, 38400, 44100, 48000, };static struct forte_chip *forte;static int found;/* AC97 Codec -------------------------------------------------------------- *//** * forte_ac97_wait: * @chip: fm801 instance whose AC97 codec to wait on * * FIXME: * Stop busy-waiting */static inline intforte_ac97_wait (struct forte_chip *chip){ int i = 10000; while ( (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_PORT_BSY) && i-- ) cpu_relax(); return i == 0;}/** * forte_ac97_read: * @codec: AC97 codec to read from * @reg: register to read */u16forte_ac97_read (struct ac97_codec *codec, u8 reg){ u16 ret = 0; struct forte_chip *chip = codec->private_data; spin_lock (&chip->ac97_lock); /* Knock, knock */ if (forte_ac97_wait (chip)) { printk (KERN_ERR PFX "ac97_read: Serial bus busy\n"); goto out; } /* Send read command */ outw (reg | (1<<7), chip->iobase + FORTE_AC97_CMD); if (forte_ac97_wait (chip)) { printk (KERN_ERR PFX "ac97_read: Bus busy reading reg 0x%x\n", reg); goto out; } /* Sanity checking */ if (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_DP_INVALID) { printk (KERN_ERR PFX "ac97_read: Invalid data port"); goto out; } /* Fetch result */ ret = inw (chip->iobase + FORTE_AC97_DATA); out: spin_unlock (&chip->ac97_lock); return ret;}/** * forte_ac97_write: * @codec: AC97 codec to send command to * @reg: register to write * @val: value to write */voidforte_ac97_write (struct ac97_codec *codec, u8 reg, u16 val){ struct forte_chip *chip = codec->private_data; spin_lock (&chip->ac97_lock); /* Knock, knock */ if (forte_ac97_wait (chip)) { printk (KERN_ERR PFX "ac97_write: Serial bus busy\n"); goto out; } outw (val, chip->iobase + FORTE_AC97_DATA); outb (reg | FORTE_AC97_WRITE, chip->iobase + FORTE_AC97_CMD); /* Wait for completion */ if (forte_ac97_wait (chip)) { printk (KERN_ERR PFX "ac97_write: Bus busy after write\n"); goto out; } out: spin_unlock (&chip->ac97_lock);}/* Mixer ------------------------------------------------------------------- *//** * forte_mixer_open: * @inode: * @file: */static intforte_mixer_open (struct inode *inode, struct file *file){ struct forte_chip *chip = forte; file->private_data = chip->ac97; return 0;}/** * forte_mixer_release: * @inode: * @file: */static intforte_mixer_release (struct inode *inode, struct file *file){ /* We will welease Wodewick */ return 0;}/** * forte_mixer_ioctl: * @inode: * @file: */static intforte_mixer_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct ac97_codec *codec = (struct ac97_codec *) file->private_data; return codec->mixer_ioctl (codec, cmd, arg);}static struct file_operations forte_mixer_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .ioctl = forte_mixer_ioctl, .open = forte_mixer_open, .release = forte_mixer_release,};/* Channel ----------------------------------------------------------------- *//** * forte_channel_reset: * @channel: Channel to reset * * Locking: Must be called with lock held. */static voidforte_channel_reset (struct forte_channel *channel){ if (!channel || !channel->iobase) return; DPRINTK ("%s: channel = %s\n", __FUNCTION__, channel->name); channel->ctrl &= ~FORTE_START; outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); /* We always play at least two fragments, hence these defaults */ channel->hwptr = channel->frag_sz; channel->next_buf = 1; channel->swptr = 0; channel->filled_frags = 0; channel->active = 0; channel->bytes = 0; channel->nr_irqs = 0; channel->mapped = 0; channel->residue = 0;}/** * forte_channel_start: * @channel: Channel to start (record/playback) * * Locking: Must be called with lock held. */static void inlineforte_channel_start (struct forte_channel *channel){ if (!channel || !channel->iobase || channel->active) return; channel->ctrl &= ~(FORTE_PAUSE | FORTE_BUF1_LAST | FORTE_BUF2_LAST | FORTE_IMMED_STOP); channel->ctrl |= FORTE_START; channel->active = 1; outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL);}/** * forte_channel_stop: * @channel: Channel to stop * * Locking: Must be called with lock held. */static void inlineforte_channel_stop (struct forte_channel *channel){ if (!channel || !channel->iobase) return; channel->ctrl &= ~(FORTE_START | FORTE_PAUSE); channel->ctrl |= FORTE_IMMED_STOP; channel->active = 0; outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL);}/** * forte_channel_pause: * @channel: Channel to pause * * Locking: Must be called with lock held. */static void inlineforte_channel_pause (struct forte_channel *channel){ if (!channel || !channel->iobase) return; channel->ctrl |= FORTE_PAUSE; channel->active = 0; outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL);}/** * forte_channel_rate: * @channel: Channel whose rate to set. Playback and record are * independent. * @rate: Channel rate in Hz * * Locking: Must be called with lock held. */static intforte_channel_rate (struct forte_channel *channel, unsigned int rate){ int new_rate; if (!channel || !channel->iobase) return -EINVAL; /* The FM801 only supports a handful of fixed frequencies. * We find the value closest to what userland requested. */ if (rate <= 6250) { rate = 5500; new_rate = 0; } else if (rate <= 8800) { rate = 8000; new_rate = 1; } else if (rate <= 10312) { rate = 9600; new_rate = 2; } else if (rate <= 13512) { rate = 11025; new_rate = 3; } else if (rate <= 17600) { rate = 16000; new_rate = 4; } else if (rate <= 20625) { rate = 19200; new_rate = 5; } else if (rate <= 27025) { rate = 22050; new_rate = 6; } else if (rate <= 35200) { rate = 32000; new_rate = 7; } else if (rate <= 41250) { rate = 38400; new_rate = 8; } else if (rate <= 46050) { rate = 44100; new_rate = 9; } else { rate = 48000; new_rate = 10; } channel->ctrl &= ~FORTE_RATE_MASK; channel->ctrl |= new_rate << FORTE_RATE_SHIFT; channel->rate = rate; DPRINTK ("%s: %s rate = %d\n", __FUNCTION__, channel->name, rate); return rate;}/** * forte_channel_format: * @channel: Channel whose audio format to set * @format: OSS format ID * * Locking: Must be called with lock held. */static intforte_channel_format (struct forte_channel *channel, int format){ if (!channel || !channel->iobase) return -EINVAL; switch (format) { case AFMT_QUERY: break; case AFMT_U8: channel->ctrl &= ~FORTE_16BIT; channel->format = AFMT_U8; break; case AFMT_S16_LE: default: channel->ctrl |= FORTE_16BIT; channel->format = AFMT_S16_LE; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -