2005-04-16 15:20:36 -07:00
/*****************************************************************************
*
* ESS Maestro3 / Allegro driver for Linux 2.4 . x
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* ( c ) Copyright 2000 Zach Brown < zab @ zabbo . net >
*
* I need to thank many people for helping make this driver happen .
* As always , Eric Brombaugh was a hacking machine and killed many bugs
* that I was too dumb to notice . Howard Kim at ESS provided reference boards
* and as much docs as he could . Todd and Mick at Dell tested snapshots on
* an army of laptops . msw and deviant at Red Hat also humoured me by hanging
* their laptops every few hours in the name of science .
*
* Shouts go out to Mike " DJ XPCom " Ang .
*
* History
* v1 .23 - Jun 5 2002 - Michael Olson < olson @ cs . odu . edu >
* added a module option to allow selection of GPIO pin number
* for external amp
* v1 .22 - Feb 28 2001 - Zach Brown < zab @ zabbo . net >
* allocate mem at insmod / setup , rather than open
* limit pci dma addresses to 28 bit , thanks guys .
* v1 .21 - Feb 04 2001 - Zach Brown < zab @ zabbo . net >
* fix up really dumb notifier - > suspend oops
* v1 .20 - Jan 30 2001 - Zach Brown < zab @ zabbo . net >
* get rid of pm callback and use pci_dev suspend / resume instead
* m3_probe cleanups , including pm oops think - o
* v1 .10 - Jan 6 2001 - Zach Brown < zab @ zabbo . net >
* revert to lame remap_page_range mmap ( ) just to make it work
* record mmap fixed .
* fix up incredibly broken open / release resource management
* duh . fix record format setting .
* add SMP locking and cleanup formatting here and there
* v1 .00 - Dec 16 2000 - Zach Brown < zab @ zabbo . net >
* port to sexy 2.4 interfaces
* properly align instance allocations so recording works
* clean up function namespace a little : /
* update PCI IDs based on mail from ESS
* arbitrarily bump version number to show its 2.4 now ,
* 2.2 will stay 0. , oss_audio port gets 2.
* v0 .03 - Nov 05 2000 - Zach Brown < zab @ zabbo . net >
* disable recording but allow dsp to be opened read
* pull out most silly compat defines
* v0 .02 - Nov 04 2000 - Zach Brown < zab @ zabbo . net >
* changed clocking setup for m3 , slowdown fixed .
* codec reset is hopefully reliable now
* rudimentary apm / power management makes suspend / resume work
* v0 .01 - Oct 31 2000 - Zach Brown < zab @ zabbo . net >
* first release
* v0 .00 - Sep 09 2000 - Zach Brown < zab @ zabbo . net >
* first pass derivation from maestro . c
*
* TODO
* in / out allocated contiguously so fullduplex mmap will work ?
* no beep on init ( mute )
* resetup msrc data memory if freq changes ?
*
* - -
*
* Allow me to ramble a bit about the m3 architecture . The core of the
* chip is the ' assp ' , the custom ESS dsp that runs the show . It has
* a small amount of code and data ram . ESS drops binary dsp code images
* on our heads , but we don ' t get to see specs on the dsp .
*
* The constant piece of code on the dsp is the ' kernel ' . It also has a
* chunk of the dsp memory that is statically set aside for its control
* info . This is the KDATA defines in maestro3 . h . Part of its core
* data is a list of code addresses that point to the pieces of DSP code
* that it should walk through in its loop . These other pieces of code
* do the real work . The kernel presumably jumps into each of them in turn .
* These code images tend to have their own data area , and one can have
* multiple data areas representing different states for each of the ' client
* instance ' code portions . There is generally a list in the kernel data
* that points to the data instances for a given piece of code .
*
* We ' ve only been given the binary image for the ' minisrc ' , mini sample
* rate converter . This is rather annoying because it limits the work
* we can do on the dsp , but it also greatly simplifies the job of managing
* dsp data memory for the code and data for our playing streams : ) . We
* statically allocate the minisrc code into a region we ' know ' to be free
* based on the map of the binary kernel image we ' re loading . We also
* statically allocate the data areas for the maximum number of pcm streams
* we can be dealing with . This max is set by the length of the static list
* in the kernel data that records the number of minisrc data regions we
* can have . Thats right , all software dsp mixing with static code list
* limits . Rock .
*
* How sound goes in and out is still a relative mystery . It appears
* that the dsp has the ability to get input and output through various
* ' connections ' . To do IO from or to a connection , you put the address
* of the minisrc client area in the static kernel data lists for that
* input or output . so for pcm - > dsp - > mixer , we put the minisrc data
* instance in the DMA list and also in the list for the mixer . I guess
* it Just Knows which is in / out , and we give some dma control info that
* helps . There are all sorts of cool inputs / outputs that it seems we can ' t
* use without dsp code images that know how to use them .
*
* So at init time we preload all the memory allocation stuff and set some
* system wide parameters . When we really get a sound to play we build
* up its minisrc header ( stream parameters , buffer addresses , input / output
* settings ) . Then we throw its header on the various lists . We also
* tickle some KDATA settings that ask the assp to raise clock interrupts
* and do some amount of software mixing before handing data to the ac97 .
*
* Sorry for the vague details . Feel free to ask Eric or myself if you
* happen to be trying to use this driver elsewhere . Please accept my
* apologies for the quality of the OSS support code , its passed through
* too many hands now and desperately wants to be rethought .
*/
/*****************************************************************************/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/ctype.h>
# include <linux/ioport.h>
# include <linux/sched.h>
# include <linux/delay.h>
# include <linux/sound.h>
# include <linux/slab.h>
# include <linux/soundcard.h>
# include <linux/pci.h>
# include <linux/vmalloc.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/poll.h>
# include <linux/reboot.h>
# include <linux/spinlock.h>
# include <linux/ac97_codec.h>
# include <linux/wait.h>
2006-03-23 03:00:39 -08:00
# include <linux/mutex.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <asm/dma.h>
# include <asm/uaccess.h>
# include "maestro3.h"
# define M_DEBUG 1
# define DRIVER_VERSION "1.23"
# define M3_MODULE_NAME "maestro3"
# define PFX M3_MODULE_NAME ": "
# define M3_STATE_MAGIC 0x734d724d
# define M3_CARD_MAGIC 0x646e6f50
# define ESS_FMT_STEREO 0x01
# define ESS_FMT_16BIT 0x02
# define ESS_FMT_MASK 0x03
# define ESS_DAC_SHIFT 0
# define ESS_ADC_SHIFT 4
# define DAC_RUNNING 1
# define ADC_RUNNING 2
# define SND_DEV_DSP16 5
# ifdef M_DEBUG
static int debug ;
# define DPMOD 1 /* per module load */
# define DPSTR 2 /* per 'stream' */
# define DPSYS 3 /* per syscall */
# define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */
# define DPINT 5 /* per interrupt, LOTS */
# define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);}
# else
# define DPRINTK(x)
# endif
struct m3_list {
int curlen ;
u16 mem_addr ;
int max ;
} ;
static int external_amp = 1 ;
static int gpio_pin = - 1 ;
struct m3_state {
unsigned int magic ;
struct m3_card * card ;
unsigned char fmt , enable ;
int index ;
/* this locks around the oss state in the driver */
/* no, this lock is removed - only use card->lock */
/* otherwise: against what are you protecting on SMP
when irqhandler uses s - > lock
and m3_assp_read uses card - > lock ?
*/
2006-03-23 03:00:39 -08:00
struct mutex open_mutex ;
2005-04-16 15:20:36 -07:00
wait_queue_head_t open_wait ;
mode_t open_mode ;
int dev_audio ;
struct assp_instance {
u16 code , data ;
} dac_inst , adc_inst ;
/* should be in dmabuf */
unsigned int rateadc , ratedac ;
struct dmabuf {
void * rawbuf ;
unsigned buforder ;
unsigned numfrag ;
unsigned fragshift ;
unsigned hwptr , swptr ;
unsigned total_bytes ;
int count ;
unsigned error ; /* over/underrun */
wait_queue_head_t wait ;
/* redundant, but makes calculations easier */
unsigned fragsize ;
unsigned dmasize ;
unsigned fragsamples ;
/* OSS stuff */
unsigned mapped : 1 ;
unsigned ready : 1 ;
unsigned endcleared : 1 ;
unsigned ossfragshift ;
int ossmaxfrags ;
unsigned subdivision ;
/* new in m3 */
int mixer_index , dma_index , msrc_index , adc1_index ;
int in_lists ;
/* 2.4.. */
dma_addr_t handle ;
} dma_dac , dma_adc ;
} ;
struct m3_card {
unsigned int magic ;
struct m3_card * next ;
struct ac97_codec * ac97 ;
spinlock_t ac97_lock ;
int card_type ;
# define NR_DSPS 1
# define MAX_DSPS NR_DSPS
struct m3_state channels [ MAX_DSPS ] ;
/* this locks around the physical registers on the card */
spinlock_t lock ;
/* hardware resources */
struct pci_dev * pcidev ;
u32 iobase ;
u32 irq ;
int dacs_active ;
int timer_users ;
struct m3_list msrc_list ,
mixer_list ,
adc1_list ,
dma_list ;
/* for storing reset state..*/
u8 reset_state ;
u16 * suspend_mem ;
int in_suspend ;
wait_queue_head_t suspend_queue ;
} ;
/*
* an arbitrary volume we set the internal
* volume settings to so that the ac97 volume
* range is a little less insane . 0x7fff is
* max .
*/
# define ARB_VOLUME ( 0x6800 )
static const unsigned sample_shift [ ] = { 0 , 1 , 1 , 2 } ;
enum {
ESS_ALLEGRO ,
ESS_MAESTRO3 ,
/*
* a maestro3 with ' hardware strapping ' , only
* found inside ESS ?
*/
ESS_MAESTRO3HW ,
} ;
static char * card_names [ ] = {
[ ESS_ALLEGRO ] = " Allegro " ,
[ ESS_MAESTRO3 ] = " Maestro3(i) " ,
[ ESS_MAESTRO3HW ] = " Maestro3(i)hw "
} ;
# ifndef PCI_VENDOR_ESS
# define PCI_VENDOR_ESS 0x125D
# endif
# define M3_DEVICE(DEV, TYPE) \
{ \
. vendor = PCI_VENDOR_ESS , \
. device = DEV , \
. subvendor = PCI_ANY_ID , \
. subdevice = PCI_ANY_ID , \
. class = PCI_CLASS_MULTIMEDIA_AUDIO < < 8 , \
. class_mask = 0xffff < < 8 , \
. driver_data = TYPE , \
}
static struct pci_device_id m3_id_table [ ] = {
M3_DEVICE ( 0x1988 , ESS_ALLEGRO ) ,
M3_DEVICE ( 0x1998 , ESS_MAESTRO3 ) ,
M3_DEVICE ( 0x199a , ESS_MAESTRO3HW ) ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , m3_id_table ) ;
/*
* reports seem to indicate that the m3 is limited
* to 28 bit bus addresses . aaaargggh . . .
*/
# define M3_PCI_DMA_MASK 0x0fffffff
static unsigned
ld2 ( unsigned int x )
{
unsigned r = 0 ;
if ( x > = 0x10000 ) {
x > > = 16 ;
r + = 16 ;
}
if ( x > = 0x100 ) {
x > > = 8 ;
r + = 8 ;
}
if ( x > = 0x10 ) {
x > > = 4 ;
r + = 4 ;
}
if ( x > = 4 ) {
x > > = 2 ;
r + = 2 ;
}
if ( x > = 2 )
r + + ;
return r ;
}
static struct m3_card * devs ;
/*
* I ' m not very good at laying out functions in a file : )
*/
static int m3_notifier ( struct notifier_block * nb , unsigned long event , void * buf ) ;
static int m3_suspend ( struct pci_dev * pci_dev , pm_message_t state ) ;
static void check_suspend ( struct m3_card * card ) ;
static struct notifier_block m3_reboot_nb = {
. notifier_call = m3_notifier ,
} ;
static void m3_outw ( struct m3_card * card ,
u16 value , unsigned long reg )
{
check_suspend ( card ) ;
outw ( value , card - > iobase + reg ) ;
}
static u16 m3_inw ( struct m3_card * card , unsigned long reg )
{
check_suspend ( card ) ;
return inw ( card - > iobase + reg ) ;
}
static void m3_outb ( struct m3_card * card ,
u8 value , unsigned long reg )
{
check_suspend ( card ) ;
outb ( value , card - > iobase + reg ) ;
}
static u8 m3_inb ( struct m3_card * card , unsigned long reg )
{
check_suspend ( card ) ;
return inb ( card - > iobase + reg ) ;
}
/*
* access 16 bit words to the code or data regions of the dsp ' s memory .
* index addresses 16 bit words .
*/
static u16 __m3_assp_read ( struct m3_card * card , u16 region , u16 index )
{
m3_outw ( card , region & MEMTYPE_MASK , DSP_PORT_MEMORY_TYPE ) ;
m3_outw ( card , index , DSP_PORT_MEMORY_INDEX ) ;
return m3_inw ( card , DSP_PORT_MEMORY_DATA ) ;
}
static u16 m3_assp_read ( struct m3_card * card , u16 region , u16 index )
{
unsigned long flags ;
u16 ret ;
spin_lock_irqsave ( & ( card - > lock ) , flags ) ;
ret = __m3_assp_read ( card , region , index ) ;
spin_unlock_irqrestore ( & ( card - > lock ) , flags ) ;
return ret ;
}
static void __m3_assp_write ( struct m3_card * card ,
u16 region , u16 index , u16 data )
{
m3_outw ( card , region & MEMTYPE_MASK , DSP_PORT_MEMORY_TYPE ) ;
m3_outw ( card , index , DSP_PORT_MEMORY_INDEX ) ;
m3_outw ( card , data , DSP_PORT_MEMORY_DATA ) ;
}
static void m3_assp_write ( struct m3_card * card ,
u16 region , u16 index , u16 data )
{
unsigned long flags ;
spin_lock_irqsave ( & ( card - > lock ) , flags ) ;
__m3_assp_write ( card , region , index , data ) ;
spin_unlock_irqrestore ( & ( card - > lock ) , flags ) ;
}
static void m3_assp_halt ( struct m3_card * card )
{
card - > reset_state = m3_inb ( card , DSP_PORT_CONTROL_REG_B ) & ~ REGB_STOP_CLOCK ;
mdelay ( 10 ) ;
m3_outb ( card , card - > reset_state & ~ REGB_ENABLE_RESET , DSP_PORT_CONTROL_REG_B ) ;
}
static void m3_assp_continue ( struct m3_card * card )
{
m3_outb ( card , card - > reset_state | REGB_ENABLE_RESET , DSP_PORT_CONTROL_REG_B ) ;
}
/*
* This makes me sad . the maestro3 has lists
* internally that must be packed . . 0 terminates ,
* apparently , or maybe all unused entries have
* to be 0 , the lists have static lengths set
* by the binary code images .
*/
static int m3_add_list ( struct m3_card * card ,
struct m3_list * list , u16 val )
{
DPRINTK ( DPSTR , " adding val 0x%x to list 0x%p at pos %d \n " ,
val , list , list - > curlen ) ;
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
list - > mem_addr + list - > curlen ,
val ) ;
return list - > curlen + + ;
}
static void m3_remove_list ( struct m3_card * card ,
struct m3_list * list , int index )
{
u16 val ;
int lastindex = list - > curlen - 1 ;
DPRINTK ( DPSTR , " removing ind %d from list 0x%p \n " ,
index , list ) ;
if ( index ! = lastindex ) {
val = m3_assp_read ( card , MEMTYPE_INTERNAL_DATA ,
list - > mem_addr + lastindex ) ;
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
list - > mem_addr + index ,
val ) ;
}
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
list - > mem_addr + lastindex ,
0 ) ;
list - > curlen - - ;
}
static void set_fmt ( struct m3_state * s , unsigned char mask , unsigned char data )
{
int tmp ;
s - > fmt = ( s - > fmt & mask ) | data ;
tmp = ( s - > fmt > > ESS_DAC_SHIFT ) & ESS_FMT_MASK ;
/* write to 'mono' word */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + SRC3_DIRECTION_OFFSET + 1 ,
( tmp & ESS_FMT_STEREO ) ? 0 : 1 ) ;
/* write to '8bit' word */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + SRC3_DIRECTION_OFFSET + 2 ,
( tmp & ESS_FMT_16BIT ) ? 0 : 1 ) ;
tmp = ( s - > fmt > > ESS_ADC_SHIFT ) & ESS_FMT_MASK ;
/* write to 'mono' word */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + SRC3_DIRECTION_OFFSET + 1 ,
( tmp & ESS_FMT_STEREO ) ? 0 : 1 ) ;
/* write to '8bit' word */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + SRC3_DIRECTION_OFFSET + 2 ,
( tmp & ESS_FMT_16BIT ) ? 0 : 1 ) ;
}
static void set_dac_rate ( struct m3_state * s , unsigned int rate )
{
u32 freq ;
if ( rate > 48000 )
rate = 48000 ;
if ( rate < 8000 )
rate = 8000 ;
s - > ratedac = rate ;
freq = ( ( rate < < 15 ) + 24000 ) / 48000 ;
if ( freq )
freq - - ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_FREQUENCY ,
freq ) ;
}
static void set_adc_rate ( struct m3_state * s , unsigned int rate )
{
u32 freq ;
if ( rate > 48000 )
rate = 48000 ;
if ( rate < 8000 )
rate = 8000 ;
s - > rateadc = rate ;
freq = ( ( rate < < 15 ) + 24000 ) / 48000 ;
if ( freq )
freq - - ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_FREQUENCY ,
freq ) ;
}
static void inc_timer_users ( struct m3_card * card )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > lock , flags ) ;
card - > timer_users + + ;
DPRINTK ( DPSYS , " inc timer users now %d \n " ,
card - > timer_users ) ;
if ( card - > timer_users ! = 1 )
goto out ;
__m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_TIMER_COUNT_RELOAD ,
240 ) ;
__m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_TIMER_COUNT_CURRENT ,
240 ) ;
m3_outw ( card ,
m3_inw ( card , HOST_INT_CTRL ) | CLKRUN_GEN_ENABLE ,
HOST_INT_CTRL ) ;
out :
spin_unlock_irqrestore ( & card - > lock , flags ) ;
}
static void dec_timer_users ( struct m3_card * card )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > lock , flags ) ;
card - > timer_users - - ;
DPRINTK ( DPSYS , " dec timer users now %d \n " ,
card - > timer_users ) ;
if ( card - > timer_users > 0 )
goto out ;
__m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_TIMER_COUNT_RELOAD ,
0 ) ;
__m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_TIMER_COUNT_CURRENT ,
0 ) ;
m3_outw ( card , m3_inw ( card , HOST_INT_CTRL ) & ~ CLKRUN_GEN_ENABLE ,
HOST_INT_CTRL ) ;
out :
spin_unlock_irqrestore ( & card - > lock , flags ) ;
}
/*
* { start , stop } _ { adc , dac } should be called
* while holding the ' state ' lock and they
* will try to grab the ' card ' lock . .
*/
static void stop_adc ( struct m3_state * s )
{
if ( ! ( s - > enable & ADC_RUNNING ) )
return ;
s - > enable & = ~ ADC_RUNNING ;
dec_timer_users ( s - > card ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_INSTANCE_READY , 0 ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
KDATA_ADC1_REQUEST , 0 ) ;
}
static void stop_dac ( struct m3_state * s )
{
if ( ! ( s - > enable & DAC_RUNNING ) )
return ;
DPRINTK ( DPSYS , " stop_dac() \n " ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_INSTANCE_READY , 0 ) ;
s - > enable & = ~ DAC_RUNNING ;
s - > card - > dacs_active - - ;
dec_timer_users ( s - > card ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
KDATA_MIXER_TASK_NUMBER ,
s - > card - > dacs_active ) ;
}
static void start_dac ( struct m3_state * s )
{
if ( ( ! s - > dma_dac . mapped & & s - > dma_dac . count < 1 ) | |
! s - > dma_dac . ready | |
( s - > enable & DAC_RUNNING ) )
return ;
DPRINTK ( DPSYS , " start_dac() \n " ) ;
s - > enable | = DAC_RUNNING ;
s - > card - > dacs_active + + ;
inc_timer_users ( s - > card ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_INSTANCE_READY , 1 ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
KDATA_MIXER_TASK_NUMBER ,
s - > card - > dacs_active ) ;
}
static void start_adc ( struct m3_state * s )
{
if ( ( ! s - > dma_adc . mapped & &
s - > dma_adc . count > = ( signed ) ( s - > dma_adc . dmasize - 2 * s - > dma_adc . fragsize ) )
| | ! s - > dma_adc . ready
| | ( s - > enable & ADC_RUNNING ) )
return ;
DPRINTK ( DPSYS , " start_adc() \n " ) ;
s - > enable | = ADC_RUNNING ;
inc_timer_users ( s - > card ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
KDATA_ADC1_REQUEST , 1 ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_INSTANCE_READY , 1 ) ;
}
static struct play_vals {
u16 addr , val ;
} pv [ ] = {
{ CDATA_LEFT_VOLUME , ARB_VOLUME } ,
{ CDATA_RIGHT_VOLUME , ARB_VOLUME } ,
{ SRC3_DIRECTION_OFFSET , 0 } ,
/* +1, +2 are stereo/16 bit */
{ SRC3_DIRECTION_OFFSET + 3 , 0x0000 } , /* fraction? */
{ SRC3_DIRECTION_OFFSET + 4 , 0 } , /* first l */
{ SRC3_DIRECTION_OFFSET + 5 , 0 } , /* first r */
{ SRC3_DIRECTION_OFFSET + 6 , 0 } , /* second l */
{ SRC3_DIRECTION_OFFSET + 7 , 0 } , /* second r */
{ SRC3_DIRECTION_OFFSET + 8 , 0 } , /* delta l */
{ SRC3_DIRECTION_OFFSET + 9 , 0 } , /* delta r */
{ SRC3_DIRECTION_OFFSET + 10 , 0x8000 } , /* round */
{ SRC3_DIRECTION_OFFSET + 11 , 0xFF00 } , /* higher bute mark */
{ SRC3_DIRECTION_OFFSET + 13 , 0 } , /* temp0 */
{ SRC3_DIRECTION_OFFSET + 14 , 0 } , /* c fraction */
{ SRC3_DIRECTION_OFFSET + 15 , 0 } , /* counter */
{ SRC3_DIRECTION_OFFSET + 16 , 8 } , /* numin */
{ SRC3_DIRECTION_OFFSET + 17 , 50 * 2 } , /* numout */
{ SRC3_DIRECTION_OFFSET + 18 , MINISRC_BIQUAD_STAGE - 1 } , /* numstage */
{ SRC3_DIRECTION_OFFSET + 20 , 0 } , /* filtertap */
{ SRC3_DIRECTION_OFFSET + 21 , 0 } /* booster */
} ;
/* the mode passed should be already shifted and masked */
static void m3_play_setup ( struct m3_state * s , int mode , u32 rate , void * buffer , int size )
{
int dsp_in_size = MINISRC_IN_BUFFER_SIZE - ( 0x20 * 2 ) ;
int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - ( 0x20 * 2 ) ;
int dsp_in_buffer = s - > dac_inst . data + ( MINISRC_TMP_BUFFER_SIZE / 2 ) ;
int dsp_out_buffer = dsp_in_buffer + ( dsp_in_size / 2 ) + 1 ;
struct dmabuf * db = & s - > dma_dac ;
int i ;
DPRINTK ( DPSTR , " mode=%d rate=%d buf=%p len=%d. \n " ,
mode , rate , buffer , size ) ;
# define LO(x) ((x) & 0xffff)
# define HI(x) LO((x) >> 16)
/* host dma buffer pointers */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_HOST_SRC_ADDRL ,
LO ( virt_to_bus ( buffer ) ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_HOST_SRC_ADDRH ,
HI ( virt_to_bus ( buffer ) ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_HOST_SRC_END_PLUS_1L ,
LO ( virt_to_bus ( buffer ) + size ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_HOST_SRC_END_PLUS_1H ,
HI ( virt_to_bus ( buffer ) + size ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_HOST_SRC_CURRENTL ,
LO ( virt_to_bus ( buffer ) ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_HOST_SRC_CURRENTH ,
HI ( virt_to_bus ( buffer ) ) ) ;
# undef LO
# undef HI
/* dsp buffers */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_IN_BUF_BEGIN ,
dsp_in_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_IN_BUF_END_PLUS_1 ,
dsp_in_buffer + ( dsp_in_size / 2 ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_IN_BUF_HEAD ,
dsp_in_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_IN_BUF_TAIL ,
dsp_in_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_OUT_BUF_BEGIN ,
dsp_out_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_OUT_BUF_END_PLUS_1 ,
dsp_out_buffer + ( dsp_out_size / 2 ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_OUT_BUF_HEAD ,
dsp_out_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_OUT_BUF_TAIL ,
dsp_out_buffer ) ;
/*
* some per client initializers
*/
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + SRC3_DIRECTION_OFFSET + 12 ,
s - > dac_inst . data + 40 + 8 ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + SRC3_DIRECTION_OFFSET + 19 ,
s - > dac_inst . code + MINISRC_COEF_LOC ) ;
/* enable or disable low pass filter? */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + SRC3_DIRECTION_OFFSET + 22 ,
s - > ratedac > 45000 ? 0xff : 0 ) ;
/* tell it which way dma is going? */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + CDATA_DMA_CONTROL ,
DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR ) ;
/*
* set an armload of static initializers
*/
for ( i = 0 ; i < ( sizeof ( pv ) / sizeof ( pv [ 0 ] ) ) ; i + + )
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > dac_inst . data + pv [ i ] . addr , pv [ i ] . val ) ;
/*
* put us in the lists if we ' re not already there
*/
if ( db - > in_lists = = 0 ) {
db - > msrc_index = m3_add_list ( s - > card , & s - > card - > msrc_list ,
s - > dac_inst . data > > DP_SHIFT_COUNT ) ;
db - > dma_index = m3_add_list ( s - > card , & s - > card - > dma_list ,
s - > dac_inst . data > > DP_SHIFT_COUNT ) ;
db - > mixer_index = m3_add_list ( s - > card , & s - > card - > mixer_list ,
s - > dac_inst . data > > DP_SHIFT_COUNT ) ;
db - > in_lists = 1 ;
}
set_dac_rate ( s , rate ) ;
start_dac ( s ) ;
}
/*
* Native record driver
*/
static struct rec_vals {
u16 addr , val ;
} rv [ ] = {
{ CDATA_LEFT_VOLUME , ARB_VOLUME } ,
{ CDATA_RIGHT_VOLUME , ARB_VOLUME } ,
{ SRC3_DIRECTION_OFFSET , 1 } ,
/* +1, +2 are stereo/16 bit */
{ SRC3_DIRECTION_OFFSET + 3 , 0x0000 } , /* fraction? */
{ SRC3_DIRECTION_OFFSET + 4 , 0 } , /* first l */
{ SRC3_DIRECTION_OFFSET + 5 , 0 } , /* first r */
{ SRC3_DIRECTION_OFFSET + 6 , 0 } , /* second l */
{ SRC3_DIRECTION_OFFSET + 7 , 0 } , /* second r */
{ SRC3_DIRECTION_OFFSET + 8 , 0 } , /* delta l */
{ SRC3_DIRECTION_OFFSET + 9 , 0 } , /* delta r */
{ SRC3_DIRECTION_OFFSET + 10 , 0x8000 } , /* round */
{ SRC3_DIRECTION_OFFSET + 11 , 0xFF00 } , /* higher bute mark */
{ SRC3_DIRECTION_OFFSET + 13 , 0 } , /* temp0 */
{ SRC3_DIRECTION_OFFSET + 14 , 0 } , /* c fraction */
{ SRC3_DIRECTION_OFFSET + 15 , 0 } , /* counter */
{ SRC3_DIRECTION_OFFSET + 16 , 50 } , /* numin */
{ SRC3_DIRECTION_OFFSET + 17 , 8 } , /* numout */
{ SRC3_DIRECTION_OFFSET + 18 , 0 } , /* numstage */
{ SRC3_DIRECTION_OFFSET + 19 , 0 } , /* coef */
{ SRC3_DIRECTION_OFFSET + 20 , 0 } , /* filtertap */
{ SRC3_DIRECTION_OFFSET + 21 , 0 } , /* booster */
{ SRC3_DIRECTION_OFFSET + 22 , 0xff } /* skip lpf */
} ;
/* again, passed mode is alrady shifted/masked */
static void m3_rec_setup ( struct m3_state * s , int mode , u32 rate , void * buffer , int size )
{
int dsp_in_size = MINISRC_IN_BUFFER_SIZE + ( 0x10 * 2 ) ;
int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - ( 0x10 * 2 ) ;
int dsp_in_buffer = s - > adc_inst . data + ( MINISRC_TMP_BUFFER_SIZE / 2 ) ;
int dsp_out_buffer = dsp_in_buffer + ( dsp_in_size / 2 ) + 1 ;
struct dmabuf * db = & s - > dma_adc ;
int i ;
DPRINTK ( DPSTR , " rec_setup mode=%d rate=%d buf=%p len=%d. \n " ,
mode , rate , buffer , size ) ;
# define LO(x) ((x) & 0xffff)
# define HI(x) LO((x) >> 16)
/* host dma buffer pointers */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_HOST_SRC_ADDRL ,
LO ( virt_to_bus ( buffer ) ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_HOST_SRC_ADDRH ,
HI ( virt_to_bus ( buffer ) ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_HOST_SRC_END_PLUS_1L ,
LO ( virt_to_bus ( buffer ) + size ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_HOST_SRC_END_PLUS_1H ,
HI ( virt_to_bus ( buffer ) + size ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_HOST_SRC_CURRENTL ,
LO ( virt_to_bus ( buffer ) ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_HOST_SRC_CURRENTH ,
HI ( virt_to_bus ( buffer ) ) ) ;
# undef LO
# undef HI
/* dsp buffers */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_IN_BUF_BEGIN ,
dsp_in_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_IN_BUF_END_PLUS_1 ,
dsp_in_buffer + ( dsp_in_size / 2 ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_IN_BUF_HEAD ,
dsp_in_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_IN_BUF_TAIL ,
dsp_in_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_OUT_BUF_BEGIN ,
dsp_out_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_OUT_BUF_END_PLUS_1 ,
dsp_out_buffer + ( dsp_out_size / 2 ) ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_OUT_BUF_HEAD ,
dsp_out_buffer ) ;
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_OUT_BUF_TAIL ,
dsp_out_buffer ) ;
/*
* some per client initializers
*/
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + SRC3_DIRECTION_OFFSET + 12 ,
s - > adc_inst . data + 40 + 8 ) ;
/* tell it which way dma is going? */
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + CDATA_DMA_CONTROL ,
DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT +
DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR ) ;
/*
* set an armload of static initializers
*/
for ( i = 0 ; i < ( sizeof ( rv ) / sizeof ( rv [ 0 ] ) ) ; i + + )
m3_assp_write ( s - > card , MEMTYPE_INTERNAL_DATA ,
s - > adc_inst . data + rv [ i ] . addr , rv [ i ] . val ) ;
/*
* put us in the lists if we ' re not already there
*/
if ( db - > in_lists = = 0 ) {
db - > adc1_index = m3_add_list ( s - > card , & s - > card - > adc1_list ,
s - > adc_inst . data > > DP_SHIFT_COUNT ) ;
db - > dma_index = m3_add_list ( s - > card , & s - > card - > dma_list ,
s - > adc_inst . data > > DP_SHIFT_COUNT ) ;
db - > msrc_index = m3_add_list ( s - > card , & s - > card - > msrc_list ,
s - > adc_inst . data > > DP_SHIFT_COUNT ) ;
db - > in_lists = 1 ;
}
set_adc_rate ( s , rate ) ;
start_adc ( s ) ;
}
/* --------------------------------------------------------------------- */
static void set_dmaa ( struct m3_state * s , unsigned int addr , unsigned int count )
{
DPRINTK ( DPINT , " set_dmaa?? \n " ) ;
}
static void set_dmac ( struct m3_state * s , unsigned int addr , unsigned int count )
{
DPRINTK ( DPINT , " set_dmac?? \n " ) ;
}
static u32 get_dma_pos ( struct m3_card * card ,
int instance_addr )
{
u16 hi = 0 , lo = 0 ;
int retry = 10 ;
/*
* try and get a valid answer
*/
while ( retry - - ) {
hi = m3_assp_read ( card , MEMTYPE_INTERNAL_DATA ,
instance_addr + CDATA_HOST_SRC_CURRENTH ) ;
lo = m3_assp_read ( card , MEMTYPE_INTERNAL_DATA ,
instance_addr + CDATA_HOST_SRC_CURRENTL ) ;
if ( hi = = m3_assp_read ( card , MEMTYPE_INTERNAL_DATA ,
instance_addr + CDATA_HOST_SRC_CURRENTH ) )
break ;
}
return lo | ( hi < < 16 ) ;
}
static u32 get_dmaa ( struct m3_state * s )
{
u32 offset ;
offset = get_dma_pos ( s - > card , s - > dac_inst . data ) -
virt_to_bus ( s - > dma_dac . rawbuf ) ;
DPRINTK ( DPINT , " get_dmaa: 0x%08x \n " , offset ) ;
return offset ;
}
static u32 get_dmac ( struct m3_state * s )
{
u32 offset ;
offset = get_dma_pos ( s - > card , s - > adc_inst . data ) -
virt_to_bus ( s - > dma_adc . rawbuf ) ;
DPRINTK ( DPINT , " get_dmac: 0x%08x \n " , offset ) ;
return offset ;
}
static int
prog_dmabuf ( struct m3_state * s , unsigned rec )
{
struct dmabuf * db = rec ? & s - > dma_adc : & s - > dma_dac ;
unsigned rate = rec ? s - > rateadc : s - > ratedac ;
unsigned bytepersec ;
unsigned bufs ;
unsigned char fmt ;
unsigned long flags ;
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
fmt = s - > fmt ;
if ( rec ) {
stop_adc ( s ) ;
fmt > > = ESS_ADC_SHIFT ;
} else {
stop_dac ( s ) ;
fmt > > = ESS_DAC_SHIFT ;
}
fmt & = ESS_FMT_MASK ;
db - > hwptr = db - > swptr = db - > total_bytes = db - > count = db - > error = db - > endcleared = 0 ;
bytepersec = rate < < sample_shift [ fmt ] ;
bufs = PAGE_SIZE < < db - > buforder ;
if ( db - > ossfragshift ) {
if ( ( 1000 < < db - > ossfragshift ) < bytepersec )
db - > fragshift = ld2 ( bytepersec / 1000 ) ;
else
db - > fragshift = db - > ossfragshift ;
} else {
db - > fragshift = ld2 ( bytepersec / 100 / ( db - > subdivision ? db - > subdivision : 1 ) ) ;
if ( db - > fragshift < 3 )
db - > fragshift = 3 ;
}
db - > numfrag = bufs > > db - > fragshift ;
while ( db - > numfrag < 4 & & db - > fragshift > 3 ) {
db - > fragshift - - ;
db - > numfrag = bufs > > db - > fragshift ;
}
db - > fragsize = 1 < < db - > fragshift ;
if ( db - > ossmaxfrags > = 4 & & db - > ossmaxfrags < db - > numfrag )
db - > numfrag = db - > ossmaxfrags ;
db - > fragsamples = db - > fragsize > > sample_shift [ fmt ] ;
db - > dmasize = db - > numfrag < < db - > fragshift ;
DPRINTK ( DPSTR , " prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d \n " , db - > numfrag , db - > fragsize , db - > dmasize ) ;
memset ( db - > rawbuf , ( fmt & ESS_FMT_16BIT ) ? 0 : 0x80 , db - > dmasize ) ;
if ( rec )
m3_rec_setup ( s , fmt , s - > rateadc , db - > rawbuf , db - > dmasize ) ;
else
m3_play_setup ( s , fmt , s - > ratedac , db - > rawbuf , db - > dmasize ) ;
db - > ready = 1 ;
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
return 0 ;
}
static void clear_advance ( struct m3_state * s )
{
unsigned char c = ( ( s - > fmt > > ESS_DAC_SHIFT ) & ESS_FMT_16BIT ) ? 0 : 0x80 ;
unsigned char * buf = s - > dma_dac . rawbuf ;
unsigned bsize = s - > dma_dac . dmasize ;
unsigned bptr = s - > dma_dac . swptr ;
unsigned len = s - > dma_dac . fragsize ;
if ( bptr + len > bsize ) {
unsigned x = bsize - bptr ;
memset ( buf + bptr , c , x ) ;
/* account for wrapping? */
bptr = 0 ;
len - = x ;
}
memset ( buf + bptr , c , len ) ;
}
/* call with spinlock held! */
static void m3_update_ptr ( struct m3_state * s )
{
unsigned hwptr ;
int diff ;
/* update ADC pointer */
if ( s - > dma_adc . ready ) {
hwptr = get_dmac ( s ) % s - > dma_adc . dmasize ;
diff = ( s - > dma_adc . dmasize + hwptr - s - > dma_adc . hwptr ) % s - > dma_adc . dmasize ;
s - > dma_adc . hwptr = hwptr ;
s - > dma_adc . total_bytes + = diff ;
s - > dma_adc . count + = diff ;
if ( s - > dma_adc . count > = ( signed ) s - > dma_adc . fragsize )
wake_up ( & s - > dma_adc . wait ) ;
if ( ! s - > dma_adc . mapped ) {
if ( s - > dma_adc . count > ( signed ) ( s - > dma_adc . dmasize - ( ( 3 * s - > dma_adc . fragsize ) > > 1 ) ) ) {
stop_adc ( s ) ;
/* brute force everyone back in sync, sigh */
s - > dma_adc . count = 0 ;
s - > dma_adc . swptr = 0 ;
s - > dma_adc . hwptr = 0 ;
s - > dma_adc . error + + ;
}
}
}
/* update DAC pointer */
if ( s - > dma_dac . ready ) {
hwptr = get_dmaa ( s ) % s - > dma_dac . dmasize ;
diff = ( s - > dma_dac . dmasize + hwptr - s - > dma_dac . hwptr ) % s - > dma_dac . dmasize ;
DPRINTK ( DPINT , " updating dac: hwptr: %6d diff: %6d count: %6d \n " ,
hwptr , diff , s - > dma_dac . count ) ;
s - > dma_dac . hwptr = hwptr ;
s - > dma_dac . total_bytes + = diff ;
if ( s - > dma_dac . mapped ) {
s - > dma_dac . count + = diff ;
if ( s - > dma_dac . count > = ( signed ) s - > dma_dac . fragsize ) {
wake_up ( & s - > dma_dac . wait ) ;
}
} else {
s - > dma_dac . count - = diff ;
if ( s - > dma_dac . count < = 0 ) {
DPRINTK ( DPCRAP , " underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x) \n " ,
diff , diff ,
s - > dma_dac . count ,
s - > dma_dac . count ,
hwptr , hwptr ,
s - > dma_dac . swptr ,
s - > dma_dac . swptr ) ;
stop_dac ( s ) ;
/* brute force everyone back in sync, sigh */
s - > dma_dac . count = 0 ;
s - > dma_dac . swptr = hwptr ;
s - > dma_dac . error + + ;
} else if ( s - > dma_dac . count < = ( signed ) s - > dma_dac . fragsize & & ! s - > dma_dac . endcleared ) {
clear_advance ( s ) ;
s - > dma_dac . endcleared = 1 ;
}
if ( s - > dma_dac . count + ( signed ) s - > dma_dac . fragsize < = ( signed ) s - > dma_dac . dmasize ) {
wake_up ( & s - > dma_dac . wait ) ;
DPRINTK ( DPINT , " waking up DAC count: %d sw: %d hw: %d \n " ,
s - > dma_dac . count , s - > dma_dac . swptr , hwptr ) ;
}
}
}
}
static irqreturn_t m3_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct m3_card * c = ( struct m3_card * ) dev_id ;
struct m3_state * s = & c - > channels [ 0 ] ;
u8 status ;
status = inb ( c - > iobase + 0x1A ) ;
if ( status = = 0xff )
return IRQ_NONE ;
/* presumably acking the ints? */
outw ( status , c - > iobase + 0x1A ) ;
if ( c - > in_suspend )
return IRQ_HANDLED ;
/*
* ack an assp int if its running
* and has an int pending
*/
if ( status & ASSP_INT_PENDING ) {
u8 ctl = inb ( c - > iobase + ASSP_CONTROL_B ) ;
if ( ! ( ctl & STOP_ASSP_CLOCK ) ) {
ctl = inb ( c - > iobase + ASSP_HOST_INT_STATUS ) ;
if ( ctl & DSP2HOST_REQ_TIMER ) {
outb ( DSP2HOST_REQ_TIMER , c - > iobase + ASSP_HOST_INT_STATUS ) ;
/* update adc/dac info if it was a timer int */
spin_lock ( & c - > lock ) ;
m3_update_ptr ( s ) ;
spin_unlock ( & c - > lock ) ;
}
}
}
/* XXX is this needed? */
if ( status & 0x40 )
outb ( 0x40 , c - > iobase + 0x1A ) ;
return IRQ_HANDLED ;
}
/* --------------------------------------------------------------------- */
static const char invalid_magic [ ] = KERN_CRIT PFX " invalid magic value in %s \n " ;
# define VALIDATE_MAGIC(FOO,MAG) \
( { \
if ( ! ( FOO ) | | ( FOO ) - > magic ! = MAG ) { \
printk ( invalid_magic , __FUNCTION__ ) ; \
return - ENXIO ; \
} \
} )
# define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC)
# define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC)
/* --------------------------------------------------------------------- */
static int drain_dac ( struct m3_state * s , int nonblock )
{
DECLARE_WAITQUEUE ( wait , current ) ;
unsigned long flags ;
int count ;
signed long tmo ;
if ( s - > dma_dac . mapped | | ! s - > dma_dac . ready )
return 0 ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
add_wait_queue ( & s - > dma_dac . wait , & wait ) ;
for ( ; ; ) {
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
count = s - > dma_dac . count ;
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
if ( count < = 0 )
break ;
if ( signal_pending ( current ) )
break ;
if ( nonblock ) {
remove_wait_queue ( & s - > dma_dac . wait , & wait ) ;
set_current_state ( TASK_RUNNING ) ;
return - EBUSY ;
}
tmo = ( count * HZ ) / s - > ratedac ;
tmo > > = sample_shift [ ( s - > fmt > > ESS_DAC_SHIFT ) & ESS_FMT_MASK ] ;
/* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken.
or something . who cares . - zach */
if ( ! schedule_timeout ( tmo ? tmo : 1 ) & & tmo )
DPRINTK ( DPCRAP , " dma timed out?? %ld \n " , jiffies ) ;
}
remove_wait_queue ( & s - > dma_dac . wait , & wait ) ;
set_current_state ( TASK_RUNNING ) ;
if ( signal_pending ( current ) )
return - ERESTARTSYS ;
return 0 ;
}
static ssize_t m3_read ( struct file * file , char __user * buffer , size_t count , loff_t * ppos )
{
struct m3_state * s = ( struct m3_state * ) file - > private_data ;
ssize_t ret ;
unsigned long flags ;
unsigned swptr ;
int cnt ;
VALIDATE_STATE ( s ) ;
if ( s - > dma_adc . mapped )
return - ENXIO ;
if ( ! s - > dma_adc . ready & & ( ret = prog_dmabuf ( s , 1 ) ) )
return ret ;
if ( ! access_ok ( VERIFY_WRITE , buffer , count ) )
return - EFAULT ;
ret = 0 ;
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
while ( count > 0 ) {
int timed_out ;
swptr = s - > dma_adc . swptr ;
cnt = s - > dma_adc . dmasize - swptr ;
if ( s - > dma_adc . count < cnt )
cnt = s - > dma_adc . count ;
if ( cnt > count )
cnt = count ;
if ( cnt < = 0 ) {
start_adc ( s ) ;
if ( file - > f_flags & O_NONBLOCK )
{
ret = ret ? ret : - EAGAIN ;
goto out ;
}
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
timed_out = interruptible_sleep_on_timeout ( & s - > dma_adc . wait , HZ ) = = 0 ;
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
if ( timed_out ) {
printk ( " read: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u \n " ,
s - > dma_adc . dmasize , s - > dma_adc . fragsize , s - > dma_adc . count ,
s - > dma_adc . hwptr , s - > dma_adc . swptr ) ;
stop_adc ( s ) ;
set_dmac ( s , virt_to_bus ( s - > dma_adc . rawbuf ) , s - > dma_adc . numfrag < < s - > dma_adc . fragshift ) ;
s - > dma_adc . count = s - > dma_adc . hwptr = s - > dma_adc . swptr = 0 ;
}
if ( signal_pending ( current ) )
{
ret = ret ? ret : - ERESTARTSYS ;
goto out ;
}
continue ;
}
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
if ( copy_to_user ( buffer , s - > dma_adc . rawbuf + swptr , cnt ) ) {
ret = ret ? ret : - EFAULT ;
return ret ;
}
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
swptr = ( swptr + cnt ) % s - > dma_adc . dmasize ;
s - > dma_adc . swptr = swptr ;
s - > dma_adc . count - = cnt ;
count - = cnt ;
buffer + = cnt ;
ret + = cnt ;
start_adc ( s ) ;
}
out :
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
return ret ;
}
static ssize_t m3_write ( struct file * file , const char __user * buffer , size_t count , loff_t * ppos )
{
struct m3_state * s = ( struct m3_state * ) file - > private_data ;
ssize_t ret ;
unsigned long flags ;
unsigned swptr ;
int cnt ;
VALIDATE_STATE ( s ) ;
if ( s - > dma_dac . mapped )
return - ENXIO ;
if ( ! s - > dma_dac . ready & & ( ret = prog_dmabuf ( s , 0 ) ) )
return ret ;
if ( ! access_ok ( VERIFY_READ , buffer , count ) )
return - EFAULT ;
ret = 0 ;
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
while ( count > 0 ) {
int timed_out ;
if ( s - > dma_dac . count < 0 ) {
s - > dma_dac . count = 0 ;
s - > dma_dac . swptr = s - > dma_dac . hwptr ;
}
swptr = s - > dma_dac . swptr ;
cnt = s - > dma_dac . dmasize - swptr ;
if ( s - > dma_dac . count + cnt > s - > dma_dac . dmasize )
cnt = s - > dma_dac . dmasize - s - > dma_dac . count ;
if ( cnt > count )
cnt = count ;
if ( cnt < = 0 ) {
start_dac ( s ) ;
if ( file - > f_flags & O_NONBLOCK ) {
if ( ! ret ) ret = - EAGAIN ;
goto out ;
}
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
timed_out = interruptible_sleep_on_timeout ( & s - > dma_dac . wait , HZ ) = = 0 ;
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
if ( timed_out ) {
DPRINTK ( DPCRAP , " write: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u \n " ,
s - > dma_dac . dmasize , s - > dma_dac . fragsize , s - > dma_dac . count ,
s - > dma_dac . hwptr , s - > dma_dac . swptr ) ;
stop_dac ( s ) ;
set_dmaa ( s , virt_to_bus ( s - > dma_dac . rawbuf ) , s - > dma_dac . numfrag < < s - > dma_dac . fragshift ) ;
s - > dma_dac . count = s - > dma_dac . hwptr = s - > dma_dac . swptr = 0 ;
}
if ( signal_pending ( current ) ) {
if ( ! ret ) ret = - ERESTARTSYS ;
goto out ;
}
continue ;
}
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
if ( copy_from_user ( s - > dma_dac . rawbuf + swptr , buffer , cnt ) ) {
if ( ! ret ) ret = - EFAULT ;
return ret ;
}
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
DPRINTK ( DPSYS , " wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d \n " ,
cnt , swptr , s - > dma_dac . count , s - > dma_dac . hwptr ) ;
swptr = ( swptr + cnt ) % s - > dma_dac . dmasize ;
s - > dma_dac . swptr = swptr ;
s - > dma_dac . count + = cnt ;
s - > dma_dac . endcleared = 0 ;
count - = cnt ;
buffer + = cnt ;
ret + = cnt ;
start_dac ( s ) ;
}
out :
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
return ret ;
}
static unsigned int m3_poll ( struct file * file , struct poll_table_struct * wait )
{
struct m3_state * s = ( struct m3_state * ) file - > private_data ;
unsigned long flags ;
unsigned int mask = 0 ;
VALIDATE_STATE ( s ) ;
if ( file - > f_mode & FMODE_WRITE )
poll_wait ( file , & s - > dma_dac . wait , wait ) ;
if ( file - > f_mode & FMODE_READ )
poll_wait ( file , & s - > dma_adc . wait , wait ) ;
spin_lock_irqsave ( & s - > card - > lock , flags ) ;
m3_update_ptr ( s ) ;
if ( file - > f_mode & FMODE_READ ) {
if ( s - > dma_adc . count > = ( signed ) s - > dma_adc . fragsize )
mask | = POLLIN | POLLRDNORM ;
}
if ( file - > f_mode & FMODE_WRITE ) {
if ( s - > dma_dac . mapped ) {
if ( s - > dma_dac . count > = ( signed ) s - > dma_dac . fragsize )
mask | = POLLOUT | POLLWRNORM ;
} else {
if ( ( signed ) s - > dma_dac . dmasize > = s - > dma_dac . count + ( signed ) s - > dma_dac . fragsize )
mask | = POLLOUT | POLLWRNORM ;
}
}
spin_unlock_irqrestore ( & s - > card - > lock , flags ) ;
return mask ;
}
static int m3_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct m3_state * s = ( struct m3_state * ) file - > private_data ;
unsigned long max_size , size , start , offset ;
struct dmabuf * db ;
int ret = - EINVAL ;
VALIDATE_STATE ( s ) ;
if ( vma - > vm_flags & VM_WRITE ) {
if ( ( ret = prog_dmabuf ( s , 0 ) ) ! = 0 )
return ret ;
db = & s - > dma_dac ;
} else
if ( vma - > vm_flags & VM_READ ) {
if ( ( ret = prog_dmabuf ( s , 1 ) ) ! = 0 )
return ret ;
db = & s - > dma_adc ;
} else
return - EINVAL ;
max_size = db - > dmasize ;
start = vma - > vm_start ;
offset = ( vma - > vm_pgoff < < PAGE_SHIFT ) ;
size = vma - > vm_end - vma - > vm_start ;
if ( size > max_size )
goto out ;
if ( offset > max_size - size )
goto out ;
/*
* this will be - > nopage ( ) once I can
* ask Jeff what the hell I ' m doing wrong .
*/
ret = - EAGAIN ;
if ( remap_pfn_range ( vma , vma - > vm_start ,
virt_to_phys ( db - > rawbuf ) > > PAGE_SHIFT ,
size , vma - > vm_page_prot ) )
goto out ;
db - > mapped = 1 ;
ret = 0 ;
out :
return ret ;
}
/*
* this function is a disaster . .
*/
# define get_user_ret(x, ptr, ret) ({ if(get_user(x, ptr)) return ret; })
static int m3_ioctl ( struct inode * inode , struct file * file , unsigned int cmd , unsigned long arg )
{
struct m3_state * s = ( struct m3_state * ) file - > private_data ;
struct m3_card * card = s - > card ;
unsigned long flags ;
audio_buf_info abinfo ;
count_info cinfo ;
int val , mapped , ret ;
unsigned char fmtm , fmtd ;
void __user * argp = ( void __user * ) arg ;
int __user * p = argp ;
VALIDATE_STATE ( s ) ;
mapped = ( ( file - > f_mode & FMODE_WRITE ) & & s - > dma_dac . mapped ) | |
( ( file - > f_mode & FMODE_READ ) & & s - > dma_adc . mapped ) ;
DPRINTK ( DPSYS , " m3_ioctl: cmd %d \n " , cmd ) ;
switch ( cmd ) {
case OSS_GETVERSION :
return put_user ( SOUND_VERSION , p ) ;
case SNDCTL_DSP_SYNC :
if ( file - > f_mode & FMODE_WRITE )
return drain_dac ( s , file - > f_flags & O_NONBLOCK ) ;
return 0 ;
case SNDCTL_DSP_SETDUPLEX :
/* XXX fix */
return 0 ;
case SNDCTL_DSP_GETCAPS :
return put_user ( DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP , p ) ;
case SNDCTL_DSP_RESET :
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( file - > f_mode & FMODE_WRITE ) {
stop_dac ( s ) ;
synchronize_irq ( s - > card - > pcidev - > irq ) ;
s - > dma_dac . swptr = s - > dma_dac . hwptr = s - > dma_dac . count = s - > dma_dac . total_bytes = 0 ;
}
if ( file - > f_mode & FMODE_READ ) {
stop_adc ( s ) ;
synchronize_irq ( s - > card - > pcidev - > irq ) ;
s - > dma_adc . swptr = s - > dma_adc . hwptr = s - > dma_adc . count = s - > dma_adc . total_bytes = 0 ;
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return 0 ;
case SNDCTL_DSP_SPEED :
get_user_ret ( val , p , - EFAULT ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( val > = 0 ) {
if ( file - > f_mode & FMODE_READ ) {
stop_adc ( s ) ;
s - > dma_adc . ready = 0 ;
set_adc_rate ( s , val ) ;
}
if ( file - > f_mode & FMODE_WRITE ) {
stop_dac ( s ) ;
s - > dma_dac . ready = 0 ;
set_dac_rate ( s , val ) ;
}
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return put_user ( ( file - > f_mode & FMODE_READ ) ? s - > rateadc : s - > ratedac , p ) ;
case SNDCTL_DSP_STEREO :
get_user_ret ( val , p , - EFAULT ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
fmtd = 0 ;
fmtm = ~ 0 ;
if ( file - > f_mode & FMODE_READ ) {
stop_adc ( s ) ;
s - > dma_adc . ready = 0 ;
if ( val )
fmtd | = ESS_FMT_STEREO < < ESS_ADC_SHIFT ;
else
fmtm & = ~ ( ESS_FMT_STEREO < < ESS_ADC_SHIFT ) ;
}
if ( file - > f_mode & FMODE_WRITE ) {
stop_dac ( s ) ;
s - > dma_dac . ready = 0 ;
if ( val )
fmtd | = ESS_FMT_STEREO < < ESS_DAC_SHIFT ;
else
fmtm & = ~ ( ESS_FMT_STEREO < < ESS_DAC_SHIFT ) ;
}
set_fmt ( s , fmtm , fmtd ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return 0 ;
case SNDCTL_DSP_CHANNELS :
get_user_ret ( val , p , - EFAULT ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( val ! = 0 ) {
fmtd = 0 ;
fmtm = ~ 0 ;
if ( file - > f_mode & FMODE_READ ) {
stop_adc ( s ) ;
s - > dma_adc . ready = 0 ;
if ( val > = 2 )
fmtd | = ESS_FMT_STEREO < < ESS_ADC_SHIFT ;
else
fmtm & = ~ ( ESS_FMT_STEREO < < ESS_ADC_SHIFT ) ;
}
if ( file - > f_mode & FMODE_WRITE ) {
stop_dac ( s ) ;
s - > dma_dac . ready = 0 ;
if ( val > = 2 )
fmtd | = ESS_FMT_STEREO < < ESS_DAC_SHIFT ;
else
fmtm & = ~ ( ESS_FMT_STEREO < < ESS_DAC_SHIFT ) ;
}
set_fmt ( s , fmtm , fmtd ) ;
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return put_user ( ( s - > fmt & ( ( file - > f_mode & FMODE_READ ) ? ( ESS_FMT_STEREO < < ESS_ADC_SHIFT )
: ( ESS_FMT_STEREO < < ESS_DAC_SHIFT ) ) ) ? 2 : 1 , p ) ;
case SNDCTL_DSP_GETFMTS : /* Returns a mask */
return put_user ( AFMT_U8 | AFMT_S16_LE , p ) ;
case SNDCTL_DSP_SETFMT : /* Selects ONE fmt*/
get_user_ret ( val , p , - EFAULT ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( val ! = AFMT_QUERY ) {
fmtd = 0 ;
fmtm = ~ 0 ;
if ( file - > f_mode & FMODE_READ ) {
stop_adc ( s ) ;
s - > dma_adc . ready = 0 ;
if ( val = = AFMT_S16_LE )
fmtd | = ESS_FMT_16BIT < < ESS_ADC_SHIFT ;
else
fmtm & = ~ ( ESS_FMT_16BIT < < ESS_ADC_SHIFT ) ;
}
if ( file - > f_mode & FMODE_WRITE ) {
stop_dac ( s ) ;
s - > dma_dac . ready = 0 ;
if ( val = = AFMT_S16_LE )
fmtd | = ESS_FMT_16BIT < < ESS_DAC_SHIFT ;
else
fmtm & = ~ ( ESS_FMT_16BIT < < ESS_DAC_SHIFT ) ;
}
set_fmt ( s , fmtm , fmtd ) ;
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return put_user ( ( s - > fmt & ( ( file - > f_mode & FMODE_READ ) ?
( ESS_FMT_16BIT < < ESS_ADC_SHIFT )
: ( ESS_FMT_16BIT < < ESS_DAC_SHIFT ) ) ) ?
AFMT_S16_LE :
AFMT_U8 ,
p ) ;
case SNDCTL_DSP_POST :
return 0 ;
case SNDCTL_DSP_GETTRIGGER :
val = 0 ;
if ( ( file - > f_mode & FMODE_READ ) & & ( s - > enable & ADC_RUNNING ) )
val | = PCM_ENABLE_INPUT ;
if ( ( file - > f_mode & FMODE_WRITE ) & & ( s - > enable & DAC_RUNNING ) )
val | = PCM_ENABLE_OUTPUT ;
return put_user ( val , p ) ;
case SNDCTL_DSP_SETTRIGGER :
get_user_ret ( val , p , - EFAULT ) ;
if ( file - > f_mode & FMODE_READ ) {
if ( val & PCM_ENABLE_INPUT ) {
if ( ! s - > dma_adc . ready & & ( ret = prog_dmabuf ( s , 1 ) ) )
return ret ;
start_adc ( s ) ;
} else
stop_adc ( s ) ;
}
if ( file - > f_mode & FMODE_WRITE ) {
if ( val & PCM_ENABLE_OUTPUT ) {
if ( ! s - > dma_dac . ready & & ( ret = prog_dmabuf ( s , 0 ) ) )
return ret ;
start_dac ( s ) ;
} else
stop_dac ( s ) ;
}
return 0 ;
case SNDCTL_DSP_GETOSPACE :
if ( ! ( file - > f_mode & FMODE_WRITE ) )
return - EINVAL ;
if ( ! ( s - > enable & DAC_RUNNING ) & & ( val = prog_dmabuf ( s , 0 ) ) ! = 0 )
return val ;
spin_lock_irqsave ( & card - > lock , flags ) ;
m3_update_ptr ( s ) ;
abinfo . fragsize = s - > dma_dac . fragsize ;
abinfo . bytes = s - > dma_dac . dmasize - s - > dma_dac . count ;
abinfo . fragstotal = s - > dma_dac . numfrag ;
abinfo . fragments = abinfo . bytes > > s - > dma_dac . fragshift ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return copy_to_user ( argp , & abinfo , sizeof ( abinfo ) ) ? - EFAULT : 0 ;
case SNDCTL_DSP_GETISPACE :
if ( ! ( file - > f_mode & FMODE_READ ) )
return - EINVAL ;
if ( ! ( s - > enable & ADC_RUNNING ) & & ( val = prog_dmabuf ( s , 1 ) ) ! = 0 )
return val ;
spin_lock_irqsave ( & card - > lock , flags ) ;
m3_update_ptr ( s ) ;
abinfo . fragsize = s - > dma_adc . fragsize ;
abinfo . bytes = s - > dma_adc . count ;
abinfo . fragstotal = s - > dma_adc . numfrag ;
abinfo . fragments = abinfo . bytes > > s - > dma_adc . fragshift ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return copy_to_user ( argp , & abinfo , sizeof ( abinfo ) ) ? - EFAULT : 0 ;
case SNDCTL_DSP_NONBLOCK :
file - > f_flags | = O_NONBLOCK ;
return 0 ;
case SNDCTL_DSP_GETODELAY :
if ( ! ( file - > f_mode & FMODE_WRITE ) )
return - EINVAL ;
spin_lock_irqsave ( & card - > lock , flags ) ;
m3_update_ptr ( s ) ;
val = s - > dma_dac . count ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return put_user ( val , p ) ;
case SNDCTL_DSP_GETIPTR :
if ( ! ( file - > f_mode & FMODE_READ ) )
return - EINVAL ;
spin_lock_irqsave ( & card - > lock , flags ) ;
m3_update_ptr ( s ) ;
cinfo . bytes = s - > dma_adc . total_bytes ;
cinfo . blocks = s - > dma_adc . count > > s - > dma_adc . fragshift ;
cinfo . ptr = s - > dma_adc . hwptr ;
if ( s - > dma_adc . mapped )
s - > dma_adc . count & = s - > dma_adc . fragsize - 1 ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
if ( copy_to_user ( argp , & cinfo , sizeof ( cinfo ) ) )
return - EFAULT ;
return 0 ;
case SNDCTL_DSP_GETOPTR :
if ( ! ( file - > f_mode & FMODE_WRITE ) )
return - EINVAL ;
spin_lock_irqsave ( & card - > lock , flags ) ;
m3_update_ptr ( s ) ;
cinfo . bytes = s - > dma_dac . total_bytes ;
cinfo . blocks = s - > dma_dac . count > > s - > dma_dac . fragshift ;
cinfo . ptr = s - > dma_dac . hwptr ;
if ( s - > dma_dac . mapped )
s - > dma_dac . count & = s - > dma_dac . fragsize - 1 ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
if ( copy_to_user ( argp , & cinfo , sizeof ( cinfo ) ) )
return - EFAULT ;
return 0 ;
case SNDCTL_DSP_GETBLKSIZE :
if ( file - > f_mode & FMODE_WRITE ) {
if ( ( val = prog_dmabuf ( s , 0 ) ) )
return val ;
return put_user ( s - > dma_dac . fragsize , p ) ;
}
if ( ( val = prog_dmabuf ( s , 1 ) ) )
return val ;
return put_user ( s - > dma_adc . fragsize , p ) ;
case SNDCTL_DSP_SETFRAGMENT :
get_user_ret ( val , p , - EFAULT ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( file - > f_mode & FMODE_READ ) {
s - > dma_adc . ossfragshift = val & 0xffff ;
s - > dma_adc . ossmaxfrags = ( val > > 16 ) & 0xffff ;
if ( s - > dma_adc . ossfragshift < 4 )
s - > dma_adc . ossfragshift = 4 ;
if ( s - > dma_adc . ossfragshift > 15 )
s - > dma_adc . ossfragshift = 15 ;
if ( s - > dma_adc . ossmaxfrags < 4 )
s - > dma_adc . ossmaxfrags = 4 ;
}
if ( file - > f_mode & FMODE_WRITE ) {
s - > dma_dac . ossfragshift = val & 0xffff ;
s - > dma_dac . ossmaxfrags = ( val > > 16 ) & 0xffff ;
if ( s - > dma_dac . ossfragshift < 4 )
s - > dma_dac . ossfragshift = 4 ;
if ( s - > dma_dac . ossfragshift > 15 )
s - > dma_dac . ossfragshift = 15 ;
if ( s - > dma_dac . ossmaxfrags < 4 )
s - > dma_dac . ossmaxfrags = 4 ;
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return 0 ;
case SNDCTL_DSP_SUBDIVIDE :
if ( ( file - > f_mode & FMODE_READ & & s - > dma_adc . subdivision ) | |
( file - > f_mode & FMODE_WRITE & & s - > dma_dac . subdivision ) )
return - EINVAL ;
get_user_ret ( val , p , - EFAULT ) ;
if ( val ! = 1 & & val ! = 2 & & val ! = 4 )
return - EINVAL ;
if ( file - > f_mode & FMODE_READ )
s - > dma_adc . subdivision = val ;
if ( file - > f_mode & FMODE_WRITE )
s - > dma_dac . subdivision = val ;
return 0 ;
case SOUND_PCM_READ_RATE :
return put_user ( ( file - > f_mode & FMODE_READ ) ? s - > rateadc : s - > ratedac , p ) ;
case SOUND_PCM_READ_CHANNELS :
return put_user ( ( s - > fmt & ( ( file - > f_mode & FMODE_READ ) ? ( ESS_FMT_STEREO < < ESS_ADC_SHIFT )
: ( ESS_FMT_STEREO < < ESS_DAC_SHIFT ) ) ) ? 2 : 1 , p ) ;
case SOUND_PCM_READ_BITS :
return put_user ( ( s - > fmt & ( ( file - > f_mode & FMODE_READ ) ? ( ESS_FMT_16BIT < < ESS_ADC_SHIFT )
: ( ESS_FMT_16BIT < < ESS_DAC_SHIFT ) ) ) ? 16 : 8 , p ) ;
case SOUND_PCM_WRITE_FILTER :
case SNDCTL_DSP_SETSYNCRO :
case SOUND_PCM_READ_FILTER :
return - EINVAL ;
}
return - EINVAL ;
}
static int
allocate_dmabuf ( struct pci_dev * pci_dev , struct dmabuf * db )
{
int order ;
DPRINTK ( DPSTR , " allocating for dmabuf %p \n " , db ) ;
/*
* alloc as big a chunk as we can , start with
* 64 k ' cause we ' re insane . based on order cause
* the amazingly complicated prog_dmabuf wants it .
*
* pci_alloc_sonsistent guarantees that it won ' t cross a natural
* boundary ; the m3 hardware can ' t have dma cross a 64 k bus
* address boundary .
*/
for ( order = 16 - PAGE_SHIFT ; order > = 1 ; order - - ) {
db - > rawbuf = pci_alloc_consistent ( pci_dev , PAGE_SIZE < < order ,
& ( db - > handle ) ) ;
if ( db - > rawbuf )
break ;
}
if ( ! db - > rawbuf )
return 1 ;
DPRINTK ( DPSTR , " allocated %ld (%d) bytes at %p \n " ,
PAGE_SIZE < < order , order , db - > rawbuf ) ;
{
struct page * page , * pend ;
pend = virt_to_page ( db - > rawbuf + ( PAGE_SIZE < < order ) - 1 ) ;
for ( page = virt_to_page ( db - > rawbuf ) ; page < = pend ; page + + )
SetPageReserved ( page ) ;
}
db - > buforder = order ;
db - > ready = 0 ;
db - > mapped = 0 ;
return 0 ;
}
static void
nuke_lists ( struct m3_card * card , struct dmabuf * db )
{
m3_remove_list ( card , & ( card - > dma_list ) , db - > dma_index ) ;
m3_remove_list ( card , & ( card - > msrc_list ) , db - > msrc_index ) ;
db - > in_lists = 0 ;
}
static void
free_dmabuf ( struct pci_dev * pci_dev , struct dmabuf * db )
{
if ( db - > rawbuf = = NULL )
return ;
DPRINTK ( DPSTR , " freeing %p from dmabuf %p \n " , db - > rawbuf , db ) ;
{
struct page * page , * pend ;
pend = virt_to_page ( db - > rawbuf + ( PAGE_SIZE < < db - > buforder ) - 1 ) ;
for ( page = virt_to_page ( db - > rawbuf ) ; page < = pend ; page + + )
ClearPageReserved ( page ) ;
}
pci_free_consistent ( pci_dev , PAGE_SIZE < < db - > buforder ,
db - > rawbuf , db - > handle ) ;
db - > rawbuf = NULL ;
db - > buforder = 0 ;
db - > mapped = 0 ;
db - > ready = 0 ;
}
static int m3_open ( struct inode * inode , struct file * file )
{
unsigned int minor = iminor ( inode ) ;
struct m3_card * c ;
struct m3_state * s = NULL ;
int i ;
unsigned char fmtm = ~ 0 , fmts = 0 ;
unsigned long flags ;
/*
* Scan the cards and find the channel . We only
* do this at open time so it is ok
*/
for ( c = devs ; c ! = NULL ; c = c - > next ) {
for ( i = 0 ; i < NR_DSPS ; i + + ) {
if ( c - > channels [ i ] . dev_audio < 0 )
continue ;
if ( ( c - > channels [ i ] . dev_audio ^ minor ) & ~ 0xf )
continue ;
s = & c - > channels [ i ] ;
break ;
}
}
if ( ! s )
return - ENODEV ;
VALIDATE_STATE ( s ) ;
file - > private_data = s ;
/* wait for device to become free */
2006-03-23 03:00:39 -08:00
mutex_lock ( & s - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
while ( s - > open_mode & file - > f_mode ) {
if ( file - > f_flags & O_NONBLOCK ) {
2006-03-23 03:00:39 -08:00
mutex_unlock ( & s - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
return - EWOULDBLOCK ;
}
2006-03-23 03:00:39 -08:00
mutex_unlock ( & s - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
interruptible_sleep_on ( & s - > open_wait ) ;
if ( signal_pending ( current ) )
return - ERESTARTSYS ;
2006-03-23 03:00:39 -08:00
mutex_lock ( & s - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
}
spin_lock_irqsave ( & c - > lock , flags ) ;
if ( file - > f_mode & FMODE_READ ) {
fmtm & = ~ ( ( ESS_FMT_STEREO | ESS_FMT_16BIT ) < < ESS_ADC_SHIFT ) ;
if ( ( minor & 0xf ) = = SND_DEV_DSP16 )
fmts | = ESS_FMT_16BIT < < ESS_ADC_SHIFT ;
s - > dma_adc . ossfragshift = s - > dma_adc . ossmaxfrags = s - > dma_adc . subdivision = 0 ;
set_adc_rate ( s , 8000 ) ;
}
if ( file - > f_mode & FMODE_WRITE ) {
fmtm & = ~ ( ( ESS_FMT_STEREO | ESS_FMT_16BIT ) < < ESS_DAC_SHIFT ) ;
if ( ( minor & 0xf ) = = SND_DEV_DSP16 )
fmts | = ESS_FMT_16BIT < < ESS_DAC_SHIFT ;
s - > dma_dac . ossfragshift = s - > dma_dac . ossmaxfrags = s - > dma_dac . subdivision = 0 ;
set_dac_rate ( s , 8000 ) ;
}
set_fmt ( s , fmtm , fmts ) ;
s - > open_mode | = file - > f_mode & ( FMODE_READ | FMODE_WRITE ) ;
2006-03-23 03:00:39 -08:00
mutex_unlock ( & s - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & c - > lock , flags ) ;
return nonseekable_open ( inode , file ) ;
}
static int m3_release ( struct inode * inode , struct file * file )
{
struct m3_state * s = ( struct m3_state * ) file - > private_data ;
struct m3_card * card = s - > card ;
unsigned long flags ;
VALIDATE_STATE ( s ) ;
if ( file - > f_mode & FMODE_WRITE )
drain_dac ( s , file - > f_flags & O_NONBLOCK ) ;
2006-03-23 03:00:39 -08:00
mutex_lock ( & s - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( file - > f_mode & FMODE_WRITE ) {
stop_dac ( s ) ;
if ( s - > dma_dac . in_lists ) {
m3_remove_list ( s - > card , & ( s - > card - > mixer_list ) , s - > dma_dac . mixer_index ) ;
nuke_lists ( s - > card , & ( s - > dma_dac ) ) ;
}
}
if ( file - > f_mode & FMODE_READ ) {
stop_adc ( s ) ;
if ( s - > dma_adc . in_lists ) {
m3_remove_list ( s - > card , & ( s - > card - > adc1_list ) , s - > dma_adc . adc1_index ) ;
nuke_lists ( s - > card , & ( s - > dma_adc ) ) ;
}
}
s - > open_mode & = ( ~ file - > f_mode ) & ( FMODE_READ | FMODE_WRITE ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
2006-03-23 03:00:39 -08:00
mutex_unlock ( & s - > open_mutex ) ;
2005-04-16 15:20:36 -07:00
wake_up ( & s - > open_wait ) ;
return 0 ;
}
/*
* Wait for the ac97 serial bus to be free .
* return nonzero if the bus is still busy .
*/
static int m3_ac97_wait ( struct m3_card * card )
{
int i = 10000 ;
while ( ( m3_inb ( card , 0x30 ) & 1 ) & & i - - ) ;
return i = = 0 ;
}
static u16 m3_ac97_read ( struct ac97_codec * codec , u8 reg )
{
u16 ret = 0 ;
struct m3_card * card = codec - > private_data ;
spin_lock ( & card - > ac97_lock ) ;
if ( m3_ac97_wait ( card ) ) {
printk ( KERN_ERR PFX " serial bus busy reading reg 0x%x \n " , reg ) ;
goto out ;
}
m3_outb ( card , 0x80 | ( reg & 0x7f ) , 0x30 ) ;
if ( m3_ac97_wait ( card ) ) {
printk ( KERN_ERR PFX " serial bus busy finishing read reg 0x%x \n " , reg ) ;
goto out ;
}
ret = m3_inw ( card , 0x32 ) ;
DPRINTK ( DPCRAP , " reading 0x%04x from 0x%02x \n " , ret , reg ) ;
out :
spin_unlock ( & card - > ac97_lock ) ;
return ret ;
}
static void m3_ac97_write ( struct ac97_codec * codec , u8 reg , u16 val )
{
struct m3_card * card = codec - > private_data ;
spin_lock ( & card - > ac97_lock ) ;
if ( m3_ac97_wait ( card ) ) {
printk ( KERN_ERR PFX " serial bus busy writing 0x%x to 0x%x \n " , val , reg ) ;
goto out ;
}
DPRINTK ( DPCRAP , " writing 0x%04x to 0x%02x \n " , val , reg ) ;
m3_outw ( card , val , 0x32 ) ;
m3_outb ( card , reg & 0x7f , 0x30 ) ;
out :
spin_unlock ( & card - > ac97_lock ) ;
}
/* OSS /dev/mixer file operation methods */
static int m3_open_mixdev ( struct inode * inode , struct file * file )
{
unsigned int minor = iminor ( inode ) ;
struct m3_card * card = devs ;
for ( card = devs ; card ! = NULL ; card = card - > next ) {
if ( ( card - > ac97 ! = NULL ) & & ( card - > ac97 - > dev_mixer = = minor ) )
break ;
}
if ( ! card ) {
return - ENODEV ;
}
file - > private_data = card - > ac97 ;
return nonseekable_open ( inode , file ) ;
}
static int m3_release_mixdev ( struct inode * inode , struct file * file )
{
return 0 ;
}
static int m3_ioctl_mixdev ( 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 m3_mixer_fops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. ioctl = m3_ioctl_mixdev ,
. open = m3_open_mixdev ,
. release = m3_release_mixdev ,
} ;
static void remote_codec_config ( int io , int isremote )
{
isremote = isremote ? 1 : 0 ;
outw ( ( inw ( io + RING_BUS_CTRL_B ) & ~ SECOND_CODEC_ID_MASK ) | isremote ,
io + RING_BUS_CTRL_B ) ;
outw ( ( inw ( io + SDO_OUT_DEST_CTRL ) & ~ COMMAND_ADDR_OUT ) | isremote ,
io + SDO_OUT_DEST_CTRL ) ;
outw ( ( inw ( io + SDO_IN_DEST_CTRL ) & ~ STATUS_ADDR_IN ) | isremote ,
io + SDO_IN_DEST_CTRL ) ;
}
/*
* hack , returns non zero on err
*/
static int try_read_vendor ( struct m3_card * card )
{
u16 ret ;
if ( m3_ac97_wait ( card ) )
return 1 ;
m3_outb ( card , 0x80 | ( AC97_VENDOR_ID1 & 0x7f ) , 0x30 ) ;
if ( m3_ac97_wait ( card ) )
return 1 ;
ret = m3_inw ( card , 0x32 ) ;
return ( ret = = 0 ) | | ( ret = = 0xffff ) ;
}
static void m3_codec_reset ( struct m3_card * card , int busywait )
{
u16 dir ;
int delay1 = 0 , delay2 = 0 , i ;
int io = card - > iobase ;
switch ( card - > card_type ) {
/*
* the onboard codec on the allegro seems
* to want to wait a very long time before
* coming back to life
*/
case ESS_ALLEGRO :
delay1 = 50 ;
delay2 = 800 ;
break ;
case ESS_MAESTRO3 :
case ESS_MAESTRO3HW :
delay1 = 20 ;
delay2 = 500 ;
break ;
}
for ( i = 0 ; i < 5 ; i + + ) {
dir = inw ( io + GPIO_DIRECTION ) ;
dir | = 0x10 ; /* assuming pci bus master? */
remote_codec_config ( io , 0 ) ;
outw ( IO_SRAM_ENABLE , io + RING_BUS_CTRL_A ) ;
udelay ( 20 ) ;
outw ( dir & ~ GPO_PRIMARY_AC97 , io + GPIO_DIRECTION ) ;
outw ( ~ GPO_PRIMARY_AC97 , io + GPIO_MASK ) ;
outw ( 0 , io + GPIO_DATA ) ;
outw ( dir | GPO_PRIMARY_AC97 , io + GPIO_DIRECTION ) ;
if ( busywait ) {
mdelay ( delay1 ) ;
} else {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( ( delay1 * HZ ) / 1000 ) ;
}
outw ( GPO_PRIMARY_AC97 , io + GPIO_DATA ) ;
udelay ( 5 ) ;
/* ok, bring back the ac-link */
outw ( IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE , io + RING_BUS_CTRL_A ) ;
outw ( ~ 0 , io + GPIO_MASK ) ;
if ( busywait ) {
mdelay ( delay2 ) ;
} else {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( ( delay2 * HZ ) / 1000 ) ;
}
if ( ! try_read_vendor ( card ) )
break ;
delay1 + = 10 ;
delay2 + = 100 ;
DPRINTK ( DPMOD , " retrying codec reset with delays of %d and %d ms \n " ,
delay1 , delay2 ) ;
}
#if 0
/* more gung-ho reset that doesn't
* seem to work anywhere : )
*/
tmp = inw ( io + RING_BUS_CTRL_A ) ;
outw ( RAC_SDFS_ENABLE | LAC_SDFS_ENABLE , io + RING_BUS_CTRL_A ) ;
mdelay ( 20 ) ;
outw ( tmp , io + RING_BUS_CTRL_A ) ;
mdelay ( 50 ) ;
# endif
}
static int __devinit m3_codec_install ( struct m3_card * card )
{
struct ac97_codec * codec ;
if ( ( codec = ac97_alloc_codec ( ) ) = = NULL )
return - ENOMEM ;
codec - > private_data = card ;
codec - > codec_read = m3_ac97_read ;
codec - > codec_write = m3_ac97_write ;
/* someday we should support secondary codecs.. */
codec - > id = 0 ;
if ( ac97_probe_codec ( codec ) = = 0 ) {
printk ( KERN_ERR PFX " codec probe failed \n " ) ;
ac97_release_codec ( codec ) ;
return - 1 ;
}
if ( ( codec - > dev_mixer = register_sound_mixer ( & m3_mixer_fops , - 1 ) ) < 0 ) {
printk ( KERN_ERR PFX " couldn't register mixer! \n " ) ;
ac97_release_codec ( codec ) ;
return - 1 ;
}
card - > ac97 = codec ;
return 0 ;
}
# define MINISRC_LPF_LEN 10
static u16 minisrc_lpf [ MINISRC_LPF_LEN ] = {
0 X0743 , 0 X1104 , 0 X0A4C , 0 XF88D , 0 X242C ,
0 X1023 , 0 X1AA9 , 0 X0B60 , 0 XEFDD , 0 X186F
} ;
static void m3_assp_init ( struct m3_card * card )
{
int i ;
/* zero kernel data */
for ( i = 0 ; i < ( REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA ) / 2 ; i + + )
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_BASE_ADDR + i , 0 ) ;
/* zero mixer data? */
for ( i = 0 ; i < ( REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA ) / 2 ; i + + )
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_BASE_ADDR2 + i , 0 ) ;
/* init dma pointer */
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_CURRENT_DMA ,
KDATA_DMA_XFER0 ) ;
/* write kernel into code memory.. */
for ( i = 0 ; i < sizeof ( assp_kernel_image ) / 2 ; i + + ) {
m3_assp_write ( card , MEMTYPE_INTERNAL_CODE ,
REV_B_CODE_MEMORY_BEGIN + i ,
assp_kernel_image [ i ] ) ;
}
/*
* We only have this one client and we know that 0x400
* is free in our kernel ' s mem map , so lets just
* drop it there . It seems that the minisrc doesn ' t
* need vectors , so we won ' t bother with them . .
*/
for ( i = 0 ; i < sizeof ( assp_minisrc_image ) / 2 ; i + + ) {
m3_assp_write ( card , MEMTYPE_INTERNAL_CODE ,
0x400 + i ,
assp_minisrc_image [ i ] ) ;
}
/*
* write the coefficients for the low pass filter ?
*/
for ( i = 0 ; i < MINISRC_LPF_LEN ; i + + ) {
m3_assp_write ( card , MEMTYPE_INTERNAL_CODE ,
0x400 + MINISRC_COEF_LOC + i ,
minisrc_lpf [ i ] ) ;
}
m3_assp_write ( card , MEMTYPE_INTERNAL_CODE ,
0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN ,
0x8000 ) ;
/*
* the minisrc is the only thing on
* our task list . .
*/
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_TASK0 ,
0x400 ) ;
/*
* init the mixer number . .
*/
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_MIXER_TASK_NUMBER , 0 ) ;
/*
* EXTREME KERNEL MASTER VOLUME
*/
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_DAC_LEFT_VOLUME , ARB_VOLUME ) ;
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_DAC_RIGHT_VOLUME , ARB_VOLUME ) ;
card - > mixer_list . mem_addr = KDATA_MIXER_XFER0 ;
card - > mixer_list . max = MAX_VIRTUAL_MIXER_CHANNELS ;
card - > adc1_list . mem_addr = KDATA_ADC1_XFER0 ;
card - > adc1_list . max = MAX_VIRTUAL_ADC1_CHANNELS ;
card - > dma_list . mem_addr = KDATA_DMA_XFER0 ;
card - > dma_list . max = MAX_VIRTUAL_DMA_CHANNELS ;
card - > msrc_list . mem_addr = KDATA_INSTANCE0_MINISRC ;
card - > msrc_list . max = MAX_INSTANCE_MINISRC ;
}
static int setup_msrc ( struct m3_card * card ,
struct assp_instance * inst , int index )
{
int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 +
MINISRC_IN_BUFFER_SIZE / 2 +
1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ) ;
int address , i ;
/*
* the revb memory map has 0x1100 through 0x1c00
* free .
*/
/*
* align instance address to 256 bytes so that it ' s
* shifted list address is aligned .
* list address = ( mem address > > 1 ) > > 7 ;
*/
data_bytes = ( data_bytes + 255 ) & ~ 255 ;
address = 0x1100 + ( ( data_bytes / 2 ) * index ) ;
if ( ( address + ( data_bytes / 2 ) ) > = 0x1c00 ) {
printk ( KERN_ERR PFX " no memory for %d bytes at ind %d (addr 0x%x) \n " ,
data_bytes , index , address ) ;
return - 1 ;
}
for ( i = 0 ; i < data_bytes / 2 ; i + + )
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
address + i , 0 ) ;
inst - > code = 0x400 ;
inst - > data = address ;
return 0 ;
}
static int m3_assp_client_init ( struct m3_state * s )
{
setup_msrc ( s - > card , & ( s - > dac_inst ) , s - > index * 2 ) ;
setup_msrc ( s - > card , & ( s - > adc_inst ) , ( s - > index * 2 ) + 1 ) ;
return 0 ;
}
static void m3_amp_enable ( struct m3_card * card , int enable )
{
/*
* this works for the reference board , have to find
* out about others
*
* this needs more magic for 4 speaker , but . .
*/
int io = card - > iobase ;
u16 gpo , polarity_port , polarity ;
if ( ! external_amp )
return ;
if ( gpio_pin > = 0 & & gpio_pin < = 15 ) {
polarity_port = 0x1000 + ( 0x100 * gpio_pin ) ;
} else {
switch ( card - > card_type ) {
case ESS_ALLEGRO :
polarity_port = 0x1800 ;
break ;
default :
polarity_port = 0x1100 ;
/* Panasonic toughbook CF72 has to be different... */
if ( card - > pcidev - > subsystem_vendor = = 0x10F7 & & card - > pcidev - > subsystem_device = = 0x833D )
polarity_port = 0x1D00 ;
break ;
}
}
gpo = ( polarity_port > > 8 ) & 0x0F ;
polarity = polarity_port > > 12 ;
if ( enable )
polarity = ! polarity ;
polarity = polarity < < gpo ;
gpo = 1 < < gpo ;
outw ( ~ gpo , io + GPIO_MASK ) ;
outw ( inw ( io + GPIO_DIRECTION ) | gpo ,
io + GPIO_DIRECTION ) ;
outw ( ( GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity ) ,
io + GPIO_DATA ) ;
outw ( 0xffff , io + GPIO_MASK ) ;
}
static int
maestro_config ( struct m3_card * card )
{
struct pci_dev * pcidev = card - > pcidev ;
u32 n ;
u8 t ; /* makes as much sense as 'n', no? */
pci_read_config_dword ( pcidev , PCI_ALLEGRO_CONFIG , & n ) ;
n & = REDUCED_DEBOUNCE ;
n | = PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING ;
pci_write_config_dword ( pcidev , PCI_ALLEGRO_CONFIG , n ) ;
outb ( RESET_ASSP , card - > iobase + ASSP_CONTROL_B ) ;
pci_read_config_dword ( pcidev , PCI_ALLEGRO_CONFIG , & n ) ;
n & = ~ INT_CLK_SELECT ;
if ( card - > card_type > = ESS_MAESTRO3 ) {
n & = ~ INT_CLK_MULT_ENABLE ;
n | = INT_CLK_SRC_NOT_PCI ;
}
n & = ~ ( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ) ;
pci_write_config_dword ( pcidev , PCI_ALLEGRO_CONFIG , n ) ;
if ( card - > card_type < = ESS_ALLEGRO ) {
pci_read_config_dword ( pcidev , PCI_USER_CONFIG , & n ) ;
n | = IN_CLK_12MHZ_SELECT ;
pci_write_config_dword ( pcidev , PCI_USER_CONFIG , n ) ;
}
t = inb ( card - > iobase + ASSP_CONTROL_A ) ;
t & = ~ ( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT ) ;
t | = ASSP_CLK_49MHZ_SELECT ;
t | = ASSP_0_WS_ENABLE ;
outb ( t , card - > iobase + ASSP_CONTROL_A ) ;
outb ( RUN_ASSP , card - > iobase + ASSP_CONTROL_B ) ;
return 0 ;
}
static void m3_enable_ints ( struct m3_card * card )
{
unsigned long io = card - > iobase ;
outw ( ASSP_INT_ENABLE , io + HOST_INT_CTRL ) ;
outb ( inb ( io + ASSP_CONTROL_C ) | ASSP_HOST_INT_ENABLE ,
io + ASSP_CONTROL_C ) ;
}
static struct file_operations m3_audio_fops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = m3_read ,
. write = m3_write ,
. poll = m3_poll ,
. ioctl = m3_ioctl ,
. mmap = m3_mmap ,
. open = m3_open ,
. release = m3_release ,
} ;
# ifdef CONFIG_PM
static int alloc_dsp_suspendmem ( struct m3_card * card )
{
int len = sizeof ( u16 ) * ( REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH ) ;
if ( ( card - > suspend_mem = vmalloc ( len ) ) = = NULL )
return 1 ;
return 0 ;
}
# else
# define alloc_dsp_suspendmem(args...) 0
# endif
/*
* great day ! this function is ugly as hell .
*/
static int __devinit m3_probe ( struct pci_dev * pci_dev , const struct pci_device_id * pci_id )
{
u32 n ;
int i ;
struct m3_card * card = NULL ;
int ret = 0 ;
int card_type = pci_id - > driver_data ;
DPRINTK ( DPMOD , " in maestro_install \n " ) ;
if ( pci_enable_device ( pci_dev ) )
return - EIO ;
if ( pci_set_dma_mask ( pci_dev , M3_PCI_DMA_MASK ) ) {
printk ( KERN_ERR PFX " architecture does not support limiting to 28bit PCI bus addresses \n " ) ;
return - ENODEV ;
}
pci_set_master ( pci_dev ) ;
if ( ( card = kmalloc ( sizeof ( struct m3_card ) , GFP_KERNEL ) ) = = NULL ) {
printk ( KERN_WARNING PFX " out of memory \n " ) ;
return - ENOMEM ;
}
memset ( card , 0 , sizeof ( struct m3_card ) ) ;
card - > pcidev = pci_dev ;
init_waitqueue_head ( & card - > suspend_queue ) ;
if ( ! request_region ( pci_resource_start ( pci_dev , 0 ) ,
pci_resource_len ( pci_dev , 0 ) , M3_MODULE_NAME ) ) {
printk ( KERN_WARNING PFX " unable to reserve I/O space. \n " ) ;
ret = - EBUSY ;
goto out ;
}
card - > iobase = pci_resource_start ( pci_dev , 0 ) ;
if ( alloc_dsp_suspendmem ( card ) ) {
printk ( KERN_WARNING PFX " couldn't alloc %d bytes for saving dsp state on suspend \n " ,
REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH ) ;
ret = - ENOMEM ;
goto out ;
}
card - > card_type = card_type ;
card - > irq = pci_dev - > irq ;
card - > next = devs ;
card - > magic = M3_CARD_MAGIC ;
spin_lock_init ( & card - > lock ) ;
spin_lock_init ( & card - > ac97_lock ) ;
devs = card ;
for ( i = 0 ; i < NR_DSPS ; i + + ) {
struct m3_state * s = & ( card - > channels [ i ] ) ;
s - > dev_audio = - 1 ;
}
printk ( KERN_INFO PFX " Configuring ESS %s found at IO 0x%04X IRQ %d \n " ,
card_names [ card - > card_type ] , card - > iobase , card - > irq ) ;
pci_read_config_dword ( pci_dev , PCI_SUBSYSTEM_VENDOR_ID , & n ) ;
printk ( KERN_INFO PFX " subvendor id: 0x%08x \n " , n ) ;
maestro_config ( card ) ;
m3_assp_halt ( card ) ;
m3_codec_reset ( card , 0 ) ;
if ( m3_codec_install ( card ) ) {
ret = - EIO ;
goto out ;
}
m3_assp_init ( card ) ;
m3_amp_enable ( card , 1 ) ;
for ( i = 0 ; i < NR_DSPS ; i + + ) {
struct m3_state * s = & card - > channels [ i ] ;
s - > index = i ;
s - > card = card ;
init_waitqueue_head ( & s - > dma_adc . wait ) ;
init_waitqueue_head ( & s - > dma_dac . wait ) ;
init_waitqueue_head ( & s - > open_wait ) ;
2006-03-23 03:00:39 -08:00
mutex_init ( & ( s - > open_mutex ) ) ;
2005-04-16 15:20:36 -07:00
s - > magic = M3_STATE_MAGIC ;
m3_assp_client_init ( s ) ;
if ( s - > dma_adc . ready | | s - > dma_dac . ready | | s - > dma_adc . rawbuf )
printk ( KERN_WARNING PFX " initing a dsp device that is already in use? \n " ) ;
/* register devices */
if ( ( s - > dev_audio = register_sound_dsp ( & m3_audio_fops , - 1 ) ) < 0 ) {
break ;
}
if ( allocate_dmabuf ( card - > pcidev , & ( s - > dma_adc ) ) | |
allocate_dmabuf ( card - > pcidev , & ( s - > dma_dac ) ) ) {
ret = - ENOMEM ;
goto out ;
}
}
2006-07-01 19:29:46 -07:00
if ( request_irq ( card - > irq , m3_interrupt , IRQF_SHARED , card_names [ card - > card_type ] , card ) ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR PFX " unable to allocate irq %d, \n " , card - > irq ) ;
ret = - EIO ;
goto out ;
}
pci_set_drvdata ( pci_dev , card ) ;
m3_enable_ints ( card ) ;
m3_assp_continue ( card ) ;
out :
if ( ret ) {
if ( card - > iobase )
release_region ( pci_resource_start ( pci_dev , 0 ) , pci_resource_len ( pci_dev , 0 ) ) ;
2006-03-28 01:56:49 -08:00
vfree ( card - > suspend_mem ) ;
2005-04-16 15:20:36 -07:00
if ( card - > ac97 ) {
unregister_sound_mixer ( card - > ac97 - > dev_mixer ) ;
kfree ( card - > ac97 ) ;
}
for ( i = 0 ; i < NR_DSPS ; i + + )
{
struct m3_state * s = & card - > channels [ i ] ;
if ( s - > dev_audio ! = - 1 )
unregister_sound_dsp ( s - > dev_audio ) ;
}
kfree ( card ) ;
}
return ret ;
}
static void m3_remove ( struct pci_dev * pci_dev )
{
struct m3_card * card ;
unregister_reboot_notifier ( & m3_reboot_nb ) ;
while ( ( card = devs ) ) {
int i ;
devs = devs - > next ;
free_irq ( card - > irq , card ) ;
unregister_sound_mixer ( card - > ac97 - > dev_mixer ) ;
kfree ( card - > ac97 ) ;
for ( i = 0 ; i < NR_DSPS ; i + + )
{
struct m3_state * s = & card - > channels [ i ] ;
if ( s - > dev_audio < 0 )
continue ;
unregister_sound_dsp ( s - > dev_audio ) ;
free_dmabuf ( card - > pcidev , & s - > dma_adc ) ;
free_dmabuf ( card - > pcidev , & s - > dma_dac ) ;
}
release_region ( card - > iobase , 256 ) ;
2006-03-28 01:56:49 -08:00
vfree ( card - > suspend_mem ) ;
2005-04-16 15:20:36 -07:00
kfree ( card ) ;
}
devs = NULL ;
}
/*
* some bioses like the sound chip to be powered down
* at shutdown . We ' re just calling _suspend to
* achieve that . .
*/
static int m3_notifier ( struct notifier_block * nb , unsigned long event , void * buf )
{
struct m3_card * card ;
DPRINTK ( DPMOD , " notifier suspending all cards \n " ) ;
for ( card = devs ; card ! = NULL ; card = card - > next ) {
if ( ! card - > in_suspend )
m3_suspend ( card - > pcidev , PMSG_SUSPEND ) ; /* XXX legal? */
}
return 0 ;
}
static int m3_suspend ( struct pci_dev * pci_dev , pm_message_t state )
{
unsigned long flags ;
int i ;
struct m3_card * card = pci_get_drvdata ( pci_dev ) ;
/* must be a better way.. */
spin_lock_irqsave ( & card - > lock , flags ) ;
DPRINTK ( DPMOD , " pm in dev %p \n " , card ) ;
for ( i = 0 ; i < NR_DSPS ; i + + ) {
struct m3_state * s = & card - > channels [ i ] ;
if ( s - > dev_audio = = - 1 )
continue ;
DPRINTK ( DPMOD , " stop_adc/dac() device %d \n " , i ) ;
stop_dac ( s ) ;
stop_adc ( s ) ;
}
mdelay ( 10 ) ; /* give the assp a chance to idle.. */
m3_assp_halt ( card ) ;
if ( card - > suspend_mem ) {
int index = 0 ;
DPRINTK ( DPMOD , " saving code \n " ) ;
for ( i = REV_B_CODE_MEMORY_BEGIN ; i < = REV_B_CODE_MEMORY_END ; i + + )
card - > suspend_mem [ index + + ] =
m3_assp_read ( card , MEMTYPE_INTERNAL_CODE , i ) ;
DPRINTK ( DPMOD , " saving data \n " ) ;
for ( i = REV_B_DATA_MEMORY_BEGIN ; i < = REV_B_DATA_MEMORY_END ; i + + )
card - > suspend_mem [ index + + ] =
m3_assp_read ( card , MEMTYPE_INTERNAL_DATA , i ) ;
}
DPRINTK ( DPMOD , " powering down apci regs \n " ) ;
m3_outw ( card , 0xffff , 0x54 ) ;
m3_outw ( card , 0xffff , 0x56 ) ;
card - > in_suspend = 1 ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
return 0 ;
}
static int m3_resume ( struct pci_dev * pci_dev )
{
unsigned long flags ;
int index ;
int i ;
struct m3_card * card = pci_get_drvdata ( pci_dev ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
card - > in_suspend = 0 ;
DPRINTK ( DPMOD , " resuming \n " ) ;
/* first lets just bring everything back. .*/
DPRINTK ( DPMOD , " bringing power back on card 0x%p \n " , card ) ;
m3_outw ( card , 0 , 0x54 ) ;
m3_outw ( card , 0 , 0x56 ) ;
DPRINTK ( DPMOD , " restoring pci configs and reseting codec \n " ) ;
maestro_config ( card ) ;
m3_assp_halt ( card ) ;
m3_codec_reset ( card , 1 ) ;
DPRINTK ( DPMOD , " restoring dsp code card \n " ) ;
index = 0 ;
for ( i = REV_B_CODE_MEMORY_BEGIN ; i < = REV_B_CODE_MEMORY_END ; i + + )
m3_assp_write ( card , MEMTYPE_INTERNAL_CODE , i ,
card - > suspend_mem [ index + + ] ) ;
for ( i = REV_B_DATA_MEMORY_BEGIN ; i < = REV_B_DATA_MEMORY_END ; i + + )
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA , i ,
card - > suspend_mem [ index + + ] ) ;
/* tell the dma engine to restart itself */
m3_assp_write ( card , MEMTYPE_INTERNAL_DATA ,
KDATA_DMA_ACTIVE , 0 ) ;
DPRINTK ( DPMOD , " resuming dsp \n " ) ;
m3_assp_continue ( card ) ;
DPRINTK ( DPMOD , " enabling ints \n " ) ;
m3_enable_ints ( card ) ;
/* bring back the old school flavor */
for ( i = 0 ; i < SOUND_MIXER_NRDEVICES ; i + + ) {
int state = card - > ac97 - > mixer_state [ i ] ;
if ( ! supported_mixer ( card - > ac97 , i ) )
continue ;
card - > ac97 - > write_mixer ( card - > ac97 , i ,
state & 0xff , ( state > > 8 ) & 0xff ) ;
}
m3_amp_enable ( card , 1 ) ;
/*
* now we flip on the music
*/
for ( i = 0 ; i < NR_DSPS ; i + + ) {
struct m3_state * s = & card - > channels [ i ] ;
if ( s - > dev_audio = = - 1 )
continue ;
/*
* db - > ready makes it so these guys can be
* called unconditionally . .
*/
DPRINTK ( DPMOD , " turning on dacs ind %d \n " , i ) ;
start_dac ( s ) ;
start_adc ( s ) ;
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
/*
* all right , we think things are ready ,
* wake up people who were using the device
* when we suspended
*/
wake_up ( & card - > suspend_queue ) ;
return 0 ;
}
MODULE_AUTHOR ( " Zach Brown <zab@zabbo.net> " ) ;
MODULE_DESCRIPTION ( " ESS Maestro3/Allegro Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# ifdef M_DEBUG
module_param ( debug , int , 0 ) ;
# endif
module_param ( external_amp , int , 0 ) ;
module_param ( gpio_pin , int , 0 ) ;
static struct pci_driver m3_pci_driver = {
. name = " ess_m3_audio " ,
. id_table = m3_id_table ,
. probe = m3_probe ,
. remove = m3_remove ,
. suspend = m3_suspend ,
. resume = m3_resume ,
} ;
static int __init m3_init_module ( void )
{
printk ( KERN_INFO PFX " version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ " \n " ) ;
if ( register_reboot_notifier ( & m3_reboot_nb ) ) {
printk ( KERN_WARNING PFX " reboot notifier registration failed \n " ) ;
return - ENODEV ; /* ? */
}
if ( pci_register_driver ( & m3_pci_driver ) ) {
unregister_reboot_notifier ( & m3_reboot_nb ) ;
return - ENODEV ;
}
return 0 ;
}
static void __exit m3_cleanup_module ( void )
{
pci_unregister_driver ( & m3_pci_driver ) ;
}
module_init ( m3_init_module ) ;
module_exit ( m3_cleanup_module ) ;
void check_suspend ( struct m3_card * card )
{
DECLARE_WAITQUEUE ( wait , current ) ;
if ( ! card - > in_suspend )
return ;
card - > in_suspend + + ;
add_wait_queue ( & card - > suspend_queue , & wait ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule ( ) ;
remove_wait_queue ( & card - > suspend_queue , & wait ) ;
set_current_state ( TASK_RUNNING ) ;
}