| /* |
| * 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 <linux/mutex.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 mutex open_mutex; /* 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 int |
| forte_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 |
| */ |
| |
| static u16 |
| forte_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 |
| */ |
| |
| static void |
| forte_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 int |
| forte_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 int |
| forte_mixer_release (struct inode *inode, struct file *file) |
| { |
| /* We will welease Wodewick */ |
| return 0; |
| } |
| |
| |
| /** |
| * forte_mixer_ioctl: |
| * @inode: |
| * @file: |
| */ |
| |
| static int |
| forte_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 void |
| forte_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 inline |
| forte_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 inline |
| forte_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 inline |
| forte_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 int |
| forte_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 int |
| forte_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; |
| } |
| |
| DPRINTK ("%s: %s want %d format, got %d\n", __FUNCTION__, channel->name, |
| format, channel->format); |
| |
| return channel->format; |
| } |
| |
| |
| /** |
| * forte_channel_stereo: |
| * @channel: Channel to toggle |
| * @stereo: 0 for Mono, 1 for Stereo |
| * |
| * Locking: Must be called with lock held. |
| */ |
| |
| static int |
| forte_channel_stereo (struct forte_channel *channel, unsigned int stereo) |
| { |
| int ret; |
| |
| if (!channel || !channel->iobase) |
| return -EINVAL; |
| |
| DPRINTK ("%s: %s stereo = %d\n", __FUNCTION__, channel->name, stereo); |
| |
| switch (stereo) { |
| |
| case 0: |
| channel->ctrl &= ~(FORTE_STEREO | FORTE_CHANNELS_MASK); |
| channel-> stereo = stereo; |
| ret = stereo; |
| break; |
| |
| case 1: |
| channel->ctrl &= ~FORTE_CHANNELS_MASK; |
| channel->ctrl |= FORTE_STEREO; |
| channel-> stereo = stereo; |
| ret = stereo; |
| break; |
| |
| default: |
| DPRINTK ("Unsupported channel format"); |
| ret = -EINVAL; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| |
| /** |
| * forte_channel_buffer: |
| * @channel: Channel whose buffer to set up |
| * |
| * Locking: Must be called with lock held. |
| */ |
| |
| static void |
| forte_channel_buffer (struct forte_channel *channel, int sz, int num) |
| { |
| unsigned int msecs, shift; |
| |
| /* Go away, I'm busy */ |
| if (channel->filled_frags || channel->bytes) |
| return; |
| |
| /* Fragment size must be a power of 2 */ |
| shift = 0; sz++; |
| while (sz >>= 1) |
| shift++; |
| channel->frag_sz = 1 << shift; |
| |
| /* Round fragment size to something reasonable */ |
| if (channel->frag_sz < FORTE_MIN_FRAG_SIZE) |
| channel->frag_sz = FORTE_MIN_FRAG_SIZE; |
| |
| if (channel->frag_sz > FORTE_MAX_FRAG_SIZE) |
| channel->frag_sz = FORTE_MAX_FRAG_SIZE; |
| |
| /* Find fragment length in milliseconds */ |
| msecs = channel->frag_sz / |
| (channel->format == AFMT_S16_LE ? 2 : 1) / |
| (channel->stereo ? 2 : 1) / |
| (channel->rate / 1000); |
| |
| channel->frag_msecs = msecs; |
| |
| /* Pick a suitable number of fragments */ |
| if (msecs * num < FORTE_MIN_BUF_MSECS) |
| num = FORTE_MIN_BUF_MSECS / msecs; |
| |
| if (msecs * num > FORTE_MAX_BUF_MSECS) |
| num = FORTE_MAX_BUF_MSECS / msecs; |
| |
| /* Fragment number must be a power of 2 */ |
| shift = 0; |
| while (num >>= 1) |
| shift++; |
| channel->frag_num = 1 << (shift + 1); |
| |
| /* Round fragment number to something reasonable */ |
| if (channel->frag_num < FORTE_MIN_FRAGMENTS) |
| channel->frag_num = FORTE_MIN_FRAGMENTS; |
| |
| if (channel->frag_num > FORTE_MAX_FRAGMENTS) |
| channel->frag_num = FORTE_MAX_FRAGMENTS; |
| |
| channel->buf_sz = channel->frag_sz * channel->frag_num; |
| |
| DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d\n", |
| __FUNCTION__, channel->name, channel->frag_sz, |
| channel->frag_num, channel->buf_sz); |
| } |
| |
| |
| /** |
| * forte_channel_prep: |
| * @channel: Channel whose buffer to prepare |
| * |
| * Locking: Lock held. |
| */ |
| |
| static void |
| forte_channel_prep (struct forte_channel *channel) |
| { |
| struct page *page; |
| int i; |
| |
| if (channel->buf) |
| return; |
| |
| forte_channel_buffer (channel, channel->frag_sz, channel->frag_num); |
| channel->buf_pages = channel->buf_sz >> PAGE_SHIFT; |
| |
| if (channel->buf_sz % PAGE_SIZE) |
| channel->buf_pages++; |
| |
| DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d, pg = %d\n", |
| __FUNCTION__, channel->name, channel->frag_sz, |
| channel->frag_num, channel->buf_sz, channel->buf_pages); |
| |
| /* DMA buffer */ |
| channel->buf = pci_alloc_consistent (forte->pci_dev, |
| channel->buf_pages * PAGE_SIZE, |
| &channel->buf_handle); |
| |
| if (!channel->buf || !channel->buf_handle) |
| BUG(); |
| |
| page = virt_to_page (channel->buf); |
| |
| /* FIXME: can this go away ? */ |
| for (i = 0 ; i < channel->buf_pages ; i++) |
| SetPageReserved(page++); |
| |
| /* Prep buffer registers */ |
| outw (channel->frag_sz - 1, channel->iobase + FORTE_PLY_COUNT); |
| outl (channel->buf_handle, channel->iobase + FORTE_PLY_BUF1); |
| outl (channel->buf_handle + channel->frag_sz, |
| channel->iobase + FORTE_PLY_BUF2); |
| |
| /* Reset hwptr */ |
| channel->hwptr = channel->frag_sz; |
| channel->next_buf = 1; |
| |
| DPRINTK ("%s: %s buffer @ %p (%p)\n", __FUNCTION__, channel->name, |
| channel->buf, channel->buf_handle); |
| } |
| |
| |
| /** |
| * forte_channel_drain: |
| * @chip: |
| * @channel: |
| * |
| * Locking: Don't hold the lock. |
| */ |
| |
| static inline int |
| forte_channel_drain (struct forte_channel *channel) |
| { |
| DECLARE_WAITQUEUE (wait, current); |
| unsigned long flags; |
| |
| DPRINTK ("%s\n", __FUNCTION__); |
| |
| if (channel->mapped) { |
| spin_lock_irqsave (&forte->lock, flags); |
| forte_channel_stop (channel); |
| spin_unlock_irqrestore (&forte->lock, flags); |
| return 0; |
| } |
| |
| spin_lock_irqsave (&forte->lock, flags); |
| add_wait_queue (&channel->wait, &wait); |
| |
| for (;;) { |
| if (channel->active == 0 || channel->filled_frags == 1) |
| break; |
| |
| spin_unlock_irqrestore (&forte->lock, flags); |
| |
| __set_current_state (TASK_INTERRUPTIBLE); |
| schedule(); |
| |
| spin_lock_irqsave (&forte->lock, flags); |
| } |
| |
| forte_channel_stop (channel); |
| forte_channel_reset (channel); |
| set_current_state (TASK_RUNNING); |
| remove_wait_queue (&channel->wait, &wait); |
| spin_unlock_irqrestore (&forte->lock, flags); |
| |
| return 0; |
| } |
| |
| |
| /** |
| * forte_channel_init: |
| * @chip: Forte chip instance the channel hangs off |
| * @channel: Channel to initialize |
| * |
| * Description: |
| * Initializes a channel, sets defaults, and allocates |
| * buffers. |
| * |
| * Locking: No lock held. |
| */ |
| |
| static int |
| forte_channel_init (struct forte_chip *chip, struct forte_channel *channel) |
| { |
| DPRINTK ("%s: chip iobase @ %p\n", __FUNCTION__, (void *)chip->iobase); |
| |
| spin_lock_irq (&chip->lock); |
| memset (channel, 0x0, sizeof (*channel)); |
| |
| if (channel == &chip->play) { |
| channel->name = "PCM_OUT"; |
| channel->iobase = chip->iobase; |
| DPRINTK ("%s: PCM-OUT iobase @ %p\n", __FUNCTION__, |
| (void *) channel->iobase); |
| } |
| else if (channel == &chip->rec) { |
| channel->name = "PCM_IN"; |
| channel->iobase = chip->iobase + FORTE_CAP_OFFSET; |
| channel->record = 1; |
| DPRINTK ("%s: PCM-IN iobase @ %p\n", __FUNCTION__, |
| (void *) channel->iobase); |
| } |
| else |
| BUG(); |
| |
| init_waitqueue_head (&channel->wait); |
| |
| /* Defaults: 48kHz, 16-bit, stereo */ |
| channel->ctrl = inw (channel->iobase + FORTE_PLY_CTRL); |
| forte_channel_reset (channel); |
| forte_channel_stereo (channel, 1); |
| forte_channel_format (channel, AFMT_S16_LE); |
| forte_channel_rate (channel, 48000); |
| channel->frag_sz = FORTE_DEF_FRAG_SIZE; |
| channel->frag_num = FORTE_DEF_FRAGMENTS; |
| |
| chip->trigger = 0; |
| spin_unlock_irq (&chip->lock); |
| |
| return 0; |
| } |
| |
| |
| /** |
| * forte_channel_free: |
| * @chip: Chip this channel hangs off |
| * @channel: Channel to nuke |
| * |
| * Description: |
| * Resets channel and frees buffers. |
| * |
| * Locking: Hold your horses. |
| */ |
| |
| static void |
| forte_channel_free (struct forte_chip *chip, struct forte_channel *channel) |
| { |
| DPRINTK ("%s: %s\n", __FUNCTION__, channel->name); |
| |
| if (!channel->buf_handle) |
| return; |
| |
| pci_free_consistent (chip->pci_dev, channel->buf_pages * PAGE_SIZE, |
| channel->buf, channel->buf_handle); |
| |
| memset (channel, 0x0, sizeof (*channel)); |
| } |
| |
| |
| /* DSP --------------------------------------------------------------------- */ |
| |
| |
| /** |
| * forte_dsp_ioctl: |
| */ |
| |
| static int |
| forte_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, |
| unsigned long arg) |
| { |
| int ival=0, ret, rval=0, rd, wr, count; |
| struct forte_chip *chip; |
| struct audio_buf_info abi; |
| struct count_info cinfo; |
| void __user *argp = (void __user *)arg; |
| int __user *p = argp; |
| |
| chip = file->private_data; |
| |
| if (file->f_mode & FMODE_WRITE) |
| wr = 1; |
| else |
| wr = 0; |
| |
| if (file->f_mode & FMODE_READ) |
| rd = 1; |
| else |
| rd = 0; |
| |
| switch (cmd) { |
| |
| case OSS_GETVERSION: |
| return put_user (SOUND_VERSION, p); |
| |
| case SNDCTL_DSP_GETCAPS: |
| DPRINTK ("%s: GETCAPS\n", __FUNCTION__); |
| |
| ival = FORTE_CAPS; /* DUPLEX */ |
| return put_user (ival, p); |
| |
| case SNDCTL_DSP_GETFMTS: |
| DPRINTK ("%s: GETFMTS\n", __FUNCTION__); |
| |
| ival = FORTE_FMTS; /* U8, 16LE */ |
| return put_user (ival, p); |
| |
| case SNDCTL_DSP_SETFMT: /* U8, 16LE */ |
| DPRINTK ("%s: SETFMT\n", __FUNCTION__); |
| |
| if (get_user (ival, p)) |
| return -EFAULT; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (rd) { |
| forte_channel_stop (&chip->rec); |
| rval = forte_channel_format (&chip->rec, ival); |
| } |
| |
| if (wr) { |
| forte_channel_stop (&chip->rec); |
| rval = forte_channel_format (&chip->play, ival); |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return put_user (rval, p); |
| |
| case SNDCTL_DSP_STEREO: /* 0 - mono, 1 - stereo */ |
| DPRINTK ("%s: STEREO\n", __FUNCTION__); |
| |
| if (get_user (ival, p)) |
| return -EFAULT; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (rd) { |
| forte_channel_stop (&chip->rec); |
| rval = forte_channel_stereo (&chip->rec, ival); |
| } |
| |
| if (wr) { |
| forte_channel_stop (&chip->rec); |
| rval = forte_channel_stereo (&chip->play, ival); |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return put_user (rval, p); |
| |
| case SNDCTL_DSP_CHANNELS: /* 1 - mono, 2 - stereo */ |
| DPRINTK ("%s: CHANNELS\n", __FUNCTION__); |
| |
| if (get_user (ival, p)) |
| return -EFAULT; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (rd) { |
| forte_channel_stop (&chip->rec); |
| rval = forte_channel_stereo (&chip->rec, ival-1) + 1; |
| } |
| |
| if (wr) { |
| forte_channel_stop (&chip->play); |
| rval = forte_channel_stereo (&chip->play, ival-1) + 1; |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return put_user (rval, p); |
| |
| case SNDCTL_DSP_SPEED: |
| DPRINTK ("%s: SPEED\n", __FUNCTION__); |
| |
| if (get_user (ival, p)) |
| return -EFAULT; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (rd) { |
| forte_channel_stop (&chip->rec); |
| rval = forte_channel_rate (&chip->rec, ival); |
| } |
| |
| if (wr) { |
| forte_channel_stop (&chip->play); |
| rval = forte_channel_rate (&chip->play, ival); |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return put_user(rval, p); |
| |
| case SNDCTL_DSP_GETBLKSIZE: |
| DPRINTK ("%s: GETBLKSIZE\n", __FUNCTION__); |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (rd) |
| ival = chip->rec.frag_sz; |
| |
| if (wr) |
| ival = chip->play.frag_sz; |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return put_user (ival, p); |
| |
| case SNDCTL_DSP_RESET: |
| DPRINTK ("%s: RESET\n", __FUNCTION__); |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (rd) |
| forte_channel_reset (&chip->rec); |
| |
| if (wr) |
| forte_channel_reset (&chip->play); |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return 0; |
| |
| case SNDCTL_DSP_SYNC: |
| DPRINTK ("%s: SYNC\n", __FUNCTION__); |
| |
| if (wr) |
| ret = forte_channel_drain (&chip->play); |
| |
| return 0; |
| |
| case SNDCTL_DSP_POST: |
| DPRINTK ("%s: POST\n", __FUNCTION__); |
| |
| if (wr) { |
| spin_lock_irq (&chip->lock); |
| |
| if (chip->play.filled_frags) |
| forte_channel_start (&chip->play); |
| |
| spin_unlock_irq (&chip->lock); |
| } |
| |
| return 0; |
| |
| case SNDCTL_DSP_SETFRAGMENT: |
| DPRINTK ("%s: SETFRAGMENT\n", __FUNCTION__); |
| |
| if (get_user (ival, p)) |
| return -EFAULT; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (rd) { |
| forte_channel_buffer (&chip->rec, ival & 0xffff, |
| (ival >> 16) & 0xffff); |
| ival = (chip->rec.frag_num << 16) + chip->rec.frag_sz; |
| } |
| |
| if (wr) { |
| forte_channel_buffer (&chip->play, ival & 0xffff, |
| (ival >> 16) & 0xffff); |
| ival = (chip->play.frag_num << 16) +chip->play.frag_sz; |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return put_user (ival, p); |
| |
| case SNDCTL_DSP_GETISPACE: |
| DPRINTK ("%s: GETISPACE\n", __FUNCTION__); |
| |
| if (!rd) |
| return -EINVAL; |
| |
| spin_lock_irq (&chip->lock); |
| |
| abi.fragstotal = chip->rec.frag_num; |
| abi.fragsize = chip->rec.frag_sz; |
| |
| if (chip->rec.mapped) { |
| abi.fragments = chip->rec.frag_num - 2; |
| abi.bytes = abi.fragments * abi.fragsize; |
| } |
| else { |
| abi.fragments = chip->rec.filled_frags; |
| abi.bytes = abi.fragments * abi.fragsize; |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return copy_to_user (argp, &abi, sizeof (abi)) ? -EFAULT : 0; |
| |
| case SNDCTL_DSP_GETIPTR: |
| DPRINTK ("%s: GETIPTR\n", __FUNCTION__); |
| |
| if (!rd) |
| return -EINVAL; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (chip->rec.active) |
| cinfo.ptr = chip->rec.hwptr; |
| else |
| cinfo.ptr = 0; |
| |
| cinfo.bytes = chip->rec.bytes; |
| cinfo.blocks = chip->rec.nr_irqs; |
| chip->rec.nr_irqs = 0; |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return copy_to_user (argp, &cinfo, sizeof (cinfo)) ? -EFAULT : 0; |
| |
| case SNDCTL_DSP_GETOSPACE: |
| if (!wr) |
| return -EINVAL; |
| |
| spin_lock_irq (&chip->lock); |
| |
| abi.fragstotal = chip->play.frag_num; |
| abi.fragsize = chip->play.frag_sz; |
| |
| if (chip->play.mapped) { |
| abi.fragments = chip->play.frag_num - 2; |
| abi.bytes = chip->play.buf_sz; |
| } |
| else { |
| abi.fragments = chip->play.frag_num - |
| chip->play.filled_frags; |
| |
| if (chip->play.residue) |
| abi.fragments--; |
| |
| abi.bytes = abi.fragments * abi.fragsize + |
| chip->play.residue; |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return copy_to_user (argp, &abi, sizeof (abi)) ? -EFAULT : 0; |
| |
| case SNDCTL_DSP_GETOPTR: |
| if (!wr) |
| return -EINVAL; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (chip->play.active) |
| cinfo.ptr = chip->play.hwptr; |
| else |
| cinfo.ptr = 0; |
| |
| cinfo.bytes = chip->play.bytes; |
| cinfo.blocks = chip->play.nr_irqs; |
| chip->play.nr_irqs = 0; |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return copy_to_user (argp, &cinfo, sizeof (cinfo)) ? -EFAULT : 0; |
| |
| case SNDCTL_DSP_GETODELAY: |
| if (!wr) |
| return -EINVAL; |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (!chip->play.active) { |
| ival = 0; |
| } |
| else if (chip->play.mapped) { |
| count = inw (chip->play.iobase + FORTE_PLY_COUNT) + 1; |
| ival = chip->play.frag_sz - count; |
| } |
| else { |
| ival = chip->play.filled_frags * chip->play.frag_sz; |
| |
| if (chip->play.residue) |
| ival += chip->play.frag_sz - chip->play.residue; |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| |
| return put_user (ival, p); |
| |
| case SNDCTL_DSP_SETDUPLEX: |
| DPRINTK ("%s: SETDUPLEX\n", __FUNCTION__); |
| |
| return -EINVAL; |
| |
| case SNDCTL_DSP_GETTRIGGER: |
| DPRINTK ("%s: GETTRIGGER\n", __FUNCTION__); |
| |
| return put_user (chip->trigger, p); |
| |
| case SNDCTL_DSP_SETTRIGGER: |
| |
| if (get_user (ival, p)) |
| return -EFAULT; |
| |
| DPRINTK ("%s: SETTRIGGER %d\n", __FUNCTION__, ival); |
| |
| if (wr) { |
| spin_lock_irq (&chip->lock); |
| |
| if (ival & PCM_ENABLE_OUTPUT) |
| forte_channel_start (&chip->play); |
| else { |
| chip->trigger = 1; |
| forte_channel_prep (&chip->play); |
| forte_channel_stop (&chip->play); |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| } |
| else if (rd) { |
| spin_lock_irq (&chip->lock); |
| |
| if (ival & PCM_ENABLE_INPUT) |
| forte_channel_start (&chip->rec); |
| else { |
| chip->trigger = 1; |
| forte_channel_prep (&chip->rec); |
| forte_channel_stop (&chip->rec); |
| } |
| |
| spin_unlock_irq (&chip->lock); |
| } |
| |
| return 0; |
| |
| case SOUND_PCM_READ_RATE: |
| DPRINTK ("%s: PCM_READ_RATE\n", __FUNCTION__); |
| return put_user (chip->play.rate, p); |
| |
| case SOUND_PCM_READ_CHANNELS: |
| DPRINTK ("%s: PCM_READ_CHANNELS\n", __FUNCTION__); |
| return put_user (chip->play.stereo, p); |
| |
| case SOUND_PCM_READ_BITS: |
| DPRINTK ("%s: PCM_READ_BITS\n", __FUNCTION__); |
| return put_user (chip->play.format, p); |
| |
| case SNDCTL_DSP_NONBLOCK: |
| DPRINTK ("%s: DSP_NONBLOCK\n", __FUNCTION__); |
| file->f_flags |= O_NONBLOCK; |
| return 0; |
| |
| default: |
| DPRINTK ("Unsupported ioctl: %x (%p)\n", cmd, argp); |
| break; |
| } |
| |
| return -EINVAL; |
| } |
| |
| |
| /** |
| * forte_dsp_open: |
| */ |
| |
| static int |
| forte_dsp_open (struct inode *inode, struct file *file) |
| { |
| struct forte_chip *chip = forte; /* FIXME: HACK FROM HELL! */ |
| |
| if (file->f_flags & O_NONBLOCK) { |
| if (!mutex_trylock(&chip->open_mutex)) { |
| DPRINTK ("%s: returning -EAGAIN\n", __FUNCTION__); |
| return -EAGAIN; |
| } |
| } |
| else { |
| if (mutex_lock_interruptible(&chip->open_mutex)) { |
| DPRINTK ("%s: returning -ERESTARTSYS\n", __FUNCTION__); |
| return -ERESTARTSYS; |
| } |
| } |
| |
| file->private_data = forte; |
| |
| DPRINTK ("%s: dsp opened by %d\n", __FUNCTION__, current->pid); |
| |
| if (file->f_mode & FMODE_WRITE) |
| forte_channel_init (forte, &forte->play); |
| |
| if (file->f_mode & FMODE_READ) |
| forte_channel_init (forte, &forte->rec); |
| |
| return nonseekable_open(inode, file); |
| } |
| |
| |
| /** |
| * forte_dsp_release: |
| */ |
| |
| static int |
| forte_dsp_release (struct inode *inode, struct file *file) |
| { |
| struct forte_chip *chip = file->private_data; |
| int ret = 0; |
| |
| DPRINTK ("%s: chip @ %p\n", __FUNCTION__, chip); |
| |
| if (file->f_mode & FMODE_WRITE) { |
| forte_channel_drain (&chip->play); |
| |
| spin_lock_irq (&chip->lock); |
| |
| forte_channel_free (chip, &chip->play); |
| |
| spin_unlock_irq (&chip->lock); |
| } |
| |
| if (file->f_mode & FMODE_READ) { |
| while (chip->rec.filled_frags > 0) |
| interruptible_sleep_on (&chip->rec.wait); |
| |
| spin_lock_irq (&chip->lock); |
| |
| forte_channel_stop (&chip->rec); |
| forte_channel_free (chip, &chip->rec); |
| |
| spin_unlock_irq (&chip->lock); |
| } |
| |
| mutex_unlock(&chip->open_mutex); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * forte_dsp_poll: |
| * |
| */ |
| |
| static unsigned int |
| forte_dsp_poll (struct file *file, struct poll_table_struct *wait) |
| { |
| struct forte_chip *chip; |
| struct forte_channel *channel; |
| unsigned int mask = 0; |
| |
| chip = file->private_data; |
| |
| if (file->f_mode & FMODE_WRITE) { |
| channel = &chip->play; |
| |
| if (channel->active) |
| poll_wait (file, &channel->wait, wait); |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (channel->frag_num - channel->filled_frags > 0) |
| mask |= POLLOUT | POLLWRNORM; |
| |
| spin_unlock_irq (&chip->lock); |
| } |
| |
| if (file->f_mode & FMODE_READ) { |
| channel = &chip->rec; |
| |
| if (channel->active) |
| poll_wait (file, &channel->wait, wait); |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (channel->filled_frags > 0) |
| mask |= POLLIN | POLLRDNORM; |
| |
| spin_unlock_irq (&chip->lock); |
| } |
| |
| return mask; |
| } |
| |
| |
| /** |
| * forte_dsp_mmap: |
| */ |
| |
| static int |
| forte_dsp_mmap (struct file *file, struct vm_area_struct *vma) |
| { |
| struct forte_chip *chip; |
| struct forte_channel *channel; |
| unsigned long size; |
| int ret; |
| |
| chip = file->private_data; |
| |
| DPRINTK ("%s: start %lXh, size %ld, pgoff %ld\n", __FUNCTION__, |
| vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_pgoff); |
| |
| spin_lock_irq (&chip->lock); |
| |
| if (vma->vm_flags & VM_WRITE && chip->play.active) { |
| ret = -EBUSY; |
| goto out; |
| } |
| |
| if (vma->vm_flags & VM_READ && chip->rec.active) { |
| ret = -EBUSY; |
| goto out; |
| } |
| |
| if (file->f_mode & FMODE_WRITE) |
| channel = &chip->play; |
| else if (file->f_mode & FMODE_READ) |
| channel = &chip->rec; |
| else { |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| forte_channel_prep (channel); |
| channel->mapped = 1; |
| |
| if (vma->vm_pgoff != 0) { |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| size = vma->vm_end - vma->vm_start; |
| |
| if (size > channel->buf_pages * PAGE_SIZE) { |
| DPRINTK ("%s: size (%ld) > buf_sz (%d) \n", __FUNCTION__, |
| size, channel->buf_sz); |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| if (remap_pfn_range(vma, vma->vm_start, |
| virt_to_phys(channel->buf) >> PAGE_SHIFT, |
| size, vma->vm_page_prot)) { |
| DPRINTK ("%s: remap el a no worko\n", __FUNCTION__); |
| ret = -EAGAIN; |
| goto out; |
| } |
| |
| ret = 0; |
| |
| out: |
| spin_unlock_irq (&chip->lock); |
| return ret; |
| } |
| |
| |
| /** |
| * forte_dsp_write: |
| */ |
| |
| static ssize_t |
| forte_dsp_write (struct file *file, const char __user *buffer, size_t bytes, |
| loff_t *ppos) |
| { |
| struct forte_chip *chip; |
| struct forte_channel *channel; |
| unsigned int i = bytes, sz = 0; |
| unsigned long flags; |
| |
| if (!access_ok (VERIFY_READ, buffer, bytes)) |
| return -EFAULT; |
| |
| chip = (struct forte_chip *) file->private_data; |
| |
| if (!chip) |
| BUG(); |
| |
| channel = &chip->play; |
| |
| if (!channel) |
| BUG(); |
| |
| spin_lock_irqsave (&chip->lock, flags); |
| |
| /* Set up buffers with the right fragment size */ |
| forte_channel_prep (channel); |
| |
| while (i) { |
| /* All fragment buffers in use -> wait */ |
| if (channel->frag_num - channel->filled_frags == 0) { |
| DECLARE_WAITQUEUE (wait, current); |
| |
| /* For trigger or non-blocking operation, get out */ |
| if (chip->trigger || file->f_flags & O_NONBLOCK) { |
| spin_unlock_irqrestore (&chip->lock, flags); |
| return -EAGAIN; |
| } |
| |
| /* Otherwise wait for buffers */ |
| add_wait_queue (&channel->wait, &wait); |
| |
| for (;;) { |
| spin_unlock_irqrestore (&chip->lock, flags); |
| |
| set_current_state (TASK_INTERRUPTIBLE); |
| schedule(); |
| |
| spin_lock_irqsave (&chip->lock, flags); |
| |
| if (channel->frag_num - channel->filled_frags) |
| break; |
| } |
| |
| remove_wait_queue (&channel->wait, &wait); |
| set_current_state (TASK_RUNNING); |
| |
| if (signal_pending (current)) { |
| spin_unlock_irqrestore (&chip->lock, flags); |
| return -ERESTARTSYS; |
| } |
| } |
| |
| if (channel->residue) |
| sz = channel->residue; |
| else if (i > channel->frag_sz) |
| sz = channel->frag_sz; |
| else |
| sz = i; |
| |
| spin_unlock_irqrestore (&chip->lock, flags); |
| |
| if (copy_from_user ((void *) channel->buf + channel->swptr, buffer, sz)) |
| return -EFAULT; |
| |
| spin_lock_irqsave (&chip->lock, flags); |
| |
| /* Advance software pointer */ |
| buffer += sz; |
| channel->swptr += sz; |
| channel->swptr %= channel->buf_sz; |
| i -= sz; |
| |
| /* Only bump filled_frags if a full fragment has been written */ |
| if (channel->swptr % channel->frag_sz == 0) { |
| channel->filled_frags++; |
| channel->residue = 0; |
| } |
| else |
| channel->residue = channel->frag_sz - sz; |
| |
| /* If playback isn't active, start it */ |
| if (channel->active == 0 && chip->trigger == 0) |
| forte_channel_start (channel); |
| } |
| |
| spin_unlock_irqrestore (&chip->lock, flags); |
| |
| return bytes - i; |
| } |
| |
| |
| /** |
| * forte_dsp_read: |
| */ |
| |
| static ssize_t |
| forte_dsp_read (struct file *file, char __user *buffer, size_t bytes, |
| loff_t *ppos) |
| { |
| struct forte_chip *chip; |
| struct forte_channel *channel; |
| unsigned int i = bytes, sz; |
| unsigned long flags; |
| |
| if (!access_ok (VERIFY_WRITE, buffer, bytes)) |
| return -EFAULT; |
| |
| chip = (struct forte_chip *) file->private_data; |
| |
| if (!chip) |
| BUG(); |
| |
| channel = &chip->rec; |
| |
| if (!channel) |
| BUG(); |
| |
| spin_lock_irqsave (&chip->lock, flags); |
| |
| /* Set up buffers with the right fragment size */ |
| forte_channel_prep (channel); |
| |
| /* Start recording */ |
| if (!chip->trigger) |
| forte_channel_start (channel); |
| |
| while (i) { |
| /* No fragment buffers in use -> wait */ |
| if (channel->filled_frags == 0) { |
| DECLARE_WAITQUEUE (wait, current); |
| |
| /* For trigger mode operation, get out */ |
| if (chip->trigger) { |
| spin_unlock_irqrestore (&chip->lock, flags); |
| return -EAGAIN; |
| } |
| |
| add_wait_queue (&channel->wait, &wait); |
| |
| for (;;) { |
| if (channel->active == 0) |
| break; |
| |
| if (channel->filled_frags) |
| break; |
| |
| spin_unlock_irqrestore (&chip->lock, flags); |
| |
| set_current_state (TASK_INTERRUPTIBLE); |
| schedule(); |
| |
| spin_lock_irqsave (&chip->lock, flags); |
| } |
| |
| set_current_state (TASK_RUNNING); |
| remove_wait_queue (&channel->wait, &wait); |
| } |
| |
| if (i > channel->frag_sz) |
| sz = channel->frag_sz; |
| else |
| sz = i; |
| |
| spin_unlock_irqrestore (&chip->lock, flags); |
| |
| if (copy_to_user (buffer, (void *)channel->buf+channel->swptr, sz)) { |
| DPRINTK ("%s: copy_to_user failed\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| spin_lock_irqsave (&chip->lock, flags); |
| |
| /* Advance software pointer */ |
| buffer += sz; |
| if (channel->filled_frags > 0) |
| channel->filled_frags--; |
| channel->swptr += channel->frag_sz; |
| channel->swptr %= channel->buf_sz; |
| i -= sz; |
| } |
| |
| spin_unlock_irqrestore (&chip->lock, flags); |
| |
| return bytes - i; |
| } |
| |
| |
| static struct file_operations forte_dsp_fops = { |
| .owner = THIS_MODULE, |
| .llseek = &no_llseek, |
| .read = &forte_dsp_read, |
| .write = &forte_dsp_write, |
| .poll = &forte_dsp_poll, |
| .ioctl = &forte_dsp_ioctl, |
| .open = &forte_dsp_open, |
| .release = &forte_dsp_release, |
| .mmap = &forte_dsp_mmap, |
| }; |
| |
| |
| /* Common ------------------------------------------------------------------ */ |
| |
| |
| /** |
| * forte_interrupt: |
| */ |
| |
| static irqreturn_t |
| forte_interrupt (int irq, void *dev_id, struct pt_regs *regs) |
| { |
| struct forte_chip *chip = dev_id; |
| struct forte_channel *channel = NULL; |
| u16 status, count; |
| |
| status = inw (chip->iobase + FORTE_IRQ_STATUS); |
| |
| /* If this is not for us, get outta here ASAP */ |
| if ((status & (FORTE_IRQ_PLAYBACK | FORTE_IRQ_CAPTURE)) == 0) |
| return IRQ_NONE; |
| |
| if (status & FORTE_IRQ_PLAYBACK) { |
| channel = &chip->play; |
| |
| spin_lock (&chip->lock); |
| |
| if (channel->frag_sz == 0) |
| goto pack; |
| |
| /* Declare a fragment done */ |
| if (channel->filled_frags > 0) |
| channel->filled_frags--; |
| channel->bytes += channel->frag_sz; |
| channel->nr_irqs++; |
| |
| /* Flip-flop between buffer I and II */ |
| channel->next_buf ^= 1; |
| |
| /* Advance hardware pointer by fragment size and wrap around */ |
| channel->hwptr += channel->frag_sz; |
| channel->hwptr %= channel->buf_sz; |
| |
| /* Buffer I or buffer II BAR */ |
| outl (channel->buf_handle + channel->hwptr, |
| channel->next_buf == 0 ? |
| channel->iobase + FORTE_PLY_BUF1 : |
| channel->iobase + FORTE_PLY_BUF2); |
| |
| /* If the currently playing fragment is last, schedule pause */ |
| if (channel->filled_frags == 1) |
| forte_channel_pause (channel); |
| |
| pack: |
| /* Acknowledge interrupt */ |
| outw (FORTE_IRQ_PLAYBACK, chip->iobase + FORTE_IRQ_STATUS); |
| |
| if (waitqueue_active (&channel->wait)) |
| wake_up_all (&channel->wait); |
| |
| spin_unlock (&chip->lock); |
| } |
| |
| if (status & FORTE_IRQ_CAPTURE) { |
| channel = &chip->rec; |
| spin_lock (&chip->lock); |
| |
| /* One fragment filled */ |
| channel->filled_frags++; |
| |
| /* Get # of completed bytes */ |
| count = inw (channel->iobase + FORTE_PLY_COUNT) + 1; |
| |
| if (count == 0) { |
| DPRINTK ("%s: last, filled_frags = %d\n", __FUNCTION__, |
| channel->filled_frags); |
| channel->filled_frags = 0; |
| goto rack; |
| } |
| |
| /* Buffer I or buffer II BAR */ |
| outl (channel->buf_handle + channel->hwptr, |
| channel->next_buf == 0 ? |
| channel->iobase + FORTE_PLY_BUF1 : |
| channel->iobase + FORTE_PLY_BUF2); |
| |
| /* Flip-flop between buffer I and II */ |
| channel->next_buf ^= 1; |
| |
| /* Advance hardware pointer by fragment size and wrap around */ |
| channel->hwptr += channel->frag_sz; |
| channel->hwptr %= channel->buf_sz; |
| |
| /* Out of buffers */ |
| if (channel->filled_frags == channel->frag_num - 1) |
| forte_channel_stop (channel); |
| rack: |
| /* Acknowledge interrupt */ |
| outw (FORTE_IRQ_CAPTURE, chip->iobase + FORTE_IRQ_STATUS); |
| |
| spin_unlock (&chip->lock); |
| |
| if (waitqueue_active (&channel->wait)) |
| wake_up_all (&channel->wait); |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| |
| /** |
| * forte_proc_read: |
| */ |
| |
| static int |
| forte_proc_read (char *page, char **start, off_t off, int count, |
| int *eof, void *data) |
| { |
| int i = 0, p_rate, p_chan, r_rate; |
| unsigned short p_reg, r_reg; |
| |
| i += sprintf (page, "ForteMedia FM801 OSS Lite driver\n%s\n \n", |
| DRIVER_VERSION); |
| |
| if (!forte->iobase) |
| return i; |
| |
| p_rate = p_chan = -1; |
| p_reg = inw (forte->iobase + FORTE_PLY_CTRL); |
| p_rate = (p_reg >> 8) & 15; |
| p_chan = (p_reg >> 12) & 3; |
| |
| if (p_rate >= 0 || p_rate <= 10) |
| p_rate = rates[p_rate]; |
| |
| if (p_chan >= 0 || p_chan <= 2) |
| p_chan = channels[p_chan]; |
| |
| r_rate = -1; |
| r_reg = inw (forte->iobase + FORTE_CAP_CTRL); |
| r_rate = (r_reg >> 8) & 15; |
| |
| if (r_rate >= 0 || r_rate <= 10) |
| r_rate = rates[r_rate]; |
| |
| i += sprintf (page + i, |
| " Playback Capture\n" |
| "FIFO empty : %-3s %-3s\n" |
| "Buf1 Last : %-3s %-3s\n" |
| "Buf2 Last : %-3s %-3s\n" |
| "Started : %-3s %-3s\n" |
| "Paused : %-3s %-3s\n" |
| "Immed Stop : %-3s %-3s\n" |
| "Rate : %-5d %-5d\n" |
| "Channels : %-5d -\n" |
| "16-bit : %-3s %-3s\n" |
| "Stereo : %-3s %-3s\n" |
| " \n" |
| "Buffer Sz : %-6d %-6d\n" |
| "Frag Sz : %-6d %-6d\n" |
| "Frag Num : %-6d %-6d\n" |
| "Frag msecs : %-6d %-6d\n" |
| "Used Frags : %-6d %-6d\n" |
| "Mapped : %-3s %-3s\n", |
| p_reg & 1<<0 ? "yes" : "no", |
| r_reg & 1<<0 ? "yes" : "no", |
| p_reg & 1<<1 ? "yes" : "no", |
| r_reg & 1<<1 ? "yes" : "no", |
| p_reg & 1<<2 ? "yes" : "no", |
| r_reg & 1<<2 ? "yes" : "no", |
| p_reg & 1<<5 ? "yes" : "no", |
| r_reg & 1<<5 ? "yes" : "no", |
| p_reg & 1<<6 ? "yes" : "no", |
| r_reg & 1<<6 ? "yes" : "no", |
| p_reg & 1<<7 ? "yes" : "no", |
| r_reg & 1<<7 ? "yes" : "no", |
| p_rate, r_rate, |
| p_chan, |
| p_reg & 1<<14 ? "yes" : "no", |
| r_reg & 1<<14 ? "yes" : "no", |
| p_reg & 1<<15 ? "yes" : "no", |
| r_reg & 1<<15 ? "yes" : "no", |
| forte->play.buf_sz, forte->rec.buf_sz, |
| forte->play.frag_sz, forte->rec.frag_sz, |
| forte->play.frag_num, forte->rec.frag_num, |
| forte->play.frag_msecs, forte->rec.frag_msecs, |
| forte->play.filled_frags, forte->rec.filled_frags, |
| forte->play.mapped ? "yes" : "no", |
| forte->rec.mapped ? "yes" : "no" |
| ); |
| |
| return i; |
| } |
| |
| |
| /** |
| * forte_proc_init: |
| * |
| * Creates driver info entries in /proc |
| */ |
| |
| static int __init |
| forte_proc_init (void) |
| { |
| if (!proc_mkdir ("driver/forte", NULL)) |
| return -EIO; |
| |
| if (!create_proc_read_entry ("driver/forte/chip", 0, NULL, forte_proc_read, forte)) { |
| remove_proc_entry ("driver/forte", NULL); |
| return -EIO; |
| } |
| |
| if (!create_proc_read_entry("driver/forte/ac97", 0, NULL, ac97_read_proc, forte->ac97)) { |
| remove_proc_entry ("driver/forte/chip", NULL); |
| remove_proc_entry ("driver/forte", NULL); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| |
| /** |
| * forte_proc_remove: |
| * |
| * Removes driver info entries in /proc |
| */ |
| |
| static void |
| forte_proc_remove (void) |
| { |
| remove_proc_entry ("driver/forte/ac97", NULL); |
| remove_proc_entry ("driver/forte/chip", NULL); |
| remove_proc_entry ("driver/forte", NULL); |
| } |
| |
| |
| /** |
| * forte_chip_init: |
| * @chip: Chip instance to initialize |
| * |
| * Description: |
| * Resets chip, configures codec and registers the driver with |
| * the sound subsystem. |
| * |
| * Press and hold Start for 8 secs, then switch on Run |
| * and hold for 4 seconds. Let go of Start. Numbers |
| * assume a properly oiled TWG. |
| */ |
| |
| static int __devinit |
| forte_chip_init (struct forte_chip *chip) |
| { |
| u8 revision; |
| u16 cmdw; |
| struct ac97_codec *codec; |
| |
| pci_read_config_byte (chip->pci_dev, PCI_REVISION_ID, &revision); |
| |
| if (revision >= 0xB1) { |
| chip->multichannel = 1; |
| printk (KERN_INFO PFX "Multi-channel device detected.\n"); |
| } |
| |
| /* Reset chip */ |
| outw (FORTE_CC_CODEC_RESET | FORTE_CC_AC97_RESET, |
| chip->iobase + FORTE_CODEC_CTRL); |
| udelay(100); |
| outw (0, chip->iobase + FORTE_CODEC_CTRL); |
| |
| /* Request read from AC97 */ |
| outw (FORTE_AC97_READ | (0 << FORTE_AC97_ADDR_SHIFT), |
| chip->iobase + FORTE_AC97_CMD); |
| mdelay(750); |
| |
| if ((inw (chip->iobase + FORTE_AC97_CMD) & (3<<8)) != (1<<8)) { |
| printk (KERN_INFO PFX "AC97 codec not responding"); |
| return -EIO; |
| } |
| |
| /* Init volume */ |
| outw (0x0808, chip->iobase + FORTE_PCM_VOL); |
| outw (0x9f1f, chip->iobase + FORTE_FM_VOL); |
| outw (0x8808, chip->iobase + FORTE_I2S_VOL); |
| |
| /* I2S control - I2S mode */ |
| outw (0x0003, chip->iobase + FORTE_I2S_MODE); |
| |
| /* Interrupt setup - unmask PLAYBACK & CAPTURE */ |
| cmdw = inw (chip->iobase + FORTE_IRQ_MASK); |
| cmdw &= ~0x0003; |
| outw (cmdw, chip->iobase + FORTE_IRQ_MASK); |
| |
| /* Interrupt clear */ |
| outw (FORTE_IRQ_PLAYBACK|FORTE_IRQ_CAPTURE, |
| chip->iobase + FORTE_IRQ_STATUS); |
| |
| /* Set up the AC97 codec */ |
| if ((codec = ac97_alloc_codec()) == NULL) |
| return -ENOMEM; |
| codec->private_data = chip; |
| codec->codec_read = forte_ac97_read; |
| codec->codec_write = forte_ac97_write; |
| codec->id = 0; |
| |
| if (ac97_probe_codec (codec) == 0) { |
| printk (KERN_ERR PFX "codec probe failed\n"); |
| ac97_release_codec(codec); |
| return -1; |
| } |
| |
| /* Register mixer */ |
| if ((codec->dev_mixer = |
| register_sound_mixer (&forte_mixer_fops, -1)) < 0) { |
| printk (KERN_ERR PFX "couldn't register mixer!\n"); |
| ac97_release_codec(codec); |
| return -1; |
| } |
| |
| chip->ac97 = codec; |
| |
| /* Register DSP */ |
| if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1) ) < 0) { |
| printk (KERN_ERR PFX "couldn't register dsp!\n"); |
| return -1; |
| } |
| |
| /* Register with /proc */ |
| if (forte_proc_init()) { |
| printk (KERN_ERR PFX "couldn't add entries to /proc!\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| /** |
| * forte_probe: |
| * @pci_dev: PCI struct for probed device |
| * @pci_id: |
| * |
| * Description: |
| * Allocates chip instance, I/O region, and IRQ |
| */ |
| static int __init |
| forte_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id) |
| { |
| struct forte_chip *chip; |
| int ret = 0; |
| |
| /* FIXME: Support more than one chip */ |
| if (found++) |
| return -EIO; |
| |
| /* Ignition */ |
| if (pci_enable_device (pci_dev)) |
| return -EIO; |
| |
| pci_set_master (pci_dev); |
| |
| /* Allocate chip instance and configure */ |
| forte = (struct forte_chip *) |
| kmalloc (sizeof (struct forte_chip), GFP_KERNEL); |
| chip = forte; |
| |
| if (chip == NULL) { |
| printk (KERN_WARNING PFX "Out of memory"); |
| return -ENOMEM; |
| } |
| |
| memset (chip, 0, sizeof (struct forte_chip)); |
| chip->pci_dev = pci_dev; |
| |
| mutex_init(&chip->open_mutex); |
| spin_lock_init (&chip->lock); |
| spin_lock_init (&chip->ac97_lock); |
| |
| if (! request_region (pci_resource_start (pci_dev, 0), |
| pci_resource_len (pci_dev, 0), DRIVER_NAME)) { |
| printk (KERN_WARNING PFX "Unable to reserve I/O space"); |
| ret = -ENOMEM; |
| goto error; |
| } |
| |
| chip->iobase = pci_resource_start (pci_dev, 0); |
| chip->irq = pci_dev->irq; |
| |
| if (request_irq (chip->irq, forte_interrupt, SA_SHIRQ, DRIVER_NAME, |
| chip)) { |
| printk (KERN_WARNING PFX "Unable to reserve IRQ"); |
| ret = -EIO; |
| goto error; |
| } |
| |
| pci_set_drvdata (pci_dev, chip); |
| |
| printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%04lX IRQ %u\n", |
| chip->iobase, pci_resource_end (pci_dev, 0), chip->irq); |
| |
| /* Power it up */ |
| if ((ret = forte_chip_init (chip)) == 0) |
| return 0; |
| |
| error: |
| if (chip->irq) |
| free_irq (chip->irq, chip); |
| |
| if (chip->iobase) |
| release_region (pci_resource_start (pci_dev, 0), |
| pci_resource_len (pci_dev, 0)); |
| |
| kfree (chip); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * forte_remove: |
| * @pci_dev: PCI device to unclaim |
| * |
| */ |
| |
| static void |
| forte_remove (struct pci_dev *pci_dev) |
| { |
| struct forte_chip *chip = pci_get_drvdata (pci_dev); |
| |
| if (chip == NULL) |
| return; |
| |
| /* Turn volume down to avoid popping */ |
| outw (0x1f1f, chip->iobase + FORTE_PCM_VOL); |
| outw (0x1f1f, chip->iobase + FORTE_FM_VOL); |
| outw (0x1f1f, chip->iobase + FORTE_I2S_VOL); |
| |
| forte_proc_remove(); |
| free_irq (chip->irq, chip); |
| release_region (chip->iobase, pci_resource_len (pci_dev, 0)); |
| |
| unregister_sound_dsp (chip->dsp); |
| unregister_sound_mixer (chip->ac97->dev_mixer); |
| ac97_release_codec(chip->ac97); |
| kfree (chip); |
| |
| printk (KERN_INFO PFX "driver released\n"); |
| } |
| |
| |
| static struct pci_device_id forte_pci_ids[] = { |
| { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, |
| { 0, } |
| }; |
| |
| |
| static struct pci_driver forte_pci_driver = { |
| .name = DRIVER_NAME, |
| .id_table = forte_pci_ids, |
| .probe = forte_probe, |
| .remove = forte_remove, |
| |
| }; |
| |
| |
| /** |
| * forte_init_module: |
| * |
| */ |
| |
| static int __init |
| forte_init_module (void) |
| { |
| printk (KERN_INFO PFX DRIVER_VERSION "\n"); |
| |
| return pci_register_driver (&forte_pci_driver); |
| } |
| |
| |
| /** |
| * forte_cleanup_module: |
| * |
| */ |
| |
| static void __exit |
| forte_cleanup_module (void) |
| { |
| pci_unregister_driver (&forte_pci_driver); |
| } |
| |
| |
| module_init(forte_init_module); |
| module_exit(forte_cleanup_module); |
| |
| MODULE_AUTHOR("Martin K. Petersen <mkp@mkp.net>"); |
| MODULE_DESCRIPTION("ForteMedia FM801 OSS Driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_DEVICE_TABLE (pci, forte_pci_ids); |