blob: 58e25c82eaf29f9db180e8b702d25e1a94b2a437 [file] [log] [blame]
/*
* Crystal SoundFusion CS46xx driver
*
* Copyright 1998-2001 Cirrus Logic Corporation <pcaudio@crystal.cirrus.com>
* <twoller@crystal.cirrus.com>
* Copyright 1999-2000 Jaroslav Kysela <perex@suse.cz>
* Copyright 2000 Alan Cox <alan@redhat.com>
*
* The core of this code is taken from the ALSA project driver by
* Jaroslav. Please send Jaroslav the credit for the driver and
* report bugs in this port to <alan@redhat.com>
*
* 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 02139, USA.
* Current maintainers:
* Cirrus Logic Corporation, Thomas Woller (tw)
* <twoller@crystal.cirrus.com>
* Nils Faerber (nf)
* <nils@kernelconcepts.de>
* Thanks to David Pollard for testing.
*
* Changes:
* 20000909-nf Changed cs_read, cs_write and drain_dac
* 20001025-tw Separate Playback/Capture structs and buffers.
* Added Scatter/Gather support for Playback.
* Added Capture.
* 20001027-nf Port to kernel 2.4.0-test9, some clean-ups
* Start of powermanagement support (CS46XX_PM).
* 20001128-tw Add module parm for default buffer order.
* added DMA_GFP flag to kmalloc dma buffer allocs.
* backfill silence to eliminate stuttering on
* underruns.
* 20001201-tw add resyncing of swptr on underruns.
* 20001205-tw-nf fixed GETOSPACE ioctl() after open()
* 20010113-tw patch from Hans Grobler general cleanup.
* 20010117-tw 2.4.0 pci cleanup, wrapper code for 2.2.16-2.4.0
* 20010118-tw basic PM support for 2.2.16+ and 2.4.0/2.4.2.
* 20010228-dh patch from David Huggins - cs_update_ptr recursion.
* 20010409-tw add hercules game theatre XP amp code.
* 20010420-tw cleanup powerdown/up code.
* 20010521-tw eliminate pops, and fixes for powerdown.
* 20010525-tw added fixes for thinkpads with powerdown logic.
* 20010723-sh patch from Horms (Simon Horman) -
* SOUND_PCM_READ_BITS returns bits as set in driver
* rather than a logical or of the possible values.
* Various ioctls handle the case where the device
* is open for reading or writing but not both better.
*
* Status:
* Playback/Capture supported from 8k-48k.
* 16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported.
*
* APM/PM - 2.2.x APM is enabled and functioning fine. APM can also
* be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro
* definition.
*
* Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp,
* so, use the drain/polarity to enable.
* hercules_egpio_disable set to 1, will force a 0 to EGPIODR.
*
* VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control
* the external amplifier for the "back" speakers, since we do not
* support the secondary codec then this external amp is also not
* turned on.
*/
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/string.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/bitops.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/ac97_codec.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/uaccess.h>
#include "cs46xxpm-24.h"
#include "cs46xx_wrapper-24.h"
#include "cs461x.h"
/* MIDI buffer sizes */
#define CS_MIDIINBUF 500
#define CS_MIDIOUTBUF 500
#define ADC_RUNNING 1
#define DAC_RUNNING 2
#define CS_FMT_16BIT 1 /* These are fixed in fact */
#define CS_FMT_STEREO 2
#define CS_FMT_MASK 3
#define CS_TYPE_ADC 1
#define CS_TYPE_DAC 2
#define CS_TRUE 1
#define CS_FALSE 0
#define CS_INC_USE_COUNT(m) (atomic_inc(m))
#define CS_DEC_USE_COUNT(m) (atomic_dec(m))
#define CS_DEC_AND_TEST(m) (atomic_dec_and_test(m))
#define CS_IN_USE(m) (atomic_read(m) != 0)
#define CS_DBGBREAKPOINT {__asm__("INT $3");}
/*
* CS461x definitions
*/
#define CS461X_BA0_SIZE 0x2000
#define CS461X_BA1_DATA0_SIZE 0x3000
#define CS461X_BA1_DATA1_SIZE 0x3800
#define CS461X_BA1_PRG_SIZE 0x7000
#define CS461X_BA1_REG_SIZE 0x0100
#define GOF_PER_SEC 200
#define CSDEBUG_INTERFACE 1
#define CSDEBUG 1
/*
* Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG
*
*
* CSDEBUG is usual mode is set to 1, then use the
* cs_debuglevel and cs_debugmask to turn on or off debugging.
* Debug level of 1 has been defined to be kernel errors and info
* that should be printed on any released driver.
*/
#if CSDEBUG
#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask)) {x;}
#else
#define CS_DBGOUT(mask,level,x)
#endif
/*
* cs_debugmask areas
*/
#define CS_INIT 0x00000001 /* initialization and probe functions */
#define CS_ERROR 0x00000002 /* tmp debugging bit placeholder */
#define CS_INTERRUPT 0x00000004 /* interrupt handler (separate from all other) */
#define CS_FUNCTION 0x00000008 /* enter/leave functions */
#define CS_WAVE_WRITE 0x00000010 /* write information for wave */
#define CS_WAVE_READ 0x00000020 /* read information for wave */
#define CS_MIDI_WRITE 0x00000040 /* write information for midi */
#define CS_MIDI_READ 0x00000080 /* read information for midi */
#define CS_MPU401_WRITE 0x00000100 /* write information for mpu401 */
#define CS_MPU401_READ 0x00000200 /* read information for mpu401 */
#define CS_OPEN 0x00000400 /* all open functions in the driver */
#define CS_RELEASE 0x00000800 /* all release functions in the driver */
#define CS_PARMS 0x00001000 /* functional and operational parameters */
#define CS_IOCTL 0x00002000 /* ioctl (non-mixer) */
#define CS_PM 0x00004000 /* PM */
#define CS_TMP 0x10000000 /* tmp debug mask bit */
#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend
#define CS_IOCTL_CMD_RESUME 0x2 // resume
#if CSDEBUG
static unsigned long cs_debuglevel=1; /* levels range from 1-9 */
module_param(cs_debuglevel, ulong, 0644);
static unsigned long cs_debugmask=CS_INIT | CS_ERROR; /* use CS_DBGOUT with various mask values */
module_param(cs_debugmask, ulong, 0644);
#endif
static unsigned long hercules_egpio_disable; /* if non-zero set all EGPIO to 0 */
module_param(hercules_egpio_disable, ulong, 0);
static unsigned long initdelay=700; /* PM delay in millisecs */
module_param(initdelay, ulong, 0);
static unsigned long powerdown=-1; /* turn on/off powerdown processing in driver */
module_param(powerdown, ulong, 0);
#define DMABUF_DEFAULTORDER 3
static unsigned long defaultorder=DMABUF_DEFAULTORDER;
module_param(defaultorder, ulong, 0);
static int external_amp;
module_param(external_amp, bool, 0);
static int thinkpad;
module_param(thinkpad, bool, 0);
/*
* set the powerdown module parm to 0 to disable all
* powerdown. also set thinkpad to 1 to disable powerdown,
* but also to enable the clkrun functionality.
*/
static unsigned cs_powerdown=1;
static unsigned cs_laptop_wait=1;
/* An instance of the 4610 channel */
struct cs_channel
{
int used;
int num;
void *state;
};
#define CS46XX_MAJOR_VERSION "1"
#define CS46XX_MINOR_VERSION "28"
#ifdef __ia64__
#define CS46XX_ARCH "64" //architecture key
#else
#define CS46XX_ARCH "32" //architecture key
#endif
static struct list_head cs46xx_devs = { &cs46xx_devs, &cs46xx_devs };
/* magic numbers to protect our data structures */
#define CS_CARD_MAGIC 0x43525553 /* "CRUS" */
#define CS_STATE_MAGIC 0x4c4f4749 /* "LOGI" */
#define NR_HW_CH 3
/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
#define NR_AC97 2
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
/* "software" or virtual channel, an instance of opened /dev/dsp */
struct cs_state {
unsigned int magic;
struct cs_card *card; /* Card info */
/* single open lock mechanism, only used for recording */
struct semaphore open_sem;
wait_queue_head_t open_wait;
/* file mode */
mode_t open_mode;
/* virtual channel number */
int virt;
struct dmabuf {
/* wave sample stuff */
unsigned int rate;
unsigned char fmt, enable;
/* hardware channel */
struct cs_channel *channel;
int pringbuf; /* Software ring slot */
void *pbuf; /* 4K hardware DMA buffer */
/* OSS buffer management stuff */
void *rawbuf;
dma_addr_t dma_handle;
unsigned buforder;
unsigned numfrag;
unsigned fragshift;
unsigned divisor;
unsigned type;
void *tmpbuff; /* tmp buffer for sample conversions */
dma_addr_t dmaaddr;
dma_addr_t dmaaddr_tmpbuff;
unsigned buforder_tmpbuff; /* Log base 2 of size in bytes.. */
/* our buffer acts like a circular ring */
unsigned hwptr; /* where dma last started, updated by update_ptr */
unsigned swptr; /* where driver last clear/filled, updated by read/write */
int count; /* bytes to be comsumed or been generated by dma machine */
unsigned total_bytes; /* total bytes dmaed by hardware */
unsigned blocks; /* total blocks */
unsigned error; /* number of over/underruns */
unsigned underrun; /* underrun pending before next write has occurred */
wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */
/* redundant, but makes calculations easier */
unsigned fragsize;
unsigned dmasize;
unsigned fragsamples;
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
unsigned endcleared:1;
unsigned SGok:1;
unsigned update_flag;
unsigned ossfragshift;
int ossmaxfrags;
unsigned subdivision;
} dmabuf;
/* Guard against mmap/write/read races */
struct semaphore sem;
};
struct cs_card {
struct cs_channel channel[2];
unsigned int magic;
/* We keep cs461x cards in a linked list */
struct cs_card *next;
/* The cs461x has a certain amount of cross channel interaction
so we use a single per card lock */
spinlock_t lock;
/* Keep AC97 sane */
spinlock_t ac97_lock;
/* mixer use count */
atomic_t mixer_use_cnt;
/* PCI device stuff */
struct pci_dev * pci_dev;
struct list_head list;
unsigned int pctl, cctl; /* Hardware DMA flag sets */
/* soundcore stuff */
int dev_audio;
int dev_midi;
/* structures for abstraction of hardware facilities, codecs, banks and channels*/
struct ac97_codec *ac97_codec[NR_AC97];
struct cs_state *states[2];
u16 ac97_features;
int amplifier; /* Amplifier control */
void (*amplifier_ctrl)(struct cs_card *, int);
void (*amp_init)(struct cs_card *);
int active; /* Active clocking */
void (*active_ctrl)(struct cs_card *, int);
/* hardware resources */
unsigned long ba0_addr;
unsigned long ba1_addr;
u32 irq;
/* mappings */
void __iomem *ba0;
union
{
struct
{
u8 __iomem *data0;
u8 __iomem *data1;
u8 __iomem *pmem;
u8 __iomem *reg;
} name;
u8 __iomem *idx[4];
} ba1;
/* Function support */
struct cs_channel *(*alloc_pcm_channel)(struct cs_card *);
struct cs_channel *(*alloc_rec_pcm_channel)(struct cs_card *);
void (*free_pcm_channel)(struct cs_card *, int chan);
/* /dev/midi stuff */
struct {
unsigned ird, iwr, icnt;
unsigned ord, owr, ocnt;
wait_queue_head_t open_wait;
wait_queue_head_t iwait;
wait_queue_head_t owait;
spinlock_t lock;
unsigned char ibuf[CS_MIDIINBUF];
unsigned char obuf[CS_MIDIOUTBUF];
mode_t open_mode;
struct semaphore open_sem;
} midi;
struct cs46xx_pm pm;
};
static int cs_open_mixdev(struct inode *inode, struct file *file);
static int cs_release_mixdev(struct inode *inode, struct file *file);
static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg);
static int cs_hardware_init(struct cs_card *card);
static int cs46xx_powerup(struct cs_card *card, unsigned int type);
static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag);
static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type);
static int cs46xx_suspend_tbl(struct pci_dev *pcidev, pm_message_t state);
static int cs46xx_resume_tbl(struct pci_dev *pcidev);
#if CSDEBUG
/* DEBUG ROUTINES */
#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int)
#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int)
#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int)
#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int)
#define SOUND_MIXER_CS_APM _SIOWR('M',124, int)
static void printioctl(unsigned int x)
{
unsigned int i;
unsigned char vidx;
/* these values are incorrect for the ac97 driver, fix.
* Index of mixtable1[] member is Device ID
* and must be <= SOUND_MIXER_NRDEVICES.
* Value of array member is index into s->mix.vol[]
*/
static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
[SOUND_MIXER_PCM] = 1, /* voice */
[SOUND_MIXER_LINE1] = 2, /* AUX */
[SOUND_MIXER_CD] = 3, /* CD */
[SOUND_MIXER_LINE] = 4, /* Line */
[SOUND_MIXER_SYNTH] = 5, /* FM */
[SOUND_MIXER_MIC] = 6, /* Mic */
[SOUND_MIXER_SPEAKER] = 7, /* Speaker */
[SOUND_MIXER_RECLEV] = 8, /* Recording level */
[SOUND_MIXER_VOLUME] = 9 /* Master Volume */
};
switch(x)
{
case SOUND_MIXER_CS_GETDBGMASK:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGMASK: ") );
break;
case SOUND_MIXER_CS_GETDBGLEVEL:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGLEVEL: ") );
break;
case SOUND_MIXER_CS_SETDBGMASK:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGMASK: ") );
break;
case SOUND_MIXER_CS_SETDBGLEVEL:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGLEVEL: ") );
break;
case OSS_GETVERSION:
CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION: ") );
break;
case SNDCTL_DSP_SYNC:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC: ") );
break;
case SNDCTL_DSP_SETDUPLEX:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX: ") );
break;
case SNDCTL_DSP_GETCAPS:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS: ") );
break;
case SNDCTL_DSP_RESET:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET: ") );
break;
case SNDCTL_DSP_SPEED:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED: ") );
break;
case SNDCTL_DSP_STEREO:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO: ") );
break;
case SNDCTL_DSP_CHANNELS:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS: ") );
break;
case SNDCTL_DSP_GETFMTS:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS: ") );
break;
case SNDCTL_DSP_SETFMT:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT: ") );
break;
case SNDCTL_DSP_POST:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST: ") );
break;
case SNDCTL_DSP_GETTRIGGER:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER: ") );
break;
case SNDCTL_DSP_SETTRIGGER:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER: ") );
break;
case SNDCTL_DSP_GETOSPACE:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE: ") );
break;
case SNDCTL_DSP_GETISPACE:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE: ") );
break;
case SNDCTL_DSP_NONBLOCK:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK: ") );
break;
case SNDCTL_DSP_GETODELAY:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY: ") );
break;
case SNDCTL_DSP_GETIPTR:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR: ") );
break;
case SNDCTL_DSP_GETOPTR:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR: ") );
break;
case SNDCTL_DSP_GETBLKSIZE:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE: ") );
break;
case SNDCTL_DSP_SETFRAGMENT:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFRAGMENT: ") );
break;
case SNDCTL_DSP_SUBDIVIDE:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE: ") );
break;
case SOUND_PCM_READ_RATE:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE: ") );
break;
case SOUND_PCM_READ_CHANNELS:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_CHANNELS: ") );
break;
case SOUND_PCM_READ_BITS:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS: ") );
break;
case SOUND_PCM_WRITE_FILTER:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_WRITE_FILTER: ") );
break;
case SNDCTL_DSP_SETSYNCRO:
CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO: ") );
break;
case SOUND_PCM_READ_FILTER:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER: ") );
break;
case SOUND_MIXER_PRIVATE1:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1: ") );
break;
case SOUND_MIXER_PRIVATE2:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2: ") );
break;
case SOUND_MIXER_PRIVATE3:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3: ") );
break;
case SOUND_MIXER_PRIVATE4:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4: ") );
break;
case SOUND_MIXER_PRIVATE5:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5: ") );
break;
case SOUND_MIXER_INFO:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO: ") );
break;
case SOUND_OLD_MIXER_INFO:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO: ") );
break;
default:
switch (_IOC_NR(x))
{
case SOUND_MIXER_VOLUME:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_VOLUME: ") );
break;
case SOUND_MIXER_SPEAKER:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SPEAKER: ") );
break;
case SOUND_MIXER_RECLEV:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECLEV: ") );
break;
case SOUND_MIXER_MIC:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_MIC: ") );
break;
case SOUND_MIXER_SYNTH:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SYNTH: ") );
break;
case SOUND_MIXER_RECSRC:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECSRC: ") );
break;
case SOUND_MIXER_DEVMASK:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_DEVMASK: ") );
break;
case SOUND_MIXER_RECMASK:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECMASK: ") );
break;
case SOUND_MIXER_STEREODEVS:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_STEREODEVS: ") );
break;
case SOUND_MIXER_CAPS:
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:") );
break;
default:
i = _IOC_NR(x);
if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
{
CS_DBGOUT(CS_IOCTL, 4, printk("UNKNOWN IOCTL: 0x%.8x NR=%d ",x,i) );
}
else
{
CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d ",
x,i) );
}
break;
}
}
CS_DBGOUT(CS_IOCTL, 4, printk("command = 0x%x IOC_NR=%d\n",x, _IOC_NR(x)) );
}
#endif
/*
* common I/O routines
*/
static void cs461x_poke(struct cs_card *codec, unsigned long reg, unsigned int val)
{
writel(val, codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff));
}
static unsigned int cs461x_peek(struct cs_card *codec, unsigned long reg)
{
return readl(codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff));
}
static void cs461x_pokeBA0(struct cs_card *codec, unsigned long reg, unsigned int val)
{
writel(val, codec->ba0+reg);
}
static unsigned int cs461x_peekBA0(struct cs_card *codec, unsigned long reg)
{
return readl(codec->ba0+reg);
}
static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg);
static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
static struct cs_channel *cs_alloc_pcm_channel(struct cs_card *card)
{
if(card->channel[1].used==1)
return NULL;
card->channel[1].used=1;
card->channel[1].num=1;
return &card->channel[1];
}
static struct cs_channel *cs_alloc_rec_pcm_channel(struct cs_card *card)
{
if(card->channel[0].used==1)
return NULL;
card->channel[0].used=1;
card->channel[0].num=0;
return &card->channel[0];
}
static void cs_free_pcm_channel(struct cs_card *card, int channel)
{
card->channel[channel].state = NULL;
card->channel[channel].used=0;
}
/*
* setup a divisor value to help with conversion from
* 16bit Stereo, down to 8bit stereo/mono or 16bit mono.
* assign a divisor of 1 if using 16bit Stereo as that is
* the only format that the static image will capture.
*/
static void cs_set_divisor(struct dmabuf *dmabuf)
{
if(dmabuf->type == CS_TYPE_DAC)
dmabuf->divisor = 1;
else if( !(dmabuf->fmt & CS_FMT_STEREO) &&
(dmabuf->fmt & CS_FMT_16BIT))
dmabuf->divisor = 2;
else if( (dmabuf->fmt & CS_FMT_STEREO) &&
!(dmabuf->fmt & CS_FMT_16BIT))
dmabuf->divisor = 2;
else if( !(dmabuf->fmt & CS_FMT_STEREO) &&
!(dmabuf->fmt & CS_FMT_16BIT))
dmabuf->divisor = 4;
else
dmabuf->divisor = 1;
CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, printk(
"cs46xx: cs_set_divisor()- %s %d\n",
(dmabuf->type == CS_TYPE_ADC) ? "ADC" : "DAC",
dmabuf->divisor) );
}
/*
* mute some of the more prevalent registers to avoid popping.
*/
static void cs_mute(struct cs_card *card, int state)
{
struct ac97_codec *dev=card->ac97_codec[0];
CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()+ %s\n",
(state == CS_TRUE) ? "Muting" : "UnMuting") );
if(state == CS_TRUE)
{
/*
* fix pops when powering up on thinkpads
*/
card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev,
(u8)BA0_AC97_MASTER_VOLUME);
card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev,
(u8)BA0_AC97_HEADPHONE_VOLUME);
card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev,
(u8)BA0_AC97_MASTER_VOLUME_MONO);
card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev,
(u8)BA0_AC97_PCM_OUT_VOLUME);
cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000);
cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000);
cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000);
cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000);
}
else
{
cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, card->pm.u32AC97_master_volume);
cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, card->pm.u32AC97_headphone_volume);
cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, card->pm.u32AC97_master_volume_mono);
cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, card->pm.u32AC97_pcm_out_volume);
}
CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()-\n"));
}
/* set playback sample rate */
static unsigned int cs_set_dac_rate(struct cs_state * state, unsigned int rate)
{
struct dmabuf *dmabuf = &state->dmabuf;
unsigned int tmp1, tmp2;
unsigned int phiIncr;
unsigned int correctionPerGOF, correctionPerSec;
unsigned long flags;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()+ %d\n",rate) );
/*
* Compute the values used to drive the actual sample rate conversion.
* The following formulas are being computed, using inline assembly
* since we need to use 64 bit arithmetic to compute the values:
*
* phiIncr = floor((Fs,in * 2^26) / Fs,out)
* correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
* GOF_PER_SEC)
* ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
* GOF_PER_SEC * correctionPerGOF
*
* i.e.
*
* phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
* correctionPerGOF:correctionPerSec =
* dividend:remainder(ulOther / GOF_PER_SEC)
*/
tmp1 = rate << 16;
phiIncr = tmp1 / 48000;
tmp1 -= phiIncr * 48000;
tmp1 <<= 10;
phiIncr <<= 10;
tmp2 = tmp1 / 48000;
phiIncr += tmp2;
tmp1 -= tmp2 * 48000;
correctionPerGOF = tmp1 / GOF_PER_SEC;
tmp1 -= correctionPerGOF * GOF_PER_SEC;
correctionPerSec = tmp1;
/*
* Fill in the SampleRateConverter control block.
*/
spin_lock_irqsave(&state->card->lock, flags);
cs461x_poke(state->card, BA1_PSRC,
((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
cs461x_poke(state->card, BA1_PPI, phiIncr);
spin_unlock_irqrestore(&state->card->lock, flags);
dmabuf->rate = rate;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()- %d\n",rate) );
return rate;
}
/* set recording sample rate */
static unsigned int cs_set_adc_rate(struct cs_state * state, unsigned int rate)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct cs_card *card = state->card;
unsigned int phiIncr, coeffIncr, tmp1, tmp2;
unsigned int correctionPerGOF, correctionPerSec, initialDelay;
unsigned int frameGroupLength, cnt;
unsigned long flags;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()+ %d\n",rate) );
/*
* We can only decimate by up to a factor of 1/9th the hardware rate.
* Correct the value if an attempt is made to stray outside that limit.
*/
if ((rate * 9) < 48000)
rate = 48000 / 9;
/*
* We can not capture at at rate greater than the Input Rate (48000).
* Return an error if an attempt is made to stray outside that limit.
*/
if (rate > 48000)
rate = 48000;
/*
* Compute the values used to drive the actual sample rate conversion.
* The following formulas are being computed, using inline assembly
* since we need to use 64 bit arithmetic to compute the values:
*
* coeffIncr = -floor((Fs,out * 2^23) / Fs,in)
* phiIncr = floor((Fs,in * 2^26) / Fs,out)
* correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
* GOF_PER_SEC)
* correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
* GOF_PER_SEC * correctionPerGOF
* initialDelay = ceil((24 * Fs,in) / Fs,out)
*
* i.e.
*
* coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
* phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
* correctionPerGOF:correctionPerSec =
* dividend:remainder(ulOther / GOF_PER_SEC)
* initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
*/
tmp1 = rate << 16;
coeffIncr = tmp1 / 48000;
tmp1 -= coeffIncr * 48000;
tmp1 <<= 7;
coeffIncr <<= 7;
coeffIncr += tmp1 / 48000;
coeffIncr ^= 0xFFFFFFFF;
coeffIncr++;
tmp1 = 48000 << 16;
phiIncr = tmp1 / rate;
tmp1 -= phiIncr * rate;
tmp1 <<= 10;
phiIncr <<= 10;
tmp2 = tmp1 / rate;
phiIncr += tmp2;
tmp1 -= tmp2 * rate;
correctionPerGOF = tmp1 / GOF_PER_SEC;
tmp1 -= correctionPerGOF * GOF_PER_SEC;
correctionPerSec = tmp1;
initialDelay = ((48000 * 24) + rate - 1) / rate;
/*
* Fill in the VariDecimate control block.
*/
spin_lock_irqsave(&card->lock, flags);
cs461x_poke(card, BA1_CSRC,
((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
cs461x_poke(card, BA1_CCI, coeffIncr);
cs461x_poke(card, BA1_CD,
(((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
cs461x_poke(card, BA1_CPI, phiIncr);
spin_unlock_irqrestore(&card->lock, flags);
/*
* Figure out the frame group length for the write back task. Basically,
* this is just the factors of 24000 (2^6*3*5^3) that are not present in
* the output sample rate.
*/
frameGroupLength = 1;
for (cnt = 2; cnt <= 64; cnt *= 2) {
if (((rate / cnt) * cnt) != rate)
frameGroupLength *= 2;
}
if (((rate / 3) * 3) != rate) {
frameGroupLength *= 3;
}
for (cnt = 5; cnt <= 125; cnt *= 5) {
if (((rate / cnt) * cnt) != rate)
frameGroupLength *= 5;
}
/*
* Fill in the WriteBack control block.
*/
spin_lock_irqsave(&card->lock, flags);
cs461x_poke(card, BA1_CFG1, frameGroupLength);
cs461x_poke(card, BA1_CFG2, (0x00800000 | frameGroupLength));
cs461x_poke(card, BA1_CCST, 0x0000FFFF);
cs461x_poke(card, BA1_CSPB, ((65536 * rate) / 24000));
cs461x_poke(card, (BA1_CSPB + 4), 0x0000FFFF);
spin_unlock_irqrestore(&card->lock, flags);
dmabuf->rate = rate;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()- %d\n",rate) );
return rate;
}
/* prepare channel attributes for playback */
static void cs_play_setup(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct cs_card *card = state->card;
unsigned int tmp, Count, playFormat;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()+\n") );
cs461x_poke(card, BA1_PVOL, 0x80008000);
if(!dmabuf->SGok)
cs461x_poke(card, BA1_PBA, virt_to_bus(dmabuf->pbuf));
Count = 4;
playFormat=cs461x_peek(card, BA1_PFIE);
if ((dmabuf->fmt & CS_FMT_STEREO)) {
playFormat &= ~DMA_RQ_C2_AC_MONO_TO_STEREO;
Count *= 2;
}
else
playFormat |= DMA_RQ_C2_AC_MONO_TO_STEREO;
if ((dmabuf->fmt & CS_FMT_16BIT)) {
playFormat &= ~(DMA_RQ_C2_AC_8_TO_16_BIT
| DMA_RQ_C2_AC_SIGNED_CONVERT);
Count *= 2;
}
else
playFormat |= (DMA_RQ_C2_AC_8_TO_16_BIT
| DMA_RQ_C2_AC_SIGNED_CONVERT);
cs461x_poke(card, BA1_PFIE, playFormat);
tmp = cs461x_peek(card, BA1_PDTC);
tmp &= 0xfffffe00;
cs461x_poke(card, BA1_PDTC, tmp | --Count);
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()-\n") );
}
static struct InitStruct
{
u32 off;
u32 val;
} InitArray[] = { {0x00000040, 0x3fc0000f},
{0x0000004c, 0x04800000},
{0x000000b3, 0x00000780},
{0x000000b7, 0x00000000},
{0x000000bc, 0x07800000},
{0x000000cd, 0x00800000},
};
/*
* "SetCaptureSPValues()" -- Initialize record task values before each
* capture startup.
*/
static void SetCaptureSPValues(struct cs_card *card)
{
unsigned i, offset;
CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()+\n") );
for(i=0; i<sizeof(InitArray)/sizeof(struct InitStruct); i++)
{
offset = InitArray[i].off*4; /* 8bit to 32bit offset value */
cs461x_poke(card, offset, InitArray[i].val );
}
CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()-\n") );
}
/* prepare channel attributes for recording */
static void cs_rec_setup(struct cs_state *state)
{
struct cs_card *card = state->card;
struct dmabuf *dmabuf = &state->dmabuf;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()+\n") );
SetCaptureSPValues(card);
/*
* set the attenuation to 0dB
*/
cs461x_poke(card, BA1_CVOL, 0x80008000);
/*
* set the physical address of the capture buffer into the SP
*/
cs461x_poke(card, BA1_CBA, virt_to_bus(dmabuf->rawbuf));
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()-\n") );
}
/* get current playback/recording dma buffer pointer (byte offset from LBA),
called with spinlock held! */
static inline unsigned cs_get_dma_addr(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
u32 offset;
if ( (!(dmabuf->enable & DAC_RUNNING)) &&
(!(dmabuf->enable & ADC_RUNNING) ) )
{
CS_DBGOUT(CS_ERROR, 2, printk(
"cs46xx: ERROR cs_get_dma_addr(): not enabled \n") );
return 0;
}
/*
* granularity is byte boundary, good part.
*/
if(dmabuf->enable & DAC_RUNNING)
{
offset = cs461x_peek(state->card, BA1_PBA);
}
else /* ADC_RUNNING must be set */
{
offset = cs461x_peek(state->card, BA1_CBA);
}
CS_DBGOUT(CS_PARMS | CS_FUNCTION, 9,
printk("cs46xx: cs_get_dma_addr() %d\n",offset) );
offset = (u32)bus_to_virt((unsigned long)offset) - (u32)dmabuf->rawbuf;
CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8,
printk("cs46xx: cs_get_dma_addr()- %d\n",offset) );
return offset;
}
static void resync_dma_ptrs(struct cs_state *state)
{
struct dmabuf *dmabuf;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()+ \n") );
if(state)
{
dmabuf = &state->dmabuf;
dmabuf->hwptr=dmabuf->swptr = 0;
dmabuf->pringbuf = 0;
}
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()- \n") );
}
/* Stop recording (lock held) */
static inline void __stop_adc(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct cs_card *card = state->card;
unsigned int tmp;
dmabuf->enable &= ~ADC_RUNNING;
tmp = cs461x_peek(card, BA1_CCTL);
tmp &= 0xFFFF0000;
cs461x_poke(card, BA1_CCTL, tmp );
}
static void stop_adc(struct cs_state *state)
{
unsigned long flags;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()+ \n") );
spin_lock_irqsave(&state->card->lock, flags);
__stop_adc(state);
spin_unlock_irqrestore(&state->card->lock, flags);
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()- \n") );
}
static void start_adc(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct cs_card *card = state->card;
unsigned long flags;
unsigned int tmp;
spin_lock_irqsave(&card->lock, flags);
if (!(dmabuf->enable & ADC_RUNNING) &&
((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize)
&& dmabuf->ready) &&
((card->pm.flags & CS46XX_PM_IDLE) ||
(card->pm.flags & CS46XX_PM_RESUMED)) )
{
dmabuf->enable |= ADC_RUNNING;
cs_set_divisor(dmabuf);
tmp = cs461x_peek(card, BA1_CCTL);
tmp &= 0xFFFF0000;
tmp |= card->cctl;
CS_DBGOUT(CS_FUNCTION, 2, printk(
"cs46xx: start_adc() poke 0x%x \n",tmp) );
cs461x_poke(card, BA1_CCTL, tmp);
}
spin_unlock_irqrestore(&card->lock, flags);
}
/* stop playback (lock held) */
static inline void __stop_dac(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct cs_card *card = state->card;
unsigned int tmp;
dmabuf->enable &= ~DAC_RUNNING;
tmp=cs461x_peek(card, BA1_PCTL);
tmp&=0xFFFF;
cs461x_poke(card, BA1_PCTL, tmp);
}
static void stop_dac(struct cs_state *state)
{
unsigned long flags;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()+ \n") );
spin_lock_irqsave(&state->card->lock, flags);
__stop_dac(state);
spin_unlock_irqrestore(&state->card->lock, flags);
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()- \n") );
}
static void start_dac(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct cs_card *card = state->card;
unsigned long flags;
int tmp;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()+ \n") );
spin_lock_irqsave(&card->lock, flags);
if (!(dmabuf->enable & DAC_RUNNING) &&
((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) &&
((card->pm.flags & CS46XX_PM_IDLE) ||
(card->pm.flags & CS46XX_PM_RESUMED)) )
{
dmabuf->enable |= DAC_RUNNING;
tmp = cs461x_peek(card, BA1_PCTL);
tmp &= 0xFFFF;
tmp |= card->pctl;
CS_DBGOUT(CS_PARMS, 6, printk(
"cs46xx: start_dac() poke card=%p tmp=0x%.08x addr=%p \n",
card, (unsigned)tmp,
card->ba1.idx[(BA1_PCTL >> 16) & 3]+(BA1_PCTL&0xffff) ) );
cs461x_poke(card, BA1_PCTL, tmp);
}
spin_unlock_irqrestore(&card->lock, flags);
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()- \n") );
}
#define DMABUF_MINORDER 1
/*
* allocate DMA buffer, playback and recording buffers are separate.
*/
static int alloc_dmabuf(struct cs_state *state)
{
struct cs_card *card=state->card;
struct dmabuf *dmabuf = &state->dmabuf;
void *rawbuf = NULL;
void *tmpbuff = NULL;
int order;
struct page *map, *mapend;
unsigned long df;
dmabuf->ready = dmabuf->mapped = 0;
dmabuf->SGok = 0;
/*
* check for order within limits, but do not overwrite value.
*/
if((defaultorder > 1) && (defaultorder < 12))
df = defaultorder;
else
df = 2;
for (order = df; order >= DMABUF_MINORDER; order--)
if ( (rawbuf = (void *) pci_alloc_consistent(
card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr)))
break;
if (!rawbuf) {
CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
"cs46xx: alloc_dmabuf(): unable to allocate rawbuf\n"));
return -ENOMEM;
}
dmabuf->buforder = order;
dmabuf->rawbuf = rawbuf;
// Now mark the pages as reserved; otherwise the
// remap_pfn_range() in cs46xx_mmap doesn't work.
// 1. get index to last page in mem_map array for rawbuf.
mapend = virt_to_page(dmabuf->rawbuf +
(PAGE_SIZE << dmabuf->buforder) - 1);
// 2. mark each physical page in range as 'reserved'.
for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)
cs4x_mem_map_reserve(map);
CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: alloc_dmabuf(): allocated %ld (order = %d) bytes at %p\n",
PAGE_SIZE << order, order, rawbuf) );
/*
* only allocate the conversion buffer for the ADC
*/
if(dmabuf->type == CS_TYPE_DAC)
{
dmabuf->tmpbuff = NULL;
dmabuf->buforder_tmpbuff = 0;
return 0;
}
/*
* now the temp buffer for 16/8 conversions
*/
tmpbuff = (void *) pci_alloc_consistent(
card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr_tmpbuff);
if (!tmpbuff)
return -ENOMEM;
CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: allocated %ld (order = %d) bytes at %p\n",
PAGE_SIZE << order, order, tmpbuff) );
dmabuf->tmpbuff = tmpbuff;
dmabuf->buforder_tmpbuff = order;
// Now mark the pages as reserved; otherwise the
// remap_pfn_range() in cs46xx_mmap doesn't work.
// 1. get index to last page in mem_map array for rawbuf.
mapend = virt_to_page(dmabuf->tmpbuff +
(PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1);
// 2. mark each physical page in range as 'reserved'.
for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++)
cs4x_mem_map_reserve(map);
return 0;
}
/* free DMA buffer */
static void dealloc_dmabuf(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct page *map, *mapend;
if (dmabuf->rawbuf) {
// Undo prog_dmabuf()'s marking the pages as reserved
mapend = virt_to_page(dmabuf->rawbuf +
(PAGE_SIZE << dmabuf->buforder) - 1);
for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)
cs4x_mem_map_unreserve(map);
free_dmabuf(state->card, dmabuf);
}
if (dmabuf->tmpbuff) {
// Undo prog_dmabuf()'s marking the pages as reserved
mapend = virt_to_page(dmabuf->tmpbuff +
(PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1);
for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++)
cs4x_mem_map_unreserve(map);
free_dmabuf2(state->card, dmabuf);
}
dmabuf->rawbuf = NULL;
dmabuf->tmpbuff = NULL;
dmabuf->mapped = dmabuf->ready = 0;
dmabuf->SGok = 0;
}
static int __prog_dmabuf(struct cs_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
unsigned long allocated_pages, allocated_bytes;
unsigned long tmp1, tmp2, fmt=0;
unsigned long *ptmp = (unsigned long *) dmabuf->pbuf;
unsigned long SGarray[9], nSGpages=0;
int ret;
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()+ \n"));
/*
* check for CAPTURE and use only non-sg for initial release
*/
if(dmabuf->type == CS_TYPE_ADC)
{
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() ADC\n"));
/*
* add in non-sg support for capture.
*/
spin_lock_irqsave(&state->card->lock, flags);
/* add code to reset the rawbuf memory. TRW */
resync_dma_ptrs(state);
dmabuf->total_bytes = dmabuf->blocks = 0;
dmabuf->count = dmabuf->error = dmabuf->underrun = 0;
dmabuf->SGok = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
/* allocate DMA buffer if not allocated yet */
if (!dmabuf->rawbuf || !dmabuf->tmpbuff)
if ((ret = alloc_dmabuf(state)))
return ret;
/*
* static image only supports 16Bit signed, stereo - hard code fmt
*/
fmt = CS_FMT_16BIT | CS_FMT_STEREO;
dmabuf->numfrag = 2;
dmabuf->fragsize = 2048;
dmabuf->fragsamples = 2048 >> sample_shift[fmt];
dmabuf->dmasize = 4096;
dmabuf->fragshift = 11;
memset(dmabuf->rawbuf, (fmt & CS_FMT_16BIT) ? 0 : 0x80,
dmabuf->dmasize);
memset(dmabuf->tmpbuff, (fmt & CS_FMT_16BIT) ? 0 : 0x80,
PAGE_SIZE<<dmabuf->buforder_tmpbuff);
/*
* Now set up the ring
*/
spin_lock_irqsave(&state->card->lock, flags);
cs_rec_setup(state);
spin_unlock_irqrestore(&state->card->lock, flags);
/* set the ready flag for the dma buffer */
dmabuf->ready = 1;
CS_DBGOUT(CS_PARMS, 4, printk(
"cs46xx: prog_dmabuf(): CAPTURE rate=%d fmt=0x%x numfrag=%d "
"fragsize=%d dmasize=%d\n",
dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
dmabuf->fragsize, dmabuf->dmasize) );
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- 0 \n"));
return 0;
}
else if (dmabuf->type == CS_TYPE_DAC)
{
/*
* Must be DAC
*/
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() DAC\n"));
spin_lock_irqsave(&state->card->lock, flags);
resync_dma_ptrs(state);
dmabuf->total_bytes = dmabuf->blocks = 0;
dmabuf->count = dmabuf->error = dmabuf->underrun = 0;
dmabuf->SGok = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
/* allocate DMA buffer if not allocated yet */
if (!dmabuf->rawbuf)
if ((ret = alloc_dmabuf(state)))
return ret;
allocated_pages = 1 << dmabuf->buforder;
allocated_bytes = allocated_pages*PAGE_SIZE;
if(allocated_pages < 2)
{
CS_DBGOUT(CS_FUNCTION, 4, printk(
"cs46xx: prog_dmabuf() Error: allocated_pages too small (%d)\n",
(unsigned)allocated_pages));
return -ENOMEM;
}
/* Use all the pages allocated, fragsize 4k. */
/* Use 'pbuf' for S/G page map table. */
dmabuf->SGok = 1; /* Use S/G. */
nSGpages = allocated_bytes/4096; /* S/G pages always 4k. */
/* Set up S/G variables. */
*ptmp = virt_to_bus(dmabuf->rawbuf);
*(ptmp+1) = 0x00000008;
for(tmp1= 1; tmp1 < nSGpages; tmp1++) {
*(ptmp+2*tmp1) = virt_to_bus( (dmabuf->rawbuf)+4096*tmp1);
if( tmp1 == nSGpages-1)
tmp2 = 0xbfff0000;
else
tmp2 = 0x80000000+8*(tmp1+1);
*(ptmp+2*tmp1+1) = tmp2;
}
SGarray[0] = 0x82c0200d;
SGarray[1] = 0xffff0000;
SGarray[2] = *ptmp;
SGarray[3] = 0x00010600;
SGarray[4] = *(ptmp+2);
SGarray[5] = 0x80000010;
SGarray[6] = *ptmp;
SGarray[7] = *(ptmp+2);
SGarray[8] = (virt_to_bus(dmabuf->pbuf) & 0xffff000) | 0x10;
if (dmabuf->SGok) {
dmabuf->numfrag = nSGpages;
dmabuf->fragsize = 4096;
dmabuf->fragsamples = 4096 >> sample_shift[dmabuf->fmt];
dmabuf->fragshift = 12;
dmabuf->dmasize = dmabuf->numfrag*4096;
}
else {
SGarray[0] = 0xf2c0000f;
SGarray[1] = 0x00000200;
SGarray[2] = 0;
SGarray[3] = 0x00010600;
SGarray[4]=SGarray[5]=SGarray[6]=SGarray[7]=SGarray[8] = 0;
dmabuf->numfrag = 2;
dmabuf->fragsize = 2048;
dmabuf->fragsamples = 2048 >> sample_shift[dmabuf->fmt];
dmabuf->dmasize = 4096;
dmabuf->fragshift = 11;
}
for(tmp1 = 0; tmp1 < sizeof(SGarray)/4; tmp1++)
cs461x_poke( state->card, BA1_PDTC+tmp1*4, SGarray[tmp1]);
memset(dmabuf->rawbuf, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80,
dmabuf->dmasize);
/*
* Now set up the ring
*/
spin_lock_irqsave(&state->card->lock, flags);
cs_play_setup(state);
spin_unlock_irqrestore(&state->card->lock, flags);
/* set the ready flag for the dma buffer */
dmabuf->ready = 1;
CS_DBGOUT(CS_PARMS, 4, printk(
"cs46xx: prog_dmabuf(): PLAYBACK rate=%d fmt=0x%x numfrag=%d "
"fragsize=%d dmasize=%d\n",
dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
dmabuf->fragsize, dmabuf->dmasize) );
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- \n"));
return 0;
}
else
{
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- Invalid Type %d\n",
dmabuf->type));
}
return 1;
}
static int prog_dmabuf(struct cs_state *state)
{
int ret;
down(&state->sem);
ret = __prog_dmabuf(state);
up(&state->sem);
return ret;
}
static void cs_clear_tail(struct cs_state *state)
{
}
static int drain_dac(struct cs_state *state, int nonblock)
{
DECLARE_WAITQUEUE(wait, current);
struct dmabuf *dmabuf = &state->dmabuf;
struct cs_card *card=state->card;
unsigned long flags;
unsigned long tmo;
int count;
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()+ \n"));
if (dmabuf->mapped || !dmabuf->ready)
{
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0, not ready\n"));
return 0;
}
add_wait_queue(&dmabuf->wait, &wait);
for (;;) {
/* It seems that we have to set the current state to TASK_INTERRUPTIBLE
every time to make the process really go to sleep */
current->state = TASK_INTERRUPTIBLE;
spin_lock_irqsave(&state->card->lock, flags);
count = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
if (count <= 0)
break;
if (signal_pending(current))
break;
if (nonblock) {
remove_wait_queue(&dmabuf->wait, &wait);
current->state = TASK_RUNNING;
return -EBUSY;
}
tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
tmo >>= sample_shift[dmabuf->fmt];
tmo += (2048*HZ)/dmabuf->rate;
if (!schedule_timeout(tmo ? tmo : 1) && tmo){
printk(KERN_ERR "cs46xx: drain_dac, dma timeout? %d\n", count);
break;
}
}
remove_wait_queue(&dmabuf->wait, &wait);
current->state = TASK_RUNNING;
if (signal_pending(current))
{
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- -ERESTARTSYS\n"));
/*
* set to silence and let that clear the fifos.
*/
cs461x_clear_serial_FIFOs(card, CS_TYPE_DAC);
return -ERESTARTSYS;
}
CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0\n"));
return 0;
}
/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
static void cs_update_ptr(struct cs_card *card, int wake)
{
struct cs_state *state;
struct dmabuf *dmabuf;
unsigned hwptr;
int diff;
/* error handling and process wake up for ADC */
state = card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
if (dmabuf->enable & ADC_RUNNING) {
/* update hardware pointer */
hwptr = cs_get_dma_addr(state);
diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
CS_DBGOUT(CS_PARMS, 9, printk(
"cs46xx: cs_update_ptr()+ ADC hwptr=%d diff=%d\n",
hwptr,diff) );
dmabuf->hwptr = hwptr;
dmabuf->total_bytes += diff;
dmabuf->count += diff;
if (dmabuf->count > dmabuf->dmasize)
dmabuf->count = dmabuf->dmasize;
if(dmabuf->mapped)
{
if (wake && dmabuf->count >= (signed)dmabuf->fragsize)
wake_up(&dmabuf->wait);
} else
{
if (wake && dmabuf->count > 0)
wake_up(&dmabuf->wait);
}
}
}
/*
* Now the DAC
*/
state = card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
/* error handling and process wake up for DAC */
if (dmabuf->enable & DAC_RUNNING) {
/* update hardware pointer */
hwptr = cs_get_dma_addr(state);
diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
CS_DBGOUT(CS_PARMS, 9, printk(
"cs46xx: cs_update_ptr()+ DAC hwptr=%d diff=%d\n",
hwptr,diff) );
dmabuf->hwptr = hwptr;
dmabuf->total_bytes += diff;
if (dmabuf->mapped) {
dmabuf->count += diff;
if (wake && dmabuf->count >= (signed)dmabuf->fragsize)
wake_up(&dmabuf->wait);
/*
* other drivers use fragsize, but don't see any sense
* in that, since dmasize is the buffer asked for
* via mmap.
*/
if( dmabuf->count > dmabuf->dmasize)
dmabuf->count &= dmabuf->dmasize-1;
} else {
dmabuf->count -= diff;
/*
* backfill with silence and clear out the last
* "diff" number of bytes.
*/
if(hwptr >= diff)
{
memset(dmabuf->rawbuf + hwptr - diff,
(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, diff);
}
else
{
memset(dmabuf->rawbuf,
(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80,
(unsigned)hwptr);
memset((char *)dmabuf->rawbuf +
dmabuf->dmasize + hwptr - diff,
(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80,
diff - hwptr);
}
if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
"cs46xx: ERROR DAC count<0 or count > dmasize (%d)\n",
dmabuf->count));
/*
* buffer underrun or buffer overrun, reset the
* count of bytes written back to 0.
*/
if(dmabuf->count < 0)
dmabuf->underrun=1;
dmabuf->count = 0;
dmabuf->error++;
}
if (wake && dmabuf->count < (signed)dmabuf->dmasize/2)
wake_up(&dmabuf->wait);
}
}
}
}
/* hold spinlock for the following! */
static void cs_handle_midi(struct cs_card *card)
{
unsigned char ch;
int wake;
unsigned temp1;
wake = 0;
while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_RBE)) {
ch = cs461x_peekBA0(card, BA0_MIDRP);
if (card->midi.icnt < CS_MIDIINBUF) {
card->midi.ibuf[card->midi.iwr] = ch;
card->midi.iwr = (card->midi.iwr + 1) % CS_MIDIINBUF;
card->midi.icnt++;
}
wake = 1;
}
if (wake)
wake_up(&card->midi.iwait);
wake = 0;
while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_TBF) && card->midi.ocnt > 0) {
temp1 = ( card->midi.obuf[card->midi.ord] ) & 0x000000ff;
cs461x_pokeBA0(card, BA0_MIDWP,temp1);
card->midi.ord = (card->midi.ord + 1) % CS_MIDIOUTBUF;
card->midi.ocnt--;
if (card->midi.ocnt < CS_MIDIOUTBUF-16)
wake = 1;
}
if (wake)
wake_up(&card->midi.owait);
}
static irqreturn_t cs_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct cs_card *card = (struct cs_card *)dev_id;
/* Single channel card */
struct cs_state *recstate = card->channel[0].state;
struct cs_state *playstate = card->channel[1].state;
u32 status;
CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()+ \n"));
spin_lock(&card->lock);
status = cs461x_peekBA0(card, BA0_HISR);
if ((status & 0x7fffffff) == 0)
{
cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV);
spin_unlock(&card->lock);
return IRQ_HANDLED; /* Might be IRQ_NONE.. */
}
/*
* check for playback or capture interrupt only
*/
if( ((status & HISR_VC0) && playstate && playstate->dmabuf.ready) ||
(((status & HISR_VC1) && recstate && recstate->dmabuf.ready)) )
{
CS_DBGOUT(CS_INTERRUPT, 8, printk(
"cs46xx: cs_interrupt() interrupt bit(s) set (0x%x)\n",status));
cs_update_ptr(card, CS_TRUE);
}
if( status & HISR_MIDI )
cs_handle_midi(card);
/* clear 'em */
cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV);
spin_unlock(&card->lock);
CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()- \n"));
return IRQ_HANDLED;
}
/**********************************************************************/
static ssize_t cs_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct cs_card *card = (struct cs_card *)file->private_data;
ssize_t ret;
unsigned long flags;
unsigned ptr;
int cnt;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
ret = 0;
while (count > 0) {
spin_lock_irqsave(&card->lock, flags);
ptr = card->midi.ird;
cnt = CS_MIDIINBUF - ptr;
if (card->midi.icnt < cnt)
cnt = card->midi.icnt;
spin_unlock_irqrestore(&card->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (file->f_flags & O_NONBLOCK)
return ret ? ret : -EAGAIN;
interruptible_sleep_on(&card->midi.iwait);
if (signal_pending(current))
return ret ? ret : -ERESTARTSYS;
continue;
}
if (copy_to_user(buffer, card->midi.ibuf + ptr, cnt))
return ret ? ret : -EFAULT;
ptr = (ptr + cnt) % CS_MIDIINBUF;
spin_lock_irqsave(&card->lock, flags);
card->midi.ird = ptr;
card->midi.icnt -= cnt;
spin_unlock_irqrestore(&card->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
}
return ret;
}
static ssize_t cs_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct cs_card *card = (struct cs_card *)file->private_data;
ssize_t ret;
unsigned long flags;
unsigned ptr;
int cnt;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
ret = 0;
while (count > 0) {
spin_lock_irqsave(&card->lock, flags);
ptr = card->midi.owr;
cnt = CS_MIDIOUTBUF - ptr;
if (card->midi.ocnt + cnt > CS_MIDIOUTBUF)
cnt = CS_MIDIOUTBUF - card->midi.ocnt;
if (cnt <= 0)
cs_handle_midi(card);
spin_unlock_irqrestore(&card->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (file->f_flags & O_NONBLOCK)
return ret ? ret : -EAGAIN;
interruptible_sleep_on(&card->midi.owait);
if (signal_pending(current))
return ret ? ret : -ERESTARTSYS;
continue;
}
if (copy_from_user(card->midi.obuf + ptr, buffer, cnt))
return ret ? ret : -EFAULT;
ptr = (ptr + cnt) % CS_MIDIOUTBUF;
spin_lock_irqsave(&card->lock, flags);
card->midi.owr = ptr;
card->midi.ocnt += cnt;
spin_unlock_irqrestore(&card->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
spin_lock_irqsave(&card->lock, flags);
cs_handle_midi(card);
spin_unlock_irqrestore(&card->lock, flags);
}
return ret;
}
static unsigned int cs_midi_poll(struct file *file, struct poll_table_struct *wait)
{
struct cs_card *card = (struct cs_card *)file->private_data;
unsigned long flags;
unsigned int mask = 0;
if (file->f_flags & FMODE_WRITE)
poll_wait(file, &card->midi.owait, wait);
if (file->f_flags & FMODE_READ)
poll_wait(file, &card->midi.iwait, wait);
spin_lock_irqsave(&card->lock, flags);
if (file->f_flags & FMODE_READ) {
if (card->midi.icnt > 0)
mask |= POLLIN | POLLRDNORM;
}
if (file->f_flags & FMODE_WRITE) {
if (card->midi.ocnt < CS_MIDIOUTBUF)
mask |= POLLOUT | POLLWRNORM;
}
spin_unlock_irqrestore(&card->lock, flags);
return mask;
}
static int cs_midi_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct cs_card *card=NULL;
unsigned long flags;
struct list_head *entry;
list_for_each(entry, &cs46xx_devs)
{
card = list_entry(entry, struct cs_card, list);
if (card->dev_midi == minor)
break;
}
if (entry == &cs46xx_devs)
return -ENODEV;
if (!card)
{
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
"cs46xx: cs46xx_midi_open(): Error - unable to find card struct\n"));
return -ENODEV;
}
file->private_data = card;
/* wait for device to become free */
down(&card->midi.open_sem);
while (card->midi.open_mode & file->f_mode) {
if (file->f_flags & O_NONBLOCK) {
up(&card->midi.open_sem);
return -EBUSY;
}
up(&card->midi.open_sem);
interruptible_sleep_on(&card->midi.open_wait);
if (signal_pending(current))
return -ERESTARTSYS;
down(&card->midi.open_sem);
}
spin_lock_irqsave(&card->midi.lock, flags);
if (!(card->midi.open_mode & (FMODE_READ | FMODE_WRITE))) {
card->midi.ird = card->midi.iwr = card->midi.icnt = 0;
card->midi.ord = card->midi.owr = card->midi.ocnt = 0;
card->midi.ird = card->midi.iwr = card->midi.icnt = 0;
cs461x_pokeBA0(card, BA0_MIDCR, 0x0000000f); /* Enable xmit, rcv. */
cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); /* Enable interrupts */
}
if (file->f_mode & FMODE_READ) {
card->midi.ird = card->midi.iwr = card->midi.icnt = 0;
}
if (file->f_mode & FMODE_WRITE) {
card->midi.ord = card->midi.owr = card->midi.ocnt = 0;
}
spin_unlock_irqrestore(&card->midi.lock, flags);
card->midi.open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE));
up(&card->midi.open_sem);
return 0;
}
static int cs_midi_release(struct inode *inode, struct file *file)
{
struct cs_card *card = (struct cs_card *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
unsigned count, tmo;
if (file->f_mode & FMODE_WRITE) {
current->state = TASK_INTERRUPTIBLE;
add_wait_queue(&card->midi.owait, &wait);
for (;;) {
spin_lock_irqsave(&card->midi.lock, flags);
count = card->midi.ocnt;
spin_unlock_irqrestore(&card->midi.lock, flags);
if (count <= 0)
break;
if (signal_pending(current))
break;
if (file->f_flags & O_NONBLOCK)
break;
tmo = (count * HZ) / 3100;
if (!schedule_timeout(tmo ? : 1) && tmo)
printk(KERN_DEBUG "cs46xx: midi timed out??\n");
}
remove_wait_queue(&card->midi.owait, &wait);
current->state = TASK_RUNNING;
}
down(&card->midi.open_sem);
card->midi.open_mode &= (~(file->f_mode & (FMODE_READ | FMODE_WRITE)));
up(&card->midi.open_sem);
wake_up(&card->midi.open_wait);
return 0;
}
/*
* Midi file operations struct.
*/
static /*const*/ struct file_operations cs_midi_fops = {
CS_OWNER CS_THIS_MODULE
.llseek = no_llseek,
.read = cs_midi_read,
.write = cs_midi_write,
.poll = cs_midi_poll,
.open = cs_midi_open,
.release = cs_midi_release,
};
/*
*
* CopySamples copies 16-bit stereo signed samples from the source to the
* destination, possibly converting down to unsigned 8-bit and/or mono.
* count specifies the number of output bytes to write.
*
* Arguments:
*
* dst - Pointer to a destination buffer.
* src - Pointer to a source buffer
* count - The number of bytes to copy into the destination buffer.
* fmt - CS_FMT_16BIT and/or CS_FMT_STEREO bits
* dmabuf - pointer to the dma buffer structure
*
* NOTES: only call this routine if the output desired is not 16 Signed Stereo
*
*
*/
static void CopySamples(char *dst, char *src, int count, unsigned fmt,
struct dmabuf *dmabuf)
{
s32 s32AudioSample;
s16 *psSrc=(s16 *)src;
s16 *psDst=(s16 *)dst;
u8 *pucDst=(u8 *)dst;
CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") );
CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
" dst=%p src=%p count=%d fmt=0x%x\n",
dst,src,count,fmt) );
/*
* See if the data should be output as 8-bit unsigned stereo.
*/
if((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT))
{
/*
* Convert each 16-bit signed stereo sample to 8-bit unsigned
* stereo using rounding.
*/
psSrc = (s16 *)src;
count = count/2;
while(count--)
{
*(pucDst++) = (u8)(((s16)(*psSrc++) + (s16)0x8000) >> 8);
}
}
/*
* See if the data should be output at 8-bit unsigned mono.
*/
else if(!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT))
{
/*
* Convert each 16-bit signed stereo sample to 8-bit unsigned
* mono using averaging and rounding.
*/
psSrc = (s16 *)src;
count = count/2;
while(count--)
{
s32AudioSample = ((*psSrc)+(*(psSrc + 1)))/2 + (s32)0x80;
if(s32AudioSample > 0x7fff)
s32AudioSample = 0x7fff;
*(pucDst++) = (u8)(((s16)s32AudioSample + (s16)0x8000) >> 8);
psSrc += 2;
}
}
/*
* See if the data should be output at 16-bit signed mono.
*/
else if(!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT))
{
/*
* Convert each 16-bit signed stereo sample to 16-bit signed
* mono using averaging.
*/
psSrc = (s16 *)src;
count = count/2;
while(count--)
{
*(psDst++) = (s16)((*psSrc)+(*(psSrc + 1)))/2;
psSrc += 2;
}
}
}
/*
* cs_copy_to_user()
* replacement for the standard copy_to_user, to allow for a conversion from
* 16 bit to 8 bit and from stereo to mono, if the record conversion is active.
* The current CS46xx/CS4280 static image only records in 16bit unsigned Stereo,
* so we convert from any of the other format combinations.
*/
static unsigned cs_copy_to_user(
struct cs_state *s,
void __user *dest,
void *hwsrc,
unsigned cnt,
unsigned *copied)
{
struct dmabuf *dmabuf = &s->dmabuf;
void *src = hwsrc; /* default to the standard destination buffer addr */
CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO
"cs_copy_to_user()+ fmt=0x%x cnt=%d dest=%p\n",
dmabuf->fmt,(unsigned)cnt,dest) );
if(cnt > dmabuf->dmasize)
{
cnt = dmabuf->dmasize;
}
if(!cnt)
{
*copied = 0;
return 0;
}
if(dmabuf->divisor != 1)
{
if(!dmabuf->tmpbuff)
{
*copied = cnt/dmabuf->divisor;
return 0;
}
CopySamples((char *)dmabuf->tmpbuff, (char *)hwsrc, cnt,
dmabuf->fmt, dmabuf);
src = dmabuf->tmpbuff;
cnt = cnt/dmabuf->divisor;
}
if (copy_to_user(dest, src, cnt))
{
CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR
"cs46xx: cs_copy_to_user()- fault dest=%p src=%p cnt=%d\n",
dest,src,cnt) );
*copied = 0;
return -EFAULT;
}
*copied = cnt;
CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO
"cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt) );
return 0;
}
/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
the user's buffer. it is filled by the dma machine and drained by this loop. */
static ssize_t cs_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct cs_card *card = (struct cs_card *) file->private_data;
struct cs_state *state;
DECLARE_WAITQUEUE(wait, current);
struct dmabuf *dmabuf;
ssize_t ret = 0;
unsigned long flags;
unsigned swptr;
int cnt;
unsigned copied=0;
CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4,
printk("cs46xx: cs_read()+ %zd\n",count) );
state = (struct cs_state *)card->states[0];
if(!state)
return -ENODEV;
dmabuf = &state->dmabuf;
if (dmabuf->mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
down(&state->sem);
if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
goto out2;
add_wait_queue(&state->dmabuf.wait, &wait);
while (count > 0) {
while(!(card->pm.flags & CS46XX_PM_IDLE))
{
schedule();
if (signal_pending(current)) {
if(!ret) ret = -ERESTARTSYS;
goto out;
}
}
spin_lock_irqsave(&state->card->lock, flags);
swptr = dmabuf->swptr;
cnt = dmabuf->dmasize - swptr;
if (dmabuf->count < cnt)
cnt = dmabuf->count;
if (cnt <= 0)
__set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&state->card->lock, flags);
if (cnt > (count * dmabuf->divisor))
cnt = count * dmabuf->divisor;
if (cnt <= 0) {
/* buffer is empty, start the dma machine and wait for data to be
recorded */
start_adc(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
goto out;
}
up(&state->sem);
schedule();
if (signal_pending(current)) {
if(!ret) ret = -ERESTARTSYS;
goto out;
}
down(&state->sem);
if (dmabuf->mapped)
{
if(!ret)
ret = -ENXIO;
goto out;
}
continue;
}
CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO
"_read() copy_to cnt=%d count=%zd ", cnt,count) );
CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
" .dmasize=%d .count=%d buffer=%p ret=%zd\n",
dmabuf->dmasize,dmabuf->count,buffer,ret) );
if (cs_copy_to_user(state, buffer,
(char *)dmabuf->rawbuf + swptr, cnt, &copied))
{
if (!ret) ret = -EFAULT;
goto out;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
spin_lock_irqsave(&card->lock, flags);
dmabuf->swptr = swptr;
dmabuf->count -= cnt;
spin_unlock_irqrestore(&card->lock, flags);
count -= copied;
buffer += copied;
ret += copied;
start_adc(state);
}
out:
remove_wait_queue(&state->dmabuf.wait, &wait);
out2:
up(&state->sem);
set_current_state(TASK_RUNNING);
CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4,
printk("cs46xx: cs_read()- %zd\n",ret) );
return ret;
}
/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
the soundcard. it is drained by the dma machine and filled by this loop. */
static ssize_t cs_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct cs_card *card = (struct cs_card *) file->private_data;
struct cs_state *state;
DECLARE_WAITQUEUE(wait, current);
struct dmabuf *dmabuf;
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4,
printk("cs46xx: cs_write called, count = %zd\n", count) );
state = (struct cs_state *)card->states[1];
if(!state)
return -ENODEV;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
dmabuf = &state->dmabuf;
down(&state->sem);
if (dmabuf->mapped)
{
ret = -ENXIO;
goto out;
}
if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
goto out;
add_wait_queue(&state->dmabuf.wait, &wait);
ret = 0;
/*
* Start the loop to read from the user's buffer and write to the dma buffer.
* check for PM events and underrun/overrun in the loop.
*/
while (count > 0) {
while(!(card->pm.flags & CS46XX_PM_IDLE))
{
schedule();
if (signal_pending(current)) {
if(!ret) ret = -ERESTARTSYS;
goto out;
}
}
spin_lock_irqsave(&state->card->lock, flags);
if (dmabuf->count < 0) {
/* buffer underrun, we are recovering from sleep_on_timeout,
resync hwptr and swptr */
dmabuf->count = 0;
dmabuf->swptr = dmabuf->hwptr;
}
if (dmabuf->underrun)
{
dmabuf->underrun = 0;
dmabuf->hwptr = cs_get_dma_addr(state);
dmabuf->swptr = dmabuf->hwptr;
}
swptr = dmabuf->swptr;
cnt = dmabuf->dmasize - swptr;
if (dmabuf->count + cnt > dmabuf->dmasize)
cnt = dmabuf->dmasize - dmabuf->count;
if (cnt <= 0)
__set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&state->card->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
/* buffer is full, start the dma machine and wait for data to be
played */
start_dac(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
goto out;
}
up(&state->sem);
schedule();
if (signal_pending(current)) {
if(!ret) ret = -ERESTARTSYS;
goto out;
}
down(&state->sem);
if (dmabuf->mapped)
{
if(!ret)
ret = -ENXIO;
goto out;
}
continue;
}
if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
if (!ret) ret = -EFAULT;
goto out;
}
spin_lock_irqsave(&state->card->lock, flags);
swptr = (swptr + cnt) % dmabuf->dmasize;
dmabuf->swptr = swptr;
dmabuf->count += cnt;
if(dmabuf->count > dmabuf->dmasize)
{
CS_DBGOUT(CS_WAVE_WRITE | CS_ERROR, 2, printk(
"cs46xx: cs_write() d->count > dmasize - resetting\n"));
dmabuf->count = dmabuf->dmasize;
}
dmabuf->endcleared = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
start_dac(state);
}
out:
up(&state->sem);
remove_wait_queue(&state->dmabuf.wait, &wait);
set_current_state(TASK_RUNNING);
CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 2,
printk("cs46xx: cs_write()- ret=%zd\n", ret) );
return ret;
}
static unsigned int cs_poll(struct file *file, struct poll_table_struct *wait)
{
struct cs_card *card = (struct cs_card *)file->private_data;
struct dmabuf *dmabuf;
struct cs_state *state;
unsigned long flags;
unsigned int mask = 0;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()+ \n"));
if (!(file->f_mode & (FMODE_WRITE | FMODE_READ)))
{
return -EINVAL;
}
if (file->f_mode & FMODE_WRITE)
{
state = card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
poll_wait(file, &dmabuf->wait, wait);
}
}
if (file->f_mode & FMODE_READ)
{
state = card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
poll_wait(file, &dmabuf->wait, wait);
}
}
spin_lock_irqsave(&card->lock, flags);
cs_update_ptr(card, CS_FALSE);
if (file->f_mode & FMODE_READ) {
state = card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLIN | POLLRDNORM;
}
}
if (file->f_mode & FMODE_WRITE) {
state = card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
if (dmabuf->mapped) {
if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
} else {
if ((signed)dmabuf->dmasize >= dmabuf->count
+ (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
}
}
}
spin_unlock_irqrestore(&card->lock, flags);
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()- (0x%x) \n",
mask));
return mask;
}
/*
* We let users mmap the ring buffer. Its not the real DMA buffer but
* that side of the code is hidden in the IRQ handling. We do a software
* emulation of DMA from a 64K or so buffer into a 2K FIFO.
* (the hardware probably deserves a moan here but Crystal send me nice
* toys ;)).
*/
static int cs_mmap(struct file *file, struct vm_area_struct *vma)
{
struct cs_card *card = (struct cs_card *)file->private_data;
struct cs_state *state;
struct dmabuf *dmabuf;
int ret = 0;
unsigned long size;
CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=%p %s %s\n",
file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "",
vma->vm_flags & VM_READ ? "VM_READ" : "") );
if (vma->vm_flags & VM_WRITE) {
state = card->states[1];
if(state)
{
CS_DBGOUT(CS_OPEN, 2, printk(
"cs46xx: cs_mmap() VM_WRITE - state TRUE prog_dmabuf DAC\n") );
if ((ret = prog_dmabuf(state)) != 0)
return ret;
}
} else if (vma->vm_flags & VM_READ) {
state = card->states[0];
if(state)
{
CS_DBGOUT(CS_OPEN, 2, printk(
"cs46xx: cs_mmap() VM_READ - state TRUE prog_dmabuf ADC\n") );
if ((ret = prog_dmabuf(state)) != 0)
return ret;
}
} else {
CS_DBGOUT(CS_ERROR, 2, printk(
"cs46xx: cs_mmap() return -EINVAL\n") );
return -EINVAL;
}
/*
* For now ONLY support playback, but seems like the only way to use
* mmap() is to open an FD with RDWR, just read or just write access
* does not function, get an error back from the kernel.
* Also, QuakeIII opens with RDWR! So, there must be something
* to needing read/write access mapping. So, allow read/write but
* use the DAC only.
*/
state = card->states[1];
if (!state) {
ret = -EINVAL;
goto out;
}
down(&state->sem);
dmabuf = &state->dmabuf;
if (cs4x_pgoff(vma) != 0)
{
ret = -EINVAL;
goto out;
}
size = vma->vm_end - vma->vm_start;
CS_DBGOUT(CS_PARMS, 2, printk("cs46xx: cs_mmap(): size=%d\n",(unsigned)size) );
if (size > (PAGE_SIZE << dmabuf->buforder))
{
ret = -EINVAL;
goto out;
}
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT,
size, vma->vm_page_prot))
{
ret = -EAGAIN;
goto out;
}
dmabuf->mapped = 1;
CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mmap()-\n") );
out:
up(&state->sem);
return ret;
}
static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct cs_card *card = (struct cs_card *)file->private_data;
struct cs_state *state;
struct dmabuf *dmabuf=NULL;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int val, valsave, mapped, ret;
void __user *argp = (void __user *)arg;
int __user *p = argp;
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
mapped = (file->f_mode & FMODE_READ) && dmabuf->mapped;
}
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
mapped |= (file->f_mode & FMODE_WRITE) && dmabuf->mapped;
}
#if CSDEBUG
printioctl(cmd);
#endif
switch (cmd)
{
case OSS_GETVERSION:
return put_user(SOUND_VERSION, p);
case SNDCTL_DSP_RESET:
/* FIXME: spin_lock ? */
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
stop_dac(state);
synchronize_irq(card->irq);
dmabuf->ready = 0;
resync_dma_ptrs(state);
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
dmabuf->blocks = 0;
dmabuf->SGok = 0;
}
}
if (file->f_mode & FMODE_READ) {
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
stop_adc(state);
synchronize_irq(card->irq);
resync_dma_ptrs(state);
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
dmabuf->blocks = 0;
dmabuf->SGok = 0;
}
}
CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_RESET()-\n") );
return 0;
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE)
return drain_dac(state, file->f_flags & O_NONBLOCK);
return 0;
case SNDCTL_DSP_SPEED: /* set sample rate */
if (get_user(val, p))
return -EFAULT;
if (val >= 0) {
if (file->f_mode & FMODE_READ) {
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
stop_adc(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
cs_set_adc_rate(state, val);
cs_set_divisor(dmabuf);
}
}
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
stop_dac(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
cs_set_dac_rate(state, val);
cs_set_divisor(dmabuf);
}
}
CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
"cs46xx: cs_ioctl() DSP_SPEED %s %s %d\n",
file->f_mode & FMODE_WRITE ? "DAC" : "",
file->f_mode & FMODE_READ ? "ADC" : "",
dmabuf->rate ) );
return put_user(dmabuf->rate, p);
}
return put_user(0, p);
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
if (get_user(val, p))
return -EFAULT;
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
stop_dac(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
if(val)
dmabuf->fmt |= CS_FMT_STEREO;
else
dmabuf->fmt &= ~CS_FMT_STEREO;
cs_set_divisor(dmabuf);
CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
"cs46xx: DSP_STEREO() DAC %s\n",
(dmabuf->fmt & CS_FMT_STEREO) ?
"STEREO":"MONO") );
}
}
if (file->f_mode & FMODE_READ) {
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
stop_adc(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
if(val)
dmabuf->fmt |= CS_FMT_STEREO;
else
dmabuf->fmt &= ~CS_FMT_STEREO;
cs_set_divisor(dmabuf);
CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
"cs46xx: DSP_STEREO() ADC %s\n",
(dmabuf->fmt & CS_FMT_STEREO) ?
"STEREO":"MONO") );
}
}
return 0;
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
if ((val = prog_dmabuf(state)))
return val;
return put_user(dmabuf->fragsize, p);
}
}
if (file->f_mode & FMODE_READ) {
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
if ((val = prog_dmabuf(state)))
return val;
return put_user(dmabuf->fragsize/dmabuf->divisor,
p);
}
}
return put_user(0, p);
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
return put_user(AFMT_S16_LE | AFMT_U8, p);
case SNDCTL_DSP_SETFMT: /* Select sample format */
if (get_user(val, p))
return -EFAULT;
CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
"cs46xx: cs_ioctl() DSP_SETFMT %s %s %s %s\n",
file->f_mode & FMODE_WRITE ? "DAC" : "",
file->f_mode & FMODE_READ ? "ADC" : "",
val == AFMT_S16_LE ? "16Bit Signed" : "",
val == AFMT_U8 ? "8Bit Unsigned" : "") );
valsave = val;
if (val != AFMT_QUERY) {
if(val==AFMT_S16_LE || val==AFMT_U8)
{
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
stop_dac(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
if(val==AFMT_S16_LE)
dmabuf->fmt |= CS_FMT_16BIT;
else
dmabuf->fmt &= ~CS_FMT_16BIT;
cs_set_divisor(dmabuf);
if((ret = prog_dmabuf(state)))
return ret;
}
}
if (file->f_mode & FMODE_READ) {
val = valsave;
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
stop_adc(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
if(val==AFMT_S16_LE)
dmabuf->fmt |= CS_FMT_16BIT;
else
dmabuf->fmt &= ~CS_FMT_16BIT;
cs_set_divisor(dmabuf);
if((ret = prog_dmabuf(state)))
return ret;
}
}
}
else
{
CS_DBGOUT(CS_IOCTL | CS_ERROR, 2, printk(
"cs46xx: DSP_SETFMT() Unsupported format (0x%x)\n",
valsave) );
}
}
else
{
if(file->f_mode & FMODE_WRITE)
{
state = (struct cs_state *)card->states[1];
if(state)
dmabuf = &state->dmabuf;
}
else if(file->f_mode & FMODE_READ)
{
state = (struct cs_state *)card->states[0];
if(state)
dmabuf = &state->dmabuf;
}
}
if(dmabuf)
{
if(dmabuf->fmt & CS_FMT_16BIT)
return put_user(AFMT_S16_LE, p);
else
return put_user(AFMT_U8, p);
}
return put_user(0, p);
case SNDCTL_DSP_CHANNELS:
if (get_user(val, p))
return -EFAULT;
if (val != 0) {
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
stop_dac(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
if(val>1)
dmabuf->fmt |= CS_FMT_STEREO;
else
dmabuf->fmt &= ~CS_FMT_STEREO;
cs_set_divisor(dmabuf);
if (prog_dmabuf(state))
return 0;
}
}
if (file->f_mode & FMODE_READ) {
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
stop_adc(state);
dmabuf->ready = 0;
dmabuf->SGok = 0;
if(val>1)
dmabuf->fmt |= CS_FMT_STEREO;
else
dmabuf->fmt &= ~CS_FMT_STEREO;
cs_set_divisor(dmabuf);
if (prog_dmabuf(state))
return 0;
}
}
}
return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1,
p);
case SNDCTL_DSP_POST:
/*
* There will be a longer than normal pause in the data.
* so... do nothing, because there is nothing that we can do.
*/
return 0;
case SNDCTL_DSP_SUBDIVIDE:
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
if (dmabuf->subdivision)
return -EINVAL;
if (get_user(val, p))
return -EFAULT;
if (val != 1 && val != 2)
return -EINVAL;
dmabuf->subdivision = val;
}
}
if (file->f_mode & FMODE_READ) {
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
if (dmabuf->subdivision)
return -EINVAL;
if (get_user(val, p))
return -EFAULT;
if (val != 1 && val != 2)
return -EINVAL;
dmabuf->subdivision = val;
}
}
return 0;
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, p))
return -EFAULT;
if (file->f_mode & FMODE_WRITE) {
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
dmabuf->ossfragshift = val & 0xffff;
dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
}
}
if (file->f_mode & FMODE_READ) {
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
dmabuf->ossfragshift = val & 0xffff;
dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
}
}
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
spin_lock_irqsave(&state->card->lock, flags);
cs_update_ptr(card, CS_TRUE);
abinfo.fragsize = dmabuf->fragsize;
abinfo.fragstotal = dmabuf->numfrag;
/*
* for mmap we always have total space available
*/
if (dmabuf->mapped)
abinfo.bytes = dmabuf->dmasize;
else
abinfo.bytes = dmabuf->dmasize - dmabuf->count;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
}
return -ENODEV;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
state = (struct cs_state *)card->states[0];
if(state)
{
dmabuf = &state->dmabuf;
spin_lock_irqsave(&state->card->lock, flags);
cs_update_ptr(card, CS_TRUE);
abinfo.fragsize = dmabuf->fragsize/dmabuf->divisor;
abinfo.bytes = dmabuf->count/dmabuf->divisor;
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
}
return -ENODEV;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETCAPS:
return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
p);
case SNDCTL_DSP_GETTRIGGER:
val = 0;
CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()+\n") );
if (file->f_mode & FMODE_WRITE)
{
state = (struct cs_state *)card->states[1];
if(state)
{
dmabuf = &state->dmabuf;
if(dmabuf->enable & DAC_RUNNING)
val |= PCM_ENABLE_INPUT;
}
}
if (file->f_mode & FMODE_READ)
{
if(state)
{
state = (struct cs_state *)card->states[0];