2005-04-16 15:20:36 -07:00
/*
* 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>
2006-03-23 03:00:39 -08:00
# include <linux/mutex.h>
2005-04-16 15:20:36 -07:00
# 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 ;
2006-03-23 03:00:39 -08:00
struct mutex open_mutex ; /* Device access */
2005-04-16 15:20:36 -07:00
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 ) {
2006-03-23 03:00:39 -08:00
if ( ! mutex_trylock ( & chip - > open_mutex ) ) {
2005-04-16 15:20:36 -07:00
DPRINTK ( " %s: returning -EAGAIN \n " , __FUNCTION__ ) ;
return - EAGAIN ;
}
}
else {
2006-03-23 03:00:39 -08:00
if ( mutex_lock_interruptible ( & chip - > open_mutex ) ) {
2005-04-16 15:20:36 -07:00
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 ) ;
}
2006-03-23 03:00:39 -08:00
mutex_unlock ( & chip - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
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 ;
2006-03-23 03:00:39 -08:00
mutex_init ( & chip - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
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 ;
2006-07-01 19:29:46 -07:00
if ( request_irq ( chip - > irq , forte_interrupt , IRQF_SHARED , DRIVER_NAME ,
2005-04-16 15:20:36 -07:00
chip ) ) {
printk ( KERN_WARNING PFX " Unable to reserve IRQ " ) ;
ret = - EIO ;
goto error ;
}
pci_set_drvdata ( pci_dev , chip ) ;
2006-06-12 14:50:27 -07:00
printk ( KERN_INFO PFX " FM801 chip found at 0x%04lX-0x%16llX IRQ %u \n " ,
chip - > iobase , ( unsigned long long ) pci_resource_end ( pci_dev , 0 ) ,
chip - > irq ) ;
2005-04-16 15:20:36 -07:00
/* 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 ) ;