summaryrefslogtreecommitdiffstats
path: root/drivers/sound/trident.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sound/trident.c')
-rw-r--r--drivers/sound/trident.c3055
1 files changed, 1141 insertions, 1914 deletions
diff --git a/drivers/sound/trident.c b/drivers/sound/trident.c
index c8c0cd0c3..2f808c92c 100644
--- a/drivers/sound/trident.c
+++ b/drivers/sound/trident.c
@@ -29,16 +29,36 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.11 Jan 27 2000 Ollie Lho
+ * DMA bug, scheduler latency, second try
+ * v0.10 Jan 24 2000 Ollie Lho
+ * DMA bug fixed, found kernel scheduling problem
+ * v0.09 Jan 20 2000 Ollie Lho
+ * Clean up of channel register access routine (prepare for channel binding)
+ * v0.08 Jan 14 2000 Ollie Lho
+ * Isolation of AC97 codec code
+ * v0.07 Jan 13 2000 Ollie Lho
+ * Get rid of ugly old low level access routines (e.g. CHRegs.lp****)
+ * v0.06 Jan 11 2000 Ollie Lho
+ * Preliminary support for dual (more ?) AC97 codecs
* v0.05 Jan 08 2000 Luca Montecchiani <m.luca@iname.com>
- * adapt to 2.3.x new __setup/__initcall
+ * adapt to 2.3.x new __setup/__init call
* v0.04 Dec 31 1999 Ollie Lho
- * Multiple Open, useing Middle Loop Interrupt to smooth playback
+ * Multiple Open, using Middle Loop Interrupt to smooth playback
* v0.03 Dec 24 1999 Ollie Lho
* mem leak in prog_dmabuf and dealloc_dmabuf removed
* v0.02 Dec 15 1999 Ollie Lho
* SiS 7018 support added, playback O.K.
* v0.01 Alan Cox et. al.
* Initial Release in kernel 2.3.30, does not work
+ *
+ * ToDo
+ * Clean up of low level channel register access code. (done)
+ * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done)
+ * Dual AC97 codecs support (done partially, need channel binding to test)
+ * Recording support
+ * Mmap support
+ * "Channel Binding" ioctl extension
*/
#include <linux/config.h>
@@ -66,34 +86,40 @@
#endif
#include "trident.h"
-#include "ac97.h"
+#include "ac97_codec.h"
#undef DEBUG
-#define DRIVER_VERSION "0.05"
-
-#define TRIDENT_FMT_STEREO 0x01
-#define TRIDENT_FMT_16BIT 0x02
-#define TRIDENT_FMT_MASK 0x03
-#define TRIDENT_DAC_SHIFT 0
-#define TRIDENT_ADC_SHIFT 4
-
-#define TRIDENT_ENABLE_PE 1
-#define TRIDENT_ENABLE_RE 2
-#define DAC_RUNNING 1
-#define ADC_RUNNING 2
+#define DRIVER_VERSION "0.11"
+/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */
-/* number of instances of opening /dev/dsp, can your CPU handle this ? */
-#define NR_DSPS 32
+/* The first 32 channels are called Bank A. They are (should be) reserved
+ for MIDI synthesizer. But since that is not supported yet, we can (ab)use
+ them to play PCM samples */
+#undef ABUSE_BANK_A
+
+/* maxinum number of instances of opening /dev/dspN, can your CPU handle this ?
+ NOTE: If /dev/dsp is opened O_RDWR (i.e. full duplex) it will consume 2 HW
+ channels */
+#ifdef ABUSE_BANK_A
+#define NR_HW_CH 64
+#else
+#define NR_HW_CH 32
+#endif
+
+/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only
+ have 2 SDATA_IN lines (currently) */
+#define NR_AC97 2
+/* minor number of /dev/dspW */
#define SND_DEV_DSP16 5
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
-static const char *sample_format[] = {"8 bits Mono", "8 bits Stereo", "16 bits Mono", "16 bits Stereo"};
+
static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n";
struct pci_audio_info {
@@ -108,52 +134,12 @@ static struct pci_audio_info pci_audio_devices[] = {
{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, "SiS 7018 PCI Audio"}
};
-static struct {
- unsigned int id;
- char *name;
-} snd_ac97_codec_ids[] = {
- {0x414B4D00, "Asahi Kasei AK4540" },
- {0x41445340, "Analog Devices AD1881" },
- {0x43525900, "Cirrus Logic CS4297" },
- {0x43525913, "Cirrus Logic CS4297A" },
- {0x43525931, "Cirrus Logic CS4299" },
- {0x4e534331, "National Semiconductor LM4549"},
- {0x83847600, "SigmaTel STAC????" },
- {0x83847604, "SigmaTel STAC9701/3/4/5"},
- {0x83847605, "SigmaTel STAC9704" },
- {0x83847608, "SigmaTel STAC9708" },
- {0x83847609, "SigmaTel STAC9721/23" },
- {0x00000000, NULL}
-};
-
-typedef struct tChannelControl
-{
- // register data
- unsigned int * lpChStart;
- unsigned int * lpChStop;
- unsigned int * lpChAint;
- unsigned int * lpChAinten;
-
- // register addresses
- unsigned int * lpAChStart;
- unsigned int * lpAChStop;
- unsigned int * lpAChAint;
- unsigned int * lpAChAinten;
-
- unsigned int data[16];
-
-} CHANNELCONTROL;
-
/* "software" or virtual channel, an instance of opened /dev/dsp */
struct trident_state {
unsigned int magic;
struct trident_card *card; /* Card info */
- /* wave stuff */
- unsigned int rateadc, ratedac;
- unsigned char fmt, enable;
-
- /* single opne lock mechanism, should be removed */
+ /* single open lock mechanism, only used for recording */
struct semaphore open_sem;
wait_queue_head_t open_wait;
@@ -164,26 +150,33 @@ struct trident_state {
int virt;
struct dmabuf {
+ /* wave sample stuff */
+ unsigned int rate;
+ unsigned char fmt, enable;
+
+ /* hardware channel */
+ struct trident_channel *channel;
+
+ /* OSS buffer manangemeent stuff */
void *rawbuf;
unsigned buforder;
unsigned numfrag;
unsigned fragshift;
- /* hardware channel number */
- int chan;
+ /* our buffer acts like a circular ring */
+ unsigned hwptr; /* where dma last started, update by update_ptr */
+ unsigned swptr; /* where driver last clear/filled, updated by read/write */
+ int count; /* bytes to be comsumed by dma machine */
+ unsigned total_bytes; /* total bytes dmaed by hardware */
- /* XXX zab - swptr only in here so that it can be referenced by
- clear_advance, as far as I can tell :( */
- unsigned hwptr, swptr;
- unsigned total_bytes;
- int count;
- unsigned error; /* over/underrun */
- wait_queue_head_t wait;
+ unsigned error; /* number of over/underruns */
+ 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;
@@ -192,44 +185,45 @@ struct trident_state {
int ossmaxfrags;
unsigned subdivision;
} dma_dac, dma_adc;
-
- u8 bDMAStart;
-
};
/* hardware channels */
struct trident_channel {
- int chan; /* channel number */
- u32 lba;
- u32 eso;
+ int num; /* channel number */
+ u32 lba; /* reg 0xe4 */
+ u32 eso; /* reg 0xe8 */
u32 delta;
- u16 attribute;
-
+ u16 attribute; /* reg 0xec */
+ u16 fm_vol;
+ u32 control; /* reg 0xf0 */
};
-struct trident_pcm_bank {
- /* registers to control bank operations */
+struct trident_pcm_bank_address {
u32 start;
u32 stop;
u32 aint;
u32 aint_en;
+};
+static struct trident_pcm_bank_address bank_a_addrs =
+{
+ T4D_START_A,
+ T4D_STOP_A,
+ T4D_AINT_A,
+ T4D_AINTEN_A
+};
+static struct trident_pcm_bank_address bank_b_addrs =
+{
+ T4D_START_B,
+ T4D_STOP_B,
+ T4D_AINT_B,
+ T4D_AINTEN_B
+};
+struct trident_pcm_bank {
+ /* register addresses to control bank operations */
+ struct trident_pcm_bank_address *addresses;
/* each bank has 32 channels */
u32 bitmap; /* channel allocation bitmap */
- //struct trident_channel channels[32];
-};
-
-struct trident_mixer {
- int modcnt;
- int supported_mixers;
- int stereo_mixers;
- int record_sources;
-
- /* the caller must guarantee arg sanity before calling these */
- /* int (*read_mixer)(struct trident_card *card, int index);*/
- void (*write_mixer)(struct trident_card *card,int mixer, unsigned int left,
- unsigned int right);
- int (*recmask_io)(struct trident_card *card,int rw,int mask);
- unsigned int mixer_state[SOUND_MIXER_NRDEVICES];
+ struct trident_channel channels[32];
};
struct trident_card {
@@ -239,7 +233,7 @@ struct trident_card {
struct trident_card *next;
/* The trident has a certain amount of cross channel interaction
- so we use a single per card lock */
+ so we use a single per card lock */
spinlock_t lock;
/* PCI device stuff */
@@ -249,286 +243,225 @@ struct trident_card {
/* soundcore stuff */
int dev_audio;
- int dev_mixer;
- struct trident_mixer mix;
- struct trident_state *channels[NR_DSPS];
+ /* structures for abstraction of hardware facilities, codecs, banks and channels*/
+ struct ac97_codec *ac97_codec[NR_AC97];
+ struct trident_pcm_bank banks[NR_BANKS];
+ struct trident_state *states[NR_HW_CH];
/* hardware resources */
unsigned long iobase;
u32 irq;
-
- /* hardware channel allocation bitmap */
- u32 bitmap[2];
-
- /* ugly stupid thing, remove ASAP */
- CHANNELCONTROL ChRegs;
- int ChanDwordCount;
};
static struct trident_card *devs = NULL;
-/*
- * Trident support library routines
- */
-
-/*---------------------------------------------------------------------------
- void ResetAinten( struct trident_state *trident, int ChannelNum)
-
- Description: This routine will disable interrupts and ack any
- existing interrupts for specified channel.
-
- Parameters: trident - pointer to target device class for 4DWave.
- ChannelNum - channel number
-
- returns: TRUE if everything went ok, else FALSE.
-
- ---------------------------------------------------------------------------*/
-
-static void ResetAinten(struct trident_card * trident, int ChannelNum)
-{
- unsigned int dwMask;
- unsigned int x = ChannelNum >> 5;
- unsigned int ChanDwordCount = trident->ChanDwordCount;
-
- IReadAinten(&trident->ChRegs);
- dwMask = 1 << (ChannelNum & 0x1f);
- trident->ChRegs.lpChAinten[x] &= ~dwMask;
- IWriteAinten(&trident->ChRegs);
- // Ack the channel in case the interrupt was set before we disable it.
- outl(dwMask, TRID_REG(trident, trident->ChRegs.lpAChAint[x]));
-}
+static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
+static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg);
-/*---------------------------------------------------------------------------
- void EnableEndInterrupts( struct trident_card *trident)
-
- Description: This routine will enable end of loop interrupts.
- End of loop interrupts will occur when a running
- channel reaches ESO.
-
- Parameters: trident - pointer to target device class for 4DWave.
-
- returns: TRUE if everything went ok, else FALSE.
-
- ---------------------------------------------------------------------------*/
-
-static int trident_enable_end_interrupts(struct trident_card * trident)
+static int trident_open_mixdev(struct inode *inode, struct file *file);
+static int trident_release_mixdev(struct inode *inode, struct file *file);
+static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg);
+static loff_t trident_llseek(struct file *file, loff_t offset, int origin);
+
+static int trident_enable_loop_interrupts(struct trident_card * card)
{
u32 global_control;
- global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+ global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR));
- switch (trident->pci_id)
+ switch (card->pci_id)
{
case PCI_DEVICE_ID_SI_7018:
- global_control |= (ENDLP_IE | BANK_B_EN);
+ global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN);
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- global_control |= ENDLP_IE;
+ global_control |= (ENDLP_IE | MIDLP_IE);
break;
default:
return FALSE;
}
- outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR));
+ outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR));
#ifdef DEBUG
- printk("trident: Enable End Interrupts, globctl = 0x%08X\n", global_control);
+ printk("trident: Enable Loop Interrupts, globctl = 0x%08X\n",
+ global_control);
#endif
return (TRUE);
}
-static int trident_enable_middle_interrupts(struct trident_card * trident)
+static int trident_disable_loop_interrupts(struct trident_card * card)
{
u32 global_control;
- global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
-
- switch (trident->pci_id)
- {
- case PCI_DEVICE_ID_SI_7018:
- global_control |= (MIDLP_IE | BANK_B_EN);
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- default:
- global_control |= MIDLP_IE;
- break;
- }
-
- outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR));
+ global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR));
+ global_control &= ~(ENDLP_IE | MIDLP_IE);
+ outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR));
#ifdef DEBUG
- printk("trident: Enable Middle Interrupts, globctl = 0x%08X\n", global_control);
+ printk("trident: Disabled Loop Interrupts, globctl = 0x%08X\n",
+ global_control);
#endif
return (TRUE);
}
-/*---------------------------------------------------------------------------
- void DisableEndInterrupts( struct trident_card *trident)
-
- Description: This routine will disable end of loop interrupts.
- End of loop interrupts will occur when a running
- channel reaches ESO.
-
- Parameters:
- trident - pointer to target device class for 4DWave.
-
- returns: TRUE if everything went ok, else FALSE.
-
- ---------------------------------------------------------------------------*/
-
-static int trident_disable_end_interrupts(struct trident_card * trident)
+
+static void trident_enable_voice_irq(struct trident_card * card, unsigned int channel)
{
- u32 global_control;
+ unsigned int mask = 1 << (channel & 0x1f);
+ struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+ u32 reg, addr = bank->addresses->aint_en;
- global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
- global_control &= ~ENDLP_IE;
- outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR));
+ reg = inl(TRID_REG(card, addr));
+ reg |= mask;
+ outl(reg, TRID_REG(card, addr));
#ifdef DEBUG
- printk("trident: Disabled End Interrupts, globctl = 0x%08X\n", global_control);
+ reg = inl(TRID_REG(card, T4D_AINTEN_B));
+ printk("trident: enabled IRQ on channel %d, AINTEN_B = 0x%08x\n",
+ channel, reg);
#endif
- return (TRUE);
}
-static int trident_disable_middle_interrupts(struct trident_card * trident)
+static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel)
{
- u32 global_control;
-
- global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
- global_control &= ~MIDLP_IE;
- outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR));
+ unsigned int mask = 1 << (channel & 0x1f);
+ struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+ u32 reg, addr = bank->addresses->aint_en;
+
+ reg = inl(TRID_REG(card, addr));
+ reg &= ~mask;
+ outl(reg, TRID_REG(card, addr));
+
+ /* Ack the channel in case the interrupt was set before we disable it. */
+ outl(mask, TRID_REG(card, bank->addresses->aint));
#ifdef DEBUG
- printk("trident: Disabled Middle Interrupts, globctl = 0x%08X\n", global_control);
+ reg = inl(TRID_REG(card, T4D_AINTEN_B));
+ printk("trident: disabled IRQ on channel %d, AINTEN_B = 0x%08x\n",
+ channel, reg);
#endif
- return (TRUE);
}
-/*---------------------------------------------------------------------------
- void trident_enable_voice_irq( unsigned int HwChannel )
-
- Description: Enable an interrupt channel, any channel 0 thru n.
- This routine automatically handles the fact that there are
- more than 32 channels available.
-
- Parameters : HwChannel - Channel number 0 thru n.
- trident - pointer to target device class for 4DWave.
-
- Return Value: None.
-
- ---------------------------------------------------------------------------*/
-void trident_enable_voice_irq(struct trident_card * trident, unsigned int channel)
+
+static void trident_start_voice(struct trident_card * card, unsigned int channel)
{
- unsigned int bank, mask, ChanDwordCount;
+ unsigned int mask = 1 << (channel & 0x1f);
+ struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+ u32 addr = bank->addresses->start;
+
+#ifdef DEBUG
u32 reg;
+#endif
- bank = channel >> 5;
- mask = 1 << (channel & 0x1f);
+ outl(mask, TRID_REG(card, addr));
- ChanDwordCount = trident->ChanDwordCount;
+#ifdef DEBUG
+ reg = inl(TRID_REG(card, T4D_START_B));
+ printk("trident: start voice on channel %d, START_B = 0x%08x\n",
+ channel, reg);
+#endif
+}
- IReadAinten(&trident->ChRegs);
- trident->ChRegs.lpChAinten[bank] |= mask;
- IWriteAinten(&trident->ChRegs);
+static void trident_stop_voice(struct trident_card * card, unsigned int channel)
+{
+ unsigned int mask = 1 << (channel & 0x1f);
+ struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+ u32 addr = bank->addresses->stop;
#ifdef DEBUG
- reg = inl(TRID_REG(trident, T4D_AINTEN_B));
- printk("trident: enabled IRQ on channel %d\n", channel);
+ u32 reg;
+#endif
+
+ outl(mask, TRID_REG(card, addr));
+
+#ifdef DEBUG
+ reg = inl(TRID_REG(card, T4D_STOP_B));
+ printk("trident: stop voice on channel %d, STOP_B = 0x%08x\n",
+ channel, reg);
#endif
}
-/*---------------------------------------------------------------------------
- void trident_disable_voice_irq( unsigned int HwChannel )
-
- Description: Disable an interrupt channel, any channel 0 thru n.
- This routine automatically handles the fact that there are
- more than 32 channels available.
-
- Parameters : HwChannel - Channel number 0 thru n.
- trident - pointer to target device class for 4DWave.
-
- Return Value: None.
-
- ---------------------------------------------------------------------------*/
-void trident_disable_voice_irq(struct trident_card * trident, unsigned int channel)
+static int trident_check_channel_interrupt(struct trident_card * card, int channel)
{
- unsigned int bank, mask, ChanDwordCount;
- u32 reg;
+ unsigned int mask = 1 << (channel & 0x1f);
+ struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+ u32 reg, addr = bank->addresses->aint;
- bank = channel >> 5;
- mask = 1 << (channel & 0x1f);
+ reg = inl(TRID_REG(card, addr));
+
+#ifdef DEBUG
+ if (reg & mask)
+ printk("trident: channel %d has interrupt, AINT_B = 0x%08x\n",
+ channel, reg);
+#endif
+ return (reg & mask) ? TRUE : FALSE;
+}
+
+static void trident_ack_channel_interrupt(struct trident_card * card, int channel)
+{
+ unsigned int mask = 1 << (channel & 0x1f);
+ struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+ u32 reg, addr = bank->addresses->aint;
- ChanDwordCount = trident->ChanDwordCount;
- IReadAinten(&trident->ChRegs);
- trident->ChRegs.lpChAinten[bank] &= ~mask;
- IWriteAinten(&trident->ChRegs);
+ reg = inl(TRID_REG(card, addr));
+ reg &= mask;
+ outl(reg, TRID_REG(card, addr));
#ifdef DEBUG
- reg = inl(TRID_REG(trident, T4D_AINTEN_B));
- printk("trident: disabled IRQ on channel %d\n", channel);
+ reg = inl(TRID_REG(card, T4D_AINT_B));
+ printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n",
+ channel, reg);
#endif
}
-/*---------------------------------------------------------------------------
- unsigned int AllocateChannelPCM( void )
-
- Description: Allocate hardware channel by reverse order (63-0).
-
- Parameters : trident - pointer to target device class for 4DWave.
-
- Return Value: hardware channel - 0-63 or -1 when no channel is available
-
- ---------------------------------------------------------------------------*/
-
-static int trident_alloc_pcm_channel(struct trident_card *trident)
+static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card)
{
+ struct trident_pcm_bank *bank;
int idx;
- if (trident->bitmap[BANK_B] == ~0UL) {
+ bank = &card->banks[BANK_B];
+ if (bank->bitmap == ~0UL) {
/* no more free channels avaliable */
printk(KERN_ERR "trident: no more channels available on Bank B.\n");
- return -1;
+#ifdef ABUSE_BANK_A
+ goto bank_a;
+#endif
+ return NULL;
}
for (idx = 31; idx >= 0; idx--) {
- if (!(trident->bitmap[BANK_B] & (1 << idx))) {
- trident->bitmap[BANK_B] |= 1 << idx;
- return idx + 32;
+ if (!(bank->bitmap & (1 << idx))) {
+ struct trident_channel *channel = &bank->channels[idx];
+ bank->bitmap |= 1 << idx;
+ channel->num = idx + 32;
+ return channel;
}
}
#ifdef ABUSE_BANK_A
/* channels in Bank A should be reserved for synthesizer
not for normal use (channels in Bank A can't record) */
- if (trident->bitmap[BANK_A] == ~0UL) {
+ bank_a:
+ bank = &card->banks[BANK_A];
+ if (bank->bitmap == ~0UL) {
/* no more free channels avaliable */
- printk(KERN_ERR "trident: no channels available on Bank A.\n");
- return -1;
+ printk(KERN_ERR "trident: no more channels available on Bank A.\n");
+ return NULL;
}
for (idx = 31; idx >= 0; idx--) {
- if (!(trident->bitmap[BANK_A] & (1 << idx))) {
- trident->bitmap[BANK_A] |= 1 << idx;
- return idx;
+ if (!(bank->bitmap & (1 << idx))) {
+ struct trident_channel *channel = &bank->channels[idx];
+ banks->bitmap |= 1 << idx;
+ channel->num = idx;
+ return channels;
}
}
#endif
-
- return -1;
+ return NULL;
}
-/*---------------------------------------------------------------------------
- void FreeChannelPCM( int channel )
-
- Description: Free hardware channel.
-
- Parameters : trident - pointer to target device class for 4DWave.
- channel - hardware channel number 0-63
-
- Return Value: none
-
- ---------------------------------------------------------------------------*/
-
-static void trident_free_pcm_channel(struct trident_card *trident, int channel)
+static void trident_free_pcm_channel(struct trident_card *card, int channel)
{
int bank;
@@ -543,236 +476,74 @@ static void trident_free_pcm_channel(struct trident_card *trident, int channel)
bank = channel >> 5;
channel = channel & 0x1f;
- if (trident->bitmap[bank] & (1 << (channel))) {
- trident->bitmap[bank] &= ~(1 << (channel));
+ if (card->banks[bank].bitmap & (1 << (channel))) {
+ card->banks[bank].bitmap &= ~(1 << (channel));
}
}
-/*---------------------------------------------------------------------------
- void trident_start_voice( ULONG HwChannel )
-
- Description: Start a channel, any channel 0 thru n.
- This routine automatically handles the fact that there are
- more than 32 channels available.
-
- Parameters : HwChannel - Channel number 0 thru n.
- trident - pointer to target device class for 4DWave.
-
- Return Value: None.
-
- ---------------------------------------------------------------------------*/
-void trident_start_voice(struct trident_card * trident, unsigned int channel)
-{
- unsigned int bank = channel >> 5;
- unsigned int mask = 1 << (channel & 0x1f);
-
- outl(mask, TRID_REG(trident, trident->ChRegs.lpAChStart[bank]));
-#ifdef DEBUG
- printk("trident: start voice on channel %d\n", channel);
-#endif
-}
-
-/*---------------------------------------------------------------------------
- void trident_stop_voice( ULONG HwChannel )
-
- Description: Stop a channel, any channel 0 thru n.
- This routine automatically handles the fact that there are
- more than 32 channels available.
-
- Parameters : HwChannel - Channel number 0 thru n.
- trident - pointer to target device class for 4DWave.
-
- Return Value: None.
-
- ---------------------------------------------------------------------------*/
-void trident_stop_voice(struct trident_card * trident, unsigned int channel)
+/* called with spin lock held */
+static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel)
{
- unsigned int bank = channel >> 5;
- unsigned int mask = 1 << (channel & 0x1f);
-
- outl(mask, TRID_REG(trident, trident->ChRegs.lpAChStop[bank]));
-#ifdef DEBUG
- printk("trident: stop voice on channel %d\n", channel);
-#endif
-}
-
-/*---------------------------------------------------------------------------
- int DidChannelInterrupt( )
-
- Description: Check if interrupt channel occurred.
-
- Parameters : trident - pointer to target device class for 4DWave.
-
- Return Value: TRUE if interrupt occurred, else FALSE.
-
- ---------------------------------------------------------------------------*/
-static int trident_check_channel_interrupt(struct trident_card * trident, int channel)
-{
- unsigned int ChanDwordCount = NUM_BANKS;
- unsigned int bank = channel >> 5;
- unsigned int mask = 1 << (channel & 0x1f);
-
- ReadAint(&trident->ChRegs);
-
-#ifdef DEBUG
- if (trident->ChRegs.lpChAint[bank] & mask)
- printk("trident: channel %d has interrupt\n", channel);
-#endif
- return (trident->ChRegs.lpChAint[bank] & mask) ? TRUE : FALSE;
-}
-
-/*---------------------------------------------------------------------------
- void AckChannelInterrupt( )
-
- Description: Acknowledge the interrupt bit for channel intrs.
-
- Parameters : trident - pointer to target device class for 4DWave.
-
- Return Value: None
-
- ---------------------------------------------------------------------------*/
-static void trident_ack_channel_interrupt(struct trident_card * trident, int channel)
-{
- unsigned int ChanDwordCount = NUM_BANKS;
- unsigned int bank = channel >> 5;
- unsigned int mask = 1 << (channel & 0x1f);
-
- ReadAint(&trident->ChRegs);
- trident->ChRegs.lpChAint[bank] &= mask;
- IWriteAint(&trident->ChRegs);
-}
-
-/*---------------------------------------------------------------------------
- int trident_load_hw_delta( unsigned int HwChannel, unsigned int Delta )
-
- Description: This routine writes Delta to the hardware.
-
- Parameters: Delta - data to write (2 Bytes only)
- HwChannel - Hardware channel to write to.
- trident - pointer to target device class for 4DWave.
-
- Returns: TRUE if all goes well, else FALSE.
-
- ---------------------------------------------------------------------------*/
-static int trident_load_hw_delta (struct trident_card * trident, unsigned int channel,
- unsigned short delta)
-{
- /* select a channel for output */
- outb(channel, TRID_REG(trident, T4D_LFO_GC_CIR));
+ int i;
- switch (trident->pci_id)
- {
- case PCI_DEVICE_ID_SI_7018:
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- outw((u16) delta, TRID_REG(trident, CH_DX_ESO_DELTA));
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- outb(delta & 0xff, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
- outb((delta >> 8)& 0xff, TRID_REG(trident, CH_NX_DELTA_ESO + 3));
- break;
- default:
+ if (channel > 63)
return FALSE;
- }
- return TRUE;
-}
-/*---------------------------------------------------------------------------
- int LoadVirtualChannel( ULONG *Data, ULONG HwChannel)
-
- Description: This routine writes all required channel registers to hardware.
-
- Parameters: *Data - a pointer to the data to write (5 ULONGS always).
- HwChannel - Hardware channel to write to.
- trident - pointer to target device class for 4DWave.
-
- Returns: TRUE if all goes well, else FALSE.
-
- ---------------------------------------------------------------------------*/
-static int LoadVirtualChannel(struct trident_card * trident, unsigned int *Data, unsigned int HwChannel)
-{
- unsigned int ChanData[CHANNEL_REGS];
- unsigned int ULONGSToDo = CHANNEL_REGS;
- unsigned int i;
- unsigned int Address = CHANNEL_START;
-
- /* Copy the data first... Hack... Before mucking with Volume! */
- memcpy((unsigned char *) ChanData, (unsigned char *) Data, ULONGSToDo * 4);
-
- outb((unsigned char) HwChannel, TRID_REG(trident, T4D_LFO_GC_CIR));
+ /* select hardware channel to write */
+ outb(channel, TRID_REG(card, T4D_LFO_GC_CIR));
+ /* output the channel registers */
+ for (i = 0; i < CHANNEL_REGS; i++) {
+ outl(data[i], TRID_REG(card, CHANNEL_START + 4*i));
+ }
- for (i = 0; i < ULONGSToDo; i++, Address += 4)
- outl(ChanData[i], TRID_REG(trident, Address));
-#ifdef DEBUG
- printk("(trident) load virtual channel %d\n", HwChannel);
-#endif
return TRUE;
}
-/*---------------------------------------------------------------------------
- trident_write_voice_regs
-
- Description: This routine will write the 5 hardware channel registers
- to hardware.
-
- Paramters: trident - pointer to target device class for 4DWave.
- Channel - Real or Virtual channel number.
- Each register field.
-
- Returns: TRUE if all goes well, else FALSE.
-
- ---------------------------------------------------------------------------*/
-int trident_write_voice_regs(struct trident_card * trident,
- unsigned int Channel,
- unsigned int LBA,
- unsigned int CSO,
- unsigned int ESO,
- unsigned int DELTA,
- unsigned int ALPHA_FMS,
- unsigned int FMC_RVOL_CVOL,
- unsigned int GVSEL,
- unsigned int PAN,
- unsigned int VOL,
- unsigned int CTRL,
- unsigned int EC)
+/* called with spin lock held */
+static int trident_write_voice_regs(struct trident_state *state, unsigned int rec)
{
- unsigned int ChanData[CHANNEL_REGS + 1], FmcRvolCvol;
+ unsigned int data[CHANNEL_REGS + 1];
+ struct trident_channel *channel;
- ChanData[1] = LBA;
- ChanData[4] = (GVSEL << 31) |
- ((PAN & 0x0000007f) << 24) |
- ((VOL & 0x000000ff) << 16) |
- ((CTRL & 0x0000000f) << 12) |
- (EC & 0x00000fff);
+ if (rec)
+ channel = state->dma_adc.channel;
+ else
+ channel = state->dma_dac.channel;
- FmcRvolCvol = FMC_RVOL_CVOL & 0x0000ffff;
+ data[1] = channel->lba;
+ data[4] = channel->control;
- switch (trident->pci_id)
+ switch (state->card->pci_id)
{
case PCI_DEVICE_ID_SI_7018:
+ data[0] = 0; /* Current Sample Offset */
+ data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
+ data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff);
+ break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- ChanData[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff);
- ChanData[2] = (ESO << 16) | (DELTA & 0x0000ffff);
- ChanData[3] = FmcRvolCvol;
+ data[0] = 0; /* Current Sample Offset */
+ data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
+ data[3] = channel->fm_vol & 0xffff;
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- ChanData[0] = (DELTA << 24) | (CSO & 0x00ffffff);
- ChanData[2] = ((DELTA << 16) & 0xff000000) | (ESO & 0x00ffffff);
- ChanData[3] = (ALPHA_FMS << 16) | FmcRvolCvol;
+ data[0] = (channel->delta << 24);
+ data[2] = ((channel->delta << 24) & 0xff000000) | (channel->eso & 0x00ffffff);
+ data[3] = channel->fm_vol & 0xffff;
break;
+ default:
+ return FALSE;
}
- LoadVirtualChannel(trident, ChanData, Channel);
-
- return TRUE;
+ return trident_load_channel_registers(state->card, data, channel->num);
}
static int compute_rate(u32 rate)
{
int delta;
- // We special case 44100 and 8000 since rounding with the equation
- // does not give us an accurate enough value. For 11025 and 22050
- // the equation gives us the best answer. All other frequencies will
- // also use the equation. JDW
+ /* We special case 44100 and 8000 since rounding with the equation
+ does not give us an accurate enough value. For 11025 and 22050
+ the equation gives us the best answer. All other frequencies will
+ also use the equation. JDW */
if (rate == 44100)
delta = 0xeb3;
else if (rate == 8000)
@@ -784,838 +555,267 @@ static int compute_rate(u32 rate)
return delta;
}
-/*---------------------------------------------------------------------------
- trident_set_dac_rate
-
- Description: This routine will set the sample rate for playback.
-
- Paramters: trident - pointer to target device class for 4DWave.
- rate - desired sample rate
- set - actually write hardware if set is true.
-
- Returns: The rate allowed by device.
-
- ---------------------------------------------------------------------------*/
-
-static unsigned int trident_set_dac_rate(struct trident_state * trident,
- unsigned int rate, int set)
-{
- u16 delta;
+/* set playback sample rate */
+static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate)
+{
+ struct dmabuf *dmabuf = &state->dma_dac;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
- delta = compute_rate(rate);
- trident->ratedac = rate;
+ dmabuf->rate = rate;
+ dmabuf->channel->delta = compute_rate(rate);
- if (set)
- trident_load_hw_delta(trident->card, trident->dma_dac.chan,
- delta);
-#ifdef DEBUG
- printk("trident: called trident_set_dac_rate : rate = %d, "
- "set = %d, delta = 0x%04x\n", rate, set, delta);
+ trident_write_voice_regs(state, 0);
+
+#ifdef DEBUG
+ printk("trident: called trident_set_dac_rate : rate = %d\n", rate);
#endif
return rate;
}
-/*---------------------------------------------------------------------------
- trident_set_adc_rate
-
- Description: This routine will set the sample rate for capture.
-
- Paramters: trident - pointer to target device class for 4DWave.
- rate - desired sample rate
- set - actually write hardware if set is true.
-
- Returns: The rate allowed by device.
-
- ---------------------------------------------------------------------------*/
-
-static unsigned int trident_set_adc_rate(struct trident_state * trident,
- unsigned int rate, int set)
+/* set recording sample rate */
+static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate)
{
- u16 delta;
-
+ struct dmabuf *dmabuf = &state->dma_adc;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
- delta = compute_rate(rate);
- trident->ratedac = rate;
-
-#if 0 /* It seems that 4D-Wave can not use wave tables channels for recording */
- if (set)
- trident_load_hw_delta(trident->card, trident->dma_adc.chan,
- delta);
-#endif
-#ifdef DEBUG
- printk("trident: called trident_set_adc_rate : rate = %d, "
- "set = %d, delta = 0x%04x\n", rate, set, delta);
-#endif
- return rate;
-}
-
-extern __inline__ 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;
-}
-
-/* Write AC97 mixer registers */
-static void trident_ac97_set(struct trident_card *trident, u8 cmd, u16 val)
-{
- unsigned int address, mask, busy;
- unsigned short count = 0xffff;
- u32 data;
-
- data = ((u32) val) << 16;
-
- switch (trident->pci_id)
- {
- default:
- case PCI_DEVICE_ID_SI_7018:
- address = SI_AC97_WRITE;
- mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY;
- busy = SI_AC97_BUSY_WRITE;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- address = DX_ACR0_AC97_W;
- mask = busy = DX_AC97_BUSY_WRITE;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- address = NX_ACR1_AC97_W;
- mask = busy = NX_AC97_BUSY_WRITE;
- break;
- }
-
- do {
- if ((inw(TRID_REG(trident, address)) & busy) == 0)
- break;
- } while (count--);
-
- data |= (mask | (cmd & AC97_REG_ADDR));
-
- if (count == 0) {
- printk(KERN_ERR "trident: AC97 CODEC write timed out.\n");
- return;
- }
- outl(data, TRID_REG(trident, address));
-}
-
-/* Read AC97 codec registers */
-static u16 trident_ac97_get(struct trident_card *trident, u8 cmd)
-{
- unsigned int address, mask, busy;
- unsigned short count = 0xffff;
- u32 data;
-
- switch (trident->pci_id)
- {
- default:
- case PCI_DEVICE_ID_SI_7018:
- address = SI_AC97_READ;
- mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY;
- busy = SI_AC97_BUSY_READ;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- address = DX_ACR1_AC97_R;
- mask = busy = DX_AC97_BUSY_READ;
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- address = NX_ACR2_AC97_R_PRIMARY;
- mask = NX_AC97_BUSY_READ;
- busy = 0x0c00;
- break;
- }
+ dmabuf->rate = rate;
+ dmabuf->channel->delta = compute_rate(rate);
- data = (mask | (cmd & AC97_REG_ADDR));
- outl(data, TRID_REG(trident, address));
-
- do {
- data = inl(TRID_REG(trident, address));
- if ((data & busy) == 0)
- break;
- } while (count--);
-
- if (count == 0) {
- printk(KERN_ERR "trident: AC97 CODEC read timed out.\n");
- data = 0;
- }
- return ((u16) (data >> 16));
-}
-
-/* OSS interface to the ac97s.. */
-
-#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\
- SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\
- SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN)
-
-#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \
- SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\
- SOUND_MASK_SPEAKER)
-
-#define AC97_RECORD_MASK (SOUND_MASK_MIC|\
- SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\
- SOUND_MASK_PHONEIN)
-
-#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<<FOO) )
-
-/* this table has default mixer values for all OSS mixers.
- be sure to fill it in if you add oss mixers
- to anyone's supported mixer defines */
-
-/* possible __init */
-static struct mixer_defaults {
- int mixer;
- unsigned int value;
-} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
- /* all values 0 -> 100 in bytes */
- {SOUND_MIXER_VOLUME, 0x3232},
- {SOUND_MIXER_BASS, 0x3232},
- {SOUND_MIXER_TREBLE, 0x3232},
- {SOUND_MIXER_SPEAKER, 0x3232},
- {SOUND_MIXER_MIC, 0x3232},
- {SOUND_MIXER_LINE, 0x3232},
- {SOUND_MIXER_CD, 0x3232},
- {SOUND_MIXER_VIDEO, 0x3232},
- {SOUND_MIXER_LINE1, 0x3232},
- {SOUND_MIXER_PCM, 0x3232},
- {SOUND_MIXER_IGAIN, 0x3232},
- {-1,0}
-};
-
-static struct ac97_mixer_hw {
- unsigned char offset;
- int scale;
-} ac97_hw[SOUND_MIXER_NRDEVICES]= {
- [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,63},
- [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 15},
- [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 15},
- [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 15},
- [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 31},
- [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 31},
- [SOUND_MIXER_CD] = {AC97_CD_VOL, 31},
- [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 31},
- [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 31},
- [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 31},
- [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 31}
-};
-
-#if 0 /* *shrug* removed simply because we never used it.
- feel free to implement again if needed */
-
-/* reads the given OSS mixer from the ac97
- the caller must have insured that the ac97 knows
- about that given mixer, and should be holding a
- spinlock for the card */
-static int ac97_read_mixer(struct trident_card *card, int mixer)
-{
- u16 val;
- int ret = 0;
- struct ac97_mixer_hw *mh = &ac97_hw[mixer];
-
- val = trident_ac97_get(card , mh->offset);
-
- if (AC97_STEREO_MASK & (1<<mixer)) {
- /* nice stereo mixers .. */
- int left,right;
-
- left = (val >> 8) & 0x7f;
- right = val & 0x7f;
-
- if (mixer == SOUND_MIXER_IGAIN) {
- right = (right * 100) / mh->scale;
- left = (left * 100) / mh->scale;
- else {
- right = 100 - ((right * 100) / mh->scale);
- left = 100 - ((left * 100) / mh->scale);
- }
-
- ret = left | (right << 8);
- } else if (mixer == SOUND_MIXER_SPEAKER) {
- ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
- } else if (mixer == SOUND_MIXER_MIC) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- } else if (mixer == SOUND_MIXER_BASS) {
- ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
- } else if (mixer == SOUND_MIXER_TREBLE) {
- ret = 100 - (((val & 0xe) * 100) / mh->scale);
- }
+ trident_write_voice_regs(state, 1);
#ifdef DEBUG
- printk("trident: read OSS mixer %2d (ac97 register 0x%02x), "
- "0x%04x -> 0x%04x\n", mixer, mh->offset, val, ret);
+ printk("trident: called trident_set_adc_rate : rate = %d\n", rate);
#endif
-
- return ret;
+ return rate;
}
-#endif
-/* write the OSS encoded volume to the given OSS encoded mixer,
- again caller's job to make sure all is well in arg land,
- call with spinlock held */
-static void ac97_write_mixer(struct trident_card *card, int mixer,
- unsigned int left, unsigned int right)
+/* prepare channel attributes for playback */
+static void trident_play_setup(struct trident_state *state)
{
- u16 val = 0;
- struct ac97_mixer_hw *mh = &ac97_hw[mixer];
+ struct dmabuf *dmabuf = &state->dma_dac;
+ struct trident_channel *channel = dmabuf->channel;
-#ifdef DEBUG
- printk("trident: wrote OSS mixer %2d (ac97 register 0x%02x), "
- "left vol:%2d, right vol:%2d:",
- mixer, mh->offset, left, right);
-#endif
+ channel->lba = virt_to_bus(dmabuf->rawbuf);
+ channel->delta = compute_rate(dmabuf->rate);
- if (AC97_STEREO_MASK & (1 << mixer)) {
- /* stereo mixers */
- if (mixer == SOUND_MIXER_IGAIN) {
- right = (right * mh->scale) / 100;
- left = (left * mh->scale) / 100;
- } else {
- right = ((100 - right) * mh->scale) / 100;
- left = ((100 - left) * mh->scale) / 100;
- }
+ channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
+ channel->eso -= 1;
- val = (left << 8) | right;
- } else if (mixer == SOUND_MIXER_SPEAKER) {
- val = (((100 - left) * mh->scale) / 100) << 1;
- } else if (mixer == SOUND_MIXER_MIC) {
- val = trident_ac97_get(card , mh->offset) & ~0x801f;
- val |= (((100 - left) * mh->scale) / 100);
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- } else if (mixer == SOUND_MIXER_BASS) {
- val = trident_ac97_get(card , mh->offset) & ~0x0f00;
- val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00;
- } else if (mixer == SOUND_MIXER_TREBLE) {
- val = trident_ac97_get(card , mh->offset) & ~0x000f;
- val |= (((100 - left) * mh->scale) / 100) & 0x000e;
+ if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) {
+ /* FIXME: channel attributes are configured by ioctls, but it is not implemented
+ so just set to ZERO for the moment */
+ channel->attribute = 0;
+ } else {
+ channel->attribute = 0;
}
-#ifdef DEBUG
- printk(" 0x%04x", val);
-#endif
- trident_ac97_set(card, mh->offset, val);
-
-#ifdef DEBUG
- val = trident_ac97_get(card, mh->offset);
- printk(" -> 0x%04x\n", val);
-#endif
-}
-
-/* the following tables allow us to go from
- OSS <-> ac97 quickly. */
-
-enum ac97_recsettings {
- AC97_REC_MIC=0,
- AC97_REC_CD,
- AC97_REC_VIDEO,
- AC97_REC_AUX,
- AC97_REC_LINE,
- AC97_REC_STEREO, /* combination of all enabled outputs.. */
- AC97_REC_MONO, /*.. or the mono equivalent */
- AC97_REC_PHONE
-};
-
-static unsigned int ac97_rm2oss[] = {
- [AC97_REC_MIC] = SOUND_MIXER_MIC,
- [AC97_REC_CD] = SOUND_MIXER_CD,
- [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
- [AC97_REC_AUX] = SOUND_MIXER_LINE1,
- [AC97_REC_LINE] = SOUND_MIXER_LINE,
- [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
-};
-
-/* indexed by bit position */
-static unsigned int ac97_oss_rm[] = {
- [SOUND_MIXER_MIC] = AC97_REC_MIC,
- [SOUND_MIXER_CD] = AC97_REC_CD,
- [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
- [SOUND_MIXER_LINE1] = AC97_REC_AUX,
- [SOUND_MIXER_LINE] = AC97_REC_LINE,
- [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
-};
+ channel->fm_vol = 0x0;
-/* read or write the recmask
- the ac97 can really have left and right recording
- inputs independantly set, but OSS doesn't seem to
- want us to express that to the user.
- the caller guarantees that we have a supported bit set,
- and they must be holding the card's spinlock */
-static int ac97_recmask_io(struct trident_card *card, int rw, int mask)
-{
- unsigned int val;
-
- if (rw) {
- /* read it from the card */
- val = trident_ac97_get(card, 0x1a) & 0x7;
- return ac97_rm2oss[val];
- }
-
- /* else, write the first set in the mask as the
- output */
-
- val = ffs(mask);
- val = ac97_oss_rm[val-1];
- val |= val << 8; /* set both channels */
-#ifdef DEBUG
- printk("trident: setting ac97 recmask to 0x%x\n", val);
-#endif
- trident_ac97_set(card, 0x1a, val);
-
- return 0;
-};
-
-/* AC97 codec initialisation. */
-static u16 trident_ac97_init(struct trident_card *trident)
-{
- u16 id1, id2;
- char *ac97_name = NULL;
- int i;
-
- /* initialize controller side of AC link */
- switch (trident->pci_id)
- {
- case PCI_DEVICE_ID_SI_7018:
- /* disable AC97 GPIO interrupt */
- outl(0x00, TRID_REG(trident, SI_AC97_GPIO));
- /* stop AC97 cold reset process */
- outl(0x00014000, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- /* playback on */
- outl(0x02, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */
- outl(0x02, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
- break;
- }
-
- /* get some information about our AC97 codec */
- id1 = trident_ac97_get(trident, AC97_VENDOR_ID1);
- id2 = trident_ac97_get(trident, AC97_VENDOR_ID2);
- for (i = 0; i < sizeof (snd_ac97_codec_ids); i++) {
- if (snd_ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
- ac97_name = snd_ac97_codec_ids[i].name;
- break;
- }
- }
- if (ac97_name == NULL)
- ac97_name = "Unknown";
- printk(KERN_INFO "trident: ac97 vendor id1: 0x%04x, id2: 0x%04x (%s)\n",
- id1, id2, ac97_name);
-
- /* initialize volume level */
- trident_ac97_set(trident, AC97_RESET, 0L);
- trident_ac97_set(trident, AC97_MASTER_VOL_STEREO, 0L);
- trident_ac97_set(trident, AC97_PCMOUT_VOL, 0L);
-
- /* set appropriate masks and function pointers */
- trident->mix.supported_mixers = AC97_SUPPORTED_MASK;
- trident->mix.stereo_mixers = AC97_STEREO_MASK;
- trident->mix.record_sources = AC97_RECORD_MASK;
- /* FIXME: trident->mix.read_mixer = ac97_read_mixer; */
- trident->mix.write_mixer = ac97_write_mixer;
- trident->mix.recmask_io = ac97_recmask_io;
-
- return 0;
-}
-
-/* this function only update fmt field in trident_state, the hardware channel attribute
- will be update in trident_play(rec)_setup() which will be called every time a new
- sample is played(recorded) */
-static void set_fmt(struct trident_state *s, unsigned char mask, unsigned char data)
-{
- s->fmt = (s->fmt & mask) | data;
-}
-
-/* the mode passed should be already shifted and masked */
-/* trident_play_setup: initialize channel for play back, mode specify the format of samples to
- be played.
- default values:
-*/
-
-static void trident_play_setup(struct trident_state *trident, int mode, u32 rate,
- void *buffer, int size)
-{
- unsigned int LBA;
- unsigned int Delta;
- unsigned int ESO;
- unsigned int CTRL;
- unsigned int FMC_RVOL_CVOL;
- unsigned int GVSEL;
- unsigned int PAN;
- unsigned int VOL;
- unsigned int EC;
-
- /* set Loop Begin Address */
- LBA = virt_to_bus(buffer);
- Delta = compute_rate(rate);
-
- /* set ESO */
- ESO = size;
- if (mode & TRIDENT_FMT_16BIT)
- ESO /= 2;
- if (mode & TRIDENT_FMT_STEREO)
- ESO /= 2;
- ESO = ESO - 1;
-
- /* loop mode enable */
- CTRL = 0x00000001;
- if (mode & TRIDENT_FMT_16BIT) {
+ channel->control = CHANNEL_LOOP;
+ if (dmabuf->fmt & TRIDENT_FMT_16BIT) {
/* 16-bits */
- CTRL |= 0x00000008;
+ channel->control |= CHANNEL_16BITS;
/* signed */
- CTRL |= 0x00000002;
+ channel->control |= CHANNEL_SIGNED;
}
- if (mode & TRIDENT_FMT_STEREO)
+ if (dmabuf->fmt & TRIDENT_FMT_STEREO)
/* stereo */
- CTRL |= 0x00000004;
-
- /* FIXME: some difference between 4D and 7018 in FMC_RVOL_CVOL */
- /* right vol: mute, ledt vol: mute */
- FMC_RVOL_CVOL = 0x0000ffff;
- GVSEL = 1;
- PAN = 0;
- VOL = 0;
- EC = 0;
-
- trident_write_voice_regs(trident->card,
- trident->dma_dac.chan,
- LBA,
- 0, /* cso */
- ESO,
- Delta,
- 0, /* alpha */
- FMC_RVOL_CVOL,
- GVSEL,
- PAN,
- VOL,
- CTRL,
- EC);
-
+ channel->control |= CHANNEL_STEREO;
+#ifdef DEBUG
+ printk("trident: trident_play_setup, LBA = 0x%08x, "
+ "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n",
+ channel->lba, channel->delta, channel->eso, channel->control);
+#endif
+ trident_write_voice_regs(state, 0);
}
-/*
- * Native record driver
- */
-/* FIXME: Not exammed yet */
-/* again, passed mode is alrady shifted/masked */
-
-static void trident_rec_setup(struct trident_state *trident, int mode, u32 rate,
- void *buffer, int size)
+/* prepare channel attributes for recording */
+static void trident_rec_setup(struct trident_state *state)
{
- unsigned int LBA;
- unsigned int Delta;
- unsigned int ESO;
- unsigned int CTRL;
- unsigned int FMC_RVOL_CVOL;
- unsigned int GVSEL;
- unsigned int PAN;
- unsigned int VOL;
- unsigned int EC;
- unsigned char bValue;
- unsigned short wValue;
- unsigned int dwValue;
- unsigned short wRecCODECSamples;
- unsigned int dwChanFlags;
- struct trident_card *card = trident->card;
-
-#ifdef DEBUG
- printk("trident: trident_rec_setup called\n");
-#endif
+ u16 w;
+ struct trident_card *card = state->card;
+ struct dmabuf *dmabuf = &state->dma_adc;
+ struct trident_channel *channel = dmabuf->channel;
- // Enable AC-97 ADC (capture), disable capture interrupt
+ /* Enable AC-97 ADC (capture) */
switch (card->pci_id)
{
case PCI_DEVICE_ID_SI_7018:
/* for 7018, the ac97 is always in playback/record (duplex) mode */
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- bValue = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT));
- outb(bValue | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+ w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+ outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- wValue = inw(TRID_REG(card, T4D_MISCINT));
- outw(wValue | 0x1000, TRID_REG(card, T4D_MISCINT));
+ w = inw(TRID_REG(card, T4D_MISCINT));
+ outw(w | 0x1000, TRID_REG(card, T4D_MISCINT));
break;
+ default:
+ return;
}
- // Initilize the channel and set channel Mode
- outb(0, TRID_REG(card, LEGACY_DMAR15));
-
- // Set DMA channel operation mode register
- bValue = inb(TRID_REG(card, LEGACY_DMAR11)) & 0x03;
- outb(bValue | 0x54, TRID_REG(card, LEGACY_DMAR11));
-
- // Set channel buffer Address
- LBA = virt_to_bus(buffer);
- outl(LBA, TRID_REG(card, LEGACY_DMAR0));
-
- /* set ESO */
- ESO = size;
+ channel->lba = virt_to_bus(dmabuf->rawbuf);
+ channel->delta = compute_rate(dmabuf->rate);
- dwValue = inl(TRID_REG(card, LEGACY_DMAR4)) & 0xff000000;
- dwValue |= (ESO - 1) & 0x0000ffff;
- outl(dwValue, TRID_REG(card, LEGACY_DMAR4));
+ channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
+ channel->eso -= 1;
- // Set channel sample rate , 4.12 format
- dwValue = (((unsigned int) 48000L << 12) / (unsigned long) (rate));
- outw((unsigned short) dwValue, TRID_REG(card, T4D_SBDELTA_DELTA_R));
-
- // Set channel interrupt blk length
- if (mode & TRIDENT_FMT_16BIT) {
- wRecCODECSamples = (unsigned short) ((ESO >> 1) - 1);
- dwChanFlags = 0xffffb000;
+ if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) {
+ /* FIXME: channel attributes are configured by ioctls, but it is not implemented
+ so just set to ZERO for the moment */
+ channel->attribute = 0;
} else {
- wRecCODECSamples = (unsigned short) (ESO - 1);
- dwChanFlags = 0xffff1000;
+ channel->attribute = 0;
}
- dwValue = ((unsigned int) wRecCODECSamples) << 16;
- dwValue |= (unsigned int) (wRecCODECSamples) & 0x0000ffff;
- outl(dwValue, TRID_REG(card, T4D_SBBL_SBCL));
-
- // Right now, set format and start to run capturing,
- // continuous run loop enable.
- trident->bDMAStart = 0x19; // 0001 1001b
-
- if (mode & TRIDENT_FMT_16BIT)
- trident->bDMAStart |= 0xa0;
- if (mode & TRIDENT_FMT_STEREO)
- trident->bDMAStart |= 0x40;
-
- // Prepare capture intr channel
-
- Delta = ((((unsigned int) rate) << 12) / ((unsigned long) (48000L)));
-
- /* set Loop Back Address */
- LBA = virt_to_bus(buffer);
-
- /* set ESO */
- ESO = size;
- if (mode & TRIDENT_FMT_16BIT)
- ESO /= 2;
- if (mode & TRIDENT_FMT_STEREO)
- ESO /= 2;
-
- ESO = ESO - 1;
- //snd_printk("trid: ESO = %d\n", ESO);
-
- /* set ctrl mode
- CTRL default: 8-bit (unsigned) mono, loop mode enabled
- */
- CTRL = 0x00000001;
- if (mode & TRIDENT_FMT_16BIT)
- CTRL |= 0x00000008; // 16-bit data
- /* XXX DO UNSIGNED XXX */
- //if (!(mode & SND_PCM1_MODE_U))
- // CTRL |= 0x00000002; // signed data
- if (mode& TRIDENT_FMT_STEREO)
- CTRL |= 0x00000004; // stereo data
-
- FMC_RVOL_CVOL = 0x0000ffff;
- GVSEL = 1;
- PAN = 0xff;
- VOL = 0xff;
- EC = 0;
-
- trident_write_voice_regs(card,
- trident->dma_adc.chan,
- LBA,
- 0, /* cso */
- ESO,
- Delta,
- 0, /* alpha */
- FMC_RVOL_CVOL,
- GVSEL,
- PAN,
- VOL,
- CTRL,
- EC);
-
+ channel->fm_vol = 0x0;
+
+ channel->control = CHANNEL_LOOP;
+ if (dmabuf->fmt & TRIDENT_FMT_16BIT) {
+ /* 16-bits */
+ channel->control |= CHANNEL_16BITS;
+ /* signed */
+ channel->control |= CHANNEL_SIGNED;
+ }
+ if (dmabuf->fmt & TRIDENT_FMT_STEREO)
+ /* stereo */
+ channel->control |= CHANNEL_STEREO;
+#ifdef DEBUG
+ printk("trident: trident_rec_setup, LBA = 0x%08x, "
+ "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n",
+ channel->lba, channel->delta, channel->eso, channel->control);
+#endif
+ trident_write_voice_regs(state, 1);
}
-/* get current playback pointer */
-__inline__ unsigned int get_dmaa(struct trident_state *trident)
+/* get current playback/recording dma buffer pointer (byte offset from LBA),
+ called with spinlock held! */
+extern __inline__ unsigned trident_get_dma_addr(struct trident_state *state, unsigned rec)
{
+ struct dmabuf *dmabuf;
u32 cso;
- u32 eso;
-#if 0
- /* FIXME: does this mean that FULL duplex is not supported ? */
- if (!(trident->enable & ADC_RUNNING))
+
+ if (rec)
+ dmabuf = &state->dma_adc;
+ else
+ dmabuf = &state->dma_dac;
+
+ if (!dmabuf->enable)
return 0;
-#endif
- outb(trident->dma_dac.chan, TRID_REG(trident->card, T4D_LFO_GC_CIR));
- switch (trident->card->pci_id)
+ outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR));
+
+ switch (state->card->pci_id)
{
case PCI_DEVICE_ID_SI_7018:
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
/* 16 bits ESO, CSO for 7018 and DX */
- cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2));
- eso = inw(TRID_REG(trident->card, CH_DX_ESO_DELTA + 2));
+ cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2));
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
/* 24 bits ESO, CSO for NX */
- cso = inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff;
- eso = inl(TRID_REG(trident->card, CH_NX_DELTA_ESO)) & 0x00ffffff;
+ cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff;
break;
default:
return 0;
}
#ifdef DEBUG
- printk("trident: get_dmaa: chip reported channel: %d, cso = %d, eso = %d\n",
- trident->dma_dac.chan, cso, eso);
+ printk("trident: trident_get_dma_addr: chip reported channel: %d, cso = %d\n",
+ dmabuf->channel->num, cso);
#endif
/* ESO and CSO are in units of Samples, convert to byte offset */
- if (cso > eso)
- cso = eso;
- if (trident->fmt & TRIDENT_FMT_16BIT)
- cso *= 2;
- if (trident->fmt & TRIDENT_FMT_STEREO)
- cso *= 2;
- return cso;
+ cso <<= sample_shift[dmabuf->fmt];
+
+ return (cso % dmabuf->dmasize);
}
-/* get current recording pointer */
-extern __inline__ unsigned get_dmac(struct trident_state *trident)
+/* Stop recording (lock held) */
+extern __inline__ void __stop_adc(struct trident_state *state)
{
- u32 cso;
-#if 0
- /* FIXME: does this mean that FULL duplex is not supported ? */
- if (!(trident->enable & DAC_RUNNING))
- return 0;
-#endif
- outb(trident->dma_adc.chan, TRID_REG(trident->card, T4D_LFO_GC_CIR));
+ struct dmabuf *dmabuf = &state->dma_adc;
+ unsigned int chan_num = dmabuf->channel->num;
+ struct trident_card *card = state->card;
- switch (trident->card->pci_id)
- {
- default:
- case PCI_DEVICE_ID_SI_7018:
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
- /* 16 bits ESO, CSO for 7018 and DX */
- cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2));
- break;
- case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
- /* 24 bits ESO, CSO for NX */
- cso = inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff;
- break;
- }
-
-#ifdef DEBUG
- printk("(trident) get_dmac: chip reported cso = %d\n", cso);
-#endif
- /* ESO and CSO are in units of Samples, convert to byte offset */
- if (trident->fmt & TRIDENT_FMT_16BIT)
- cso *= 2;
- if (trident->fmt & TRIDENT_FMT_STEREO)
- cso *= 2;
- return cso;
+ dmabuf->enable &= ~DMA_RUNNING;
+ trident_stop_voice(card, chan_num);
+ trident_disable_voice_irq(card, chan_num);
}
-/* Stop recording (lock held) */
-extern inline void __stop_adc(struct trident_state *s)
+static void stop_adc(struct trident_state *state)
{
- struct trident_card *trident = s->card;
-#ifdef DEBUG
- printk("(trident) stopping ADC\n");
-#endif
- s->enable &= ~ADC_RUNNING;
- trident_disable_voice_irq(trident, s->dma_adc.chan);
- outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
- trident_disable_voice_irq(trident, s->dma_adc.chan);
- trident_stop_voice(trident, s->dma_adc.chan);
- ResetAinten(trident, s->dma_adc.chan);
-}
+ struct trident_card *card = state->card;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ __stop_adc(state);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
-extern inline void stop_adc(struct trident_state *s)
+static void start_adc(struct trident_state *state)
{
+ struct dmabuf *dmabuf = &state->dma_adc;
+ unsigned int chan_num = dmabuf->channel->num;
+ struct trident_card *card = state->card;
unsigned long flags;
- struct trident_card *trident = s->card;
- spin_lock_irqsave(&trident->lock, flags);
- __stop_adc(s);
- spin_unlock_irqrestore(&trident->lock, flags);
-}
+ spin_lock_irqsave(&card->lock, flags);
+ if ((dmabuf->mapped ||
+ dmabuf->count < (signed)(dmabuf->dmasize - 2*dmabuf->fragsize))
+ && dmabuf->ready) {
+ dmabuf->enable |= DMA_RUNNING;
+ trident_enable_voice_irq(card, chan_num);
+ trident_start_voice(card, chan_num);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+}
/* stop playback (lock held) */
-extern inline void __stop_dac(struct trident_state *state)
+extern __inline__ void __stop_dac(struct trident_state *state)
{
- struct trident_card *trident = state->card;
- trident_stop_voice(trident, state->dma_dac.chan);
- trident_disable_voice_irq(trident, state->dma_dac.chan);
- state->enable &= ~DAC_RUNNING;
-}
+ struct dmabuf *dmabuf = &state->dma_dac;
+ unsigned int chan_num = dmabuf->channel->num;
+ struct trident_card *card = state->card;
-extern inline void stop_dac(struct trident_state *state)
+ dmabuf->enable &= ~DMA_RUNNING;
+ trident_stop_voice(card, chan_num);
+ trident_disable_voice_irq(card, chan_num);
+}
+
+static void stop_dac(struct trident_state *state)
{
- struct trident_card *trident = state->card;
+ struct trident_card *card = state->card;
unsigned long flags;
- spin_lock_irqsave(&trident->lock, flags);
+ spin_lock_irqsave(&card->lock, flags);
__stop_dac(state);
- spin_unlock_irqrestore(&trident->lock, flags);
+ spin_unlock_irqrestore(&card->lock, flags);
}
static void start_dac(struct trident_state *state)
{
+ struct dmabuf *dmabuf = &state->dma_dac;
+ unsigned int chan_num = dmabuf->channel->num;
+ struct trident_card *card = state->card;
unsigned long flags;
- struct trident_card *trident = state->card;
- spin_lock_irqsave(&state->card->lock, flags);
- if ((state->dma_dac.mapped || state->dma_dac.count > 0) && state->dma_dac.ready) {
- state->enable |= DAC_RUNNING;
- trident_enable_voice_irq(trident, state->dma_dac.chan);
- trident_start_voice(trident, state->dma_dac.chan);
+ spin_lock_irqsave(&card->lock, flags);
+ if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
+ dmabuf->enable |= DMA_RUNNING;
+ trident_enable_voice_irq(card, chan_num);
+ trident_start_voice(card, chan_num);
}
- spin_unlock_irqrestore(&state->card->lock, flags);
-}
-
-static void start_adc(struct trident_state *s)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&s->card->lock, flags);
- 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;
- trident_enable_voice_irq(s->card, s->dma_adc.chan);
- outb(s->bDMAStart, TRID_REG(s->card, T4D_SBCTRL_SBE2R_SBDD));
- trident_start_voice(s->card, s->dma_adc.chan);
-#ifdef DEBUG
- printk("(trident) starting ADC\n");
-#endif
- }
- spin_unlock_irqrestore(&s->card->lock, flags);
-}
+ spin_unlock_irqrestore(&card->lock, flags);
+}
#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
#define DMABUF_MINORDER 1
@@ -1623,10 +823,16 @@ static void start_adc(struct trident_state *s)
/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
static int alloc_dmabuf(struct trident_state *state, unsigned rec)
{
+ struct dmabuf *dmabuf;
void *rawbuf;
int order;
unsigned long map, mapend;
+ if (rec)
+ dmabuf = &state->dma_adc;
+ else
+ dmabuf = &state->dma_dac;
+
/* alloc as big a chunk as we can, FIXME: is this necessary ?? */
for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
if ((rawbuf = (void *)__get_free_pages(GFP_KERNEL, order)))
@@ -1634,7 +840,7 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec)
if (!rawbuf)
return -ENOMEM;
#ifdef DEBUG
- printk("trident: allocated %ld (%d) bytes at %p\n",
+ printk("trident: allocated %ld (order = %d) bytes at %p\n",
PAGE_SIZE << order, order, rawbuf);
#endif
@@ -1648,17 +854,10 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec)
return -ENOMEM;
}
- if (rec) {
- state->dma_adc.ready = state->dma_adc.mapped = 0;
- state->dma_adc.rawbuf = rawbuf;
- state->dma_adc.buforder = order;
- }
- else {
- state->dma_dac.ready = state->dma_dac.mapped = 0;
- state->dma_dac.rawbuf = rawbuf;
- state->dma_dac.buforder = order;
- }
-
+ dmabuf->ready = dmabuf->mapped = 0;
+ dmabuf->rawbuf = rawbuf;
+ dmabuf->buforder = order;
+
/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1);
for (map = MAP_NR(rawbuf); map <= mapend; map++)
@@ -1668,180 +867,237 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec)
}
/* free DMA buffer */
-static void dealloc_dmabuf(struct dmabuf *db)
+static void dealloc_dmabuf(struct dmabuf *dmabuf)
{
unsigned long map, mapend;
- if (db->rawbuf) {
+ if (dmabuf->rawbuf) {
/* undo marking the pages as reserved */
- mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
- for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+ mapend = MAP_NR(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
+ for (map = MAP_NR(dmabuf->rawbuf); map <= mapend; map++)
clear_bit(PG_reserved, &mem_map[map].flags);
- free_pages((unsigned long)db->rawbuf, db->buforder);
+ free_pages((unsigned long)dmabuf->rawbuf, dmabuf->buforder);
}
- db->rawbuf = NULL;
- db->mapped = db->ready = 0;
+ dmabuf->rawbuf = NULL;
+ dmabuf->mapped = dmabuf->ready = 0;
}
static int prog_dmabuf(struct trident_state *state, unsigned rec)
{
- struct dmabuf *db = rec ? &state->dma_adc : &state->dma_dac;
- unsigned rate = rec ? state->rateadc : state->ratedac;
+ struct dmabuf *dmabuf;
unsigned bytepersec;
- unsigned bufs;
- unsigned char fmt;
+ unsigned bufsize;
unsigned long flags;
int ret;
+ if (rec)
+ dmabuf = &state->dma_adc;
+ else
+ dmabuf = &state->dma_dac;
+
spin_lock_irqsave(&state->card->lock, flags);
- fmt = state->fmt;
- if (rec) {
- state->enable &= ~TRIDENT_ENABLE_RE;
- fmt >>= TRIDENT_ADC_SHIFT;
- } else {
- state->enable &= ~TRIDENT_ENABLE_PE;
- fmt >>= TRIDENT_DAC_SHIFT;
- }
+ dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->error = dmabuf->endcleared = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
- fmt &= TRIDENT_FMT_MASK;
-
- db->hwptr = db->swptr = db->total_bytes = 0;
- db->count = db->error = db->endcleared = 0;
-
/* allocate DMA buffer if not allocated yet */
- if (!db->rawbuf)
+ if (!dmabuf->rawbuf)
if ((ret = alloc_dmabuf(state, rec)))
return ret;
- bytepersec = rate << sample_shift[fmt];
- bufs = PAGE_SIZE << db->buforder;
- if (db->ossfragshift) {
- if ((1000 << db->ossfragshift) < bytepersec)
- db->fragshift = ld2(bytepersec/1000);
+ /* FIXME: figure out all this OSS fragment stuff */
+ bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
+ bufsize = PAGE_SIZE << dmabuf->buforder;
+ if (dmabuf->ossfragshift) {
+ if ((1000 << dmabuf->ossfragshift) < bytepersec)
+ dmabuf->fragshift = ld2(bytepersec/1000);
else
- db->fragshift = db->ossfragshift;
+ dmabuf->fragshift = dmabuf->ossfragshift;
} else {
/* lets hand out reasonable big ass buffers by default */
- db->fragshift = (db->buforder + PAGE_SHIFT -2);
+ dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
}
- db->numfrag = bufs >> db->fragshift;
- while (db->numfrag < 4 && db->fragshift > 3) {
- db->fragshift--;
- db->numfrag = bufs >> db->fragshift;
+ dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+ while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
+ dmabuf->fragshift--;
+ dmabuf->numfrag = bufsize >> dmabuf->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;
+ dmabuf->fragsize = 1 << dmabuf->fragshift;
+ if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)
+ dmabuf->numfrag = dmabuf->ossmaxfrags;
+ dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];
+ dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
- memset(db->rawbuf, (fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, db->dmasize);
+ memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80,
+ dmabuf->dmasize);
spin_lock_irqsave(&state->card->lock, flags);
if (rec) {
- trident_rec_setup(state, fmt, state->rateadc,
- db->rawbuf, db->numfrag << db->fragshift);
+ trident_rec_setup(state);
} else {
- trident_play_setup(state, fmt, state->ratedac,
- db->rawbuf, db->numfrag << db->fragshift);
+ trident_play_setup(state);
}
spin_unlock_irqrestore(&state->card->lock, flags);
/* set the ready flag for the dma buffer */
- db->ready = 1;
+ dmabuf->ready = 1;
#ifdef DEBUG
printk("trident: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, "
"fragsize = %d dmasize = %d\n",
- rate, fmt, db->numfrag, db->fragsize, db->dmasize);
+ dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
+ dmabuf->fragsize, dmabuf->dmasize);
#endif
return 0;
}
-extern __inline__ void clear_advance(struct trident_state *s)
+/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e.
+ |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx|
+ but we almost always get this
+ |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------|
+ so we have to clear the tail space to "silence"
+ |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000|
+*/
+static void trident_clear_tail(struct trident_state *state)
{
- unsigned char c = ((s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_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;
+ struct dmabuf *dmabuf = &state->dma_dac;
+ unsigned swptr;
+ unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80;
+ unsigned int len;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->card->lock, flags);
+ swptr = dmabuf->swptr;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+
+ if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize)
+ return;
+
+
+ if (swptr < dmabuf->dmasize/2)
+ len = dmabuf->dmasize/2 - swptr;
+ else
+ len = dmabuf->dmasize - swptr;
+
+ memset(dmabuf->rawbuf + swptr, silence, len);
+
+ spin_lock_irqsave(&state->card->lock, flags);
+ dmabuf->swptr += len;
+ dmabuf->count += len;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+
+ /* restart the dma machine in case it is halted */
+ start_dac(state);
+}
+
+static int drain_dac(struct trident_state *state, int nonblock)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct dmabuf *dmabuf = &state->dma_dac;
+ unsigned long flags;
+ unsigned long tmo;
+ int count;
+
+ if (dmabuf->mapped || !dmabuf->ready)
+ 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;
+ }
+
+ /* No matter how much data left in the buffer, we have to wait untill
+ CSO == ESO/2 or CSO == ESO when address engine interrupts */
+ tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
+ tmo >>= sample_shift[dmabuf->fmt];
+ if (!schedule_timeout(tmo ? tmo : 1) && tmo){
+ printk(KERN_ERR "trident: drain_dac, dma timeout?\n");
+ break;
+ }
}
- memset(buf + bptr, c, len);
+ remove_wait_queue(&dmabuf->wait, &wait);
+ current->state = TASK_RUNNING;
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ return 0;
}
/* call with spinlock held! */
-static void trident_update_ptr(struct trident_state *s)
+static void trident_update_ptr(struct trident_state *state)
{
+ struct dmabuf *dmabuf;
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))) {
- s->enable &= ~TRIDENT_ENABLE_RE;
- __stop_adc(s);
- s->dma_adc.error++;
+ if (state->dma_adc.ready) {
+ dmabuf = &state->dma_adc;
+ hwptr = trident_get_dma_addr(state, 1);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
+ dmabuf->count += diff;
+
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
+ wake_up(&dmabuf->wait);
+ if (!dmabuf->mapped) {
+ if (dmabuf->count > (signed)(dmabuf->dmasize - ((3 * dmabuf->fragsize) >> 1))) {
+ __stop_adc(state);
+ dmabuf->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;
- 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);
- }
+ if (state->dma_dac.ready) {
+ dmabuf = &state->dma_dac;
+ hwptr = trident_get_dma_addr(state, 0);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
+
+ if (dmabuf->mapped) {
+ dmabuf->count += diff;
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
+ wake_up(&dmabuf->wait);
+ }
else {
- s->dma_dac.count -= diff;
- if (s->dma_dac.count <= 0) {
- s->enable &= ~TRIDENT_ENABLE_PE;
- /* Lock already held */
- __stop_dac(s);
- /* brute force everyone back in sync, sigh */
- s->dma_dac.count = 0;
- s->dma_dac.swptr = 0;
- s->dma_dac.hwptr = 0;
- 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;
+ dmabuf->count -= diff;
+ if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
+ /* buffer underrun or buffer overrun, we have no way to recover
+ it here, just stop the machine and let the process force hwptr
+ and swptr to sync */
+ __stop_dac(state);
+ dmabuf->error++;
}
-
- if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
- wake_up(&s->dma_dac.wait);
+ /* since dma machine only interrupts at ESO and ESO/2, we sure have at
+ least half of dma buffer free, so wake up the process unconditionally */
+ wake_up(&dmabuf->wait);
}
}
}
-/*
- * Trident interrupt handlers.
- */
static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct trident_state *state;
@@ -1858,19 +1114,17 @@ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (event & ADDRESS_IRQ) {
/* Update the pointers for all channels we are running. */
- /* FIXME: improve interrupt latency !!! */
- for (i = 0; i < NR_DSPS; i++) {
- state = card->channels[i];
+ /* FIXME: should read interrupt status only once */
+ for (i = 0; i < NR_HW_CH; i++) {
if (trident_check_channel_interrupt(card, 63 - i)) {
trident_ack_channel_interrupt(card, 63 - i);
- if (state != NULL)
+ if ((state = card->states[i]) != NULL) {
trident_update_ptr(state);
- else {
- /* Spurious ? */
- printk("trident: spurious channel irq %d.\n",
+ } else {
+ printk("trident: spurious channel irq %d.\n",
63 - i);
trident_stop_voice(card, i);
- trident_disable_voice_irq(card, i);
+ trident_disable_voice_irq(card, i);
}
}
}
@@ -1886,254 +1140,29 @@ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
spin_unlock(&card->lock);
}
-static void set_mixer(struct trident_card *card,unsigned int mixer, unsigned int val )
-{
- unsigned int left,right;
-
- /* cleanse input a little */
- right = ((val >> 8) & 0xff) ;
- left = (val & 0xff) ;
-
- if (right > 100) right = 100;
- if (left > 100) left = 100;
-
- card->mix.mixer_state[mixer] = (right << 8) | left;
- card->mix.write_mixer(card, mixer, left, right);
-}
-
-static int mixer_ioctl(struct trident_card *card, unsigned int cmd, unsigned long arg)
-{
- unsigned long flags;
- int i, val = 0;
-
- VALIDATE_CARD(card);
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- strncpy(info.id, card->pci_info->name, sizeof(info.id));
- strncpy(info.name, card->pci_info->name, sizeof(info.name));
- info.modify_counter = card->mix.modcnt;
- if (copy_to_user((void *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- strncpy(info.id, card->pci_info->name, sizeof(info.id));
- strncpy(info.name, card->pci_info->name, sizeof(info.name));
- if (copy_to_user((void *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
-
- if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int *)arg);
-
- if (_IOC_DIR(cmd) == _IOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* give them the current record source */
- if (!card->mix.recmask_io) {
- val = 0;
- } else {
- spin_lock_irqsave(&card->lock, flags);
- val = card->mix.recmask_io(card,1,0);
- spin_unlock_irqrestore(&card->lock, flags);
- }
- break;
-
- case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
- val = card->mix.supported_mixers;
- break;
-
- case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
- val = card->mix.record_sources;
- break;
-
- case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
- val = card->mix.stereo_mixers;
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default: /* read a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(card,i))
- return -EINVAL;
-
- /* do we ever want to touch the hardware? */
- /* spin_lock_irqsave(&s->lock, flags);
- val = card->mix.read_mixer(card,i);
- spin_unlock_irqrestore(&s->lock, flags);*/
-
- val = card->mix.mixer_state[i];
- /* printk("returned 0x%x for mixer %d\n",val,i);*/
- break;
- }
- return put_user(val,(int *)arg);
- }
-
- if (_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) {
- card->mix.modcnt++;
- get_user_ret(val, (int *)arg, -EFAULT);
-
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- if (!card->mix.recmask_io) return -EINVAL;
- if (!(val &= card->mix.record_sources)) return -EINVAL;
-
- spin_lock_irqsave(&card->lock, flags);
- card->mix.recmask_io(card, 0, val);
- spin_unlock_irqrestore(&card->lock, flags);
-
- return 0;
- default: /* write a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(card, i))
- return -EINVAL;
-
- spin_lock_irqsave(&card->lock, flags);
- set_mixer(card, i, val);
- spin_unlock_irqrestore(&card->lock, flags);
-
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int trident_open_mixdev(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- struct trident_card *card = devs;
-
- while (card && card->dev_mixer != minor)
- card = card->next;
- if (!card)
- return -ENODEV;
-
- file->private_data = card;
-
- //FIXME put back in
- //MOD_INC_USE_COUNT;
- return 0;
-}
-
-static int trident_release_mixdev(struct inode *inode, struct file *file)
-{
- struct trident_card *card = (struct trident_card *)file->private_data;
-
- VALIDATE_CARD(card);
-
- //FIXME put back in
- //MOD_DEC_USE_COUNT;
- return 0;
-}
-
-static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct trident_card *card = (struct trident_card *)file->private_data;
-
- VALIDATE_CARD(card);
- return mixer_ioctl(card, cmd, arg);
-}
-
static loff_t trident_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
-static /*const*/ struct file_operations trident_mixer_fops = {
- &trident_llseek,
- NULL, /* read */
- NULL, /* write */
- NULL, /* readdir */
- NULL, /* poll */
- &trident_ioctl_mixdev,
- NULL, /* mmap */
- &trident_open_mixdev,
- NULL, /* flush */
- &trident_release_mixdev,
- NULL, /* fsync */
- NULL, /* fasync */
- NULL, /* lock */
-};
-
-/* drain the DAC buffer
- FIXME: This function will block (forever ??) when using
- XMMS Qsound plugin and direct cat sample.wav > /dev/dsp
- This behavior is when drain_dac is called by trident_release. */
-static int drain_dac(struct trident_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;
-
- 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);
- current->state = TASK_RUNNING;
- return -EBUSY;
- }
-
- tmo = (count * HZ) / s->ratedac;
- tmo >>= sample_shift[(s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_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)
- printk(KERN_ERR "trident: drain_dac, "
- "dma timed out? jiffies = %ld\n",
- jiffies);
- }
- remove_wait_queue(&s->dma_dac.wait, &wait);
- current->state = TASK_RUNNING;
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- return 0;
-}
-
/* in this loop, dma_adc.count signifies the amount of data thats waiting
to be copied to the user's buffer. it is filled by the interrupt
handler and drained by this loop. */
static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dma_dac;
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
-
+
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
return -ESPIPE;
- if (state->dma_adc.mapped)
+ if (dmabuf->mapped)
return -ENXIO;
- if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1)))
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
return ret;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
@@ -2141,12 +1170,10 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
- /* remember, all these things are expressed in bytes to be
- sent to the user.. hence the evil / 2 down below */
- swptr = state->dma_adc.swptr;
- cnt = state->dma_adc.dmasize - swptr;
- if (state->dma_adc.count < cnt)
- cnt = state->dma_adc.count;
+ swptr = dmabuf->swptr;
+ cnt = dmabuf->dmasize - swptr;
+ if (dmabuf->count < cnt)
+ cnt = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
if (cnt > count)
@@ -2158,20 +1185,17 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_
ret = ret ? ret : -EAGAIN;
return ret;
}
- if (!interruptible_sleep_on_timeout(&state->dma_adc.wait, HZ)) {
- printk(KERN_DEBUG "(trident) read: chip lockup? "
+ if (!interruptible_sleep_on_timeout(&dmabuf->wait, HZ)) {
+ printk(KERN_ERR
+ "(trident) read: chip lockup? "
"dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
- state->dma_adc.dmasize,
- state->dma_adc.fragsize,
- state->dma_adc.count,
- state->dma_adc.hwptr,
- state->dma_adc.swptr);
+ dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+ dmabuf->hwptr, dmabuf->swptr);
stop_adc(state);
-
spin_lock_irqsave(&state->card->lock, flags);
- state->dma_adc.count = 0;
- state->dma_adc.hwptr = 0;
- state->dma_adc.swptr = 0;
+ dmabuf->count = 0;
+ dmabuf->hwptr = 0;
+ dmabuf->swptr = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
}
if (signal_pending(current)) {
@@ -2180,17 +1204,19 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_
}
continue;
}
-
- if (copy_to_user(buffer, state->dma_adc.rawbuf + swptr, cnt)) {
+
+ if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
ret = ret ? ret : -EFAULT;
return ret;
}
- swptr = (swptr + cnt) % state->dma_adc.dmasize;
+ swptr = (swptr + cnt) % dmabuf->dmasize;
+
spin_lock_irqsave(&state->card->lock, flags);
- state->dma_adc.swptr = swptr;
- state->dma_adc.count -= cnt;
+ dmabuf->swptr = swptr;
+ dmabuf->count -= cnt;
spin_unlock_irqrestore(&state->card->lock, flags);
+
count -= cnt;
buffer += cnt;
ret += cnt;
@@ -2203,11 +1229,11 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_
static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dma_dac;
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
- int mode = (state->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_MASK;
#ifdef DEBUG
printk("trident: trident_write called, count = %d\n", count);
@@ -2216,9 +1242,9 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
return -ESPIPE;
- if (state->dma_dac.mapped)
+ if (dmabuf->mapped)
return -ENXIO;
- if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0)))
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
return ret;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
@@ -2226,41 +1252,47 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
- if (state->dma_dac.count < 0) {
- state->dma_dac.count = 0;
- state->dma_dac.swptr = state->dma_dac.hwptr;
+ if (dmabuf->count < 0) {
+ /* buffer underrun, we are recovering from sleep_on_timeout,
+ resync hwptr and swptr */
+ dmabuf->count = 0;
+ dmabuf->swptr = dmabuf->hwptr;
}
- swptr = state->dma_dac.swptr;
- cnt = state->dma_dac.dmasize - swptr;
- if (state->dma_dac.count + cnt > state->dma_dac.dmasize)
- cnt = state->dma_dac.dmasize - state->dma_dac.count;
+ swptr = dmabuf->swptr;
+ cnt = dmabuf->dmasize - swptr;
+ if (dmabuf->count + cnt > dmabuf->dmasize)
+ cnt = dmabuf->dmasize - dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
- /* buffer is full, wait for it to be played */
+ unsigned long tmo;
+ /* 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;
return ret;
}
- if (!interruptible_sleep_on_timeout(&state->dma_dac.wait, HZ)) {
- printk(KERN_ERR
- "trident: write: chip lockup? "
- "dmasz %u fragsz %u count %i "
- "hwptr %u swptr %u\n",
- state->dma_dac.dmasize,
- state->dma_dac.fragsize,
- state->dma_dac.count,
- state->dma_dac.hwptr,
- state->dma_dac.swptr);
- stop_dac(state);
- spin_lock_irqsave(&state->card->lock, flags);
- state->dma_dac.count = 0;
- state->dma_dac.hwptr = 0;
- state->dma_dac.swptr = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ /* No matter how much data left in the buffer, we have to wait untill
+ CSO == ESO/2 or CSO == ESO when address engine interrupts */
+ tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
+ tmo >>= sample_shift[dmabuf->fmt];
+ /* There are two situations when sleep_on_timeout returns, one is when the
+ interrupt is serviced correctly and the process is waked up by ISR ON TIME.
+ Another is when timeout is expired, which means that either interrupt is NOT
+ serviced correctly (pending interrupt) or it is TOO LATE for the process to
+ be scheduled to run (scheduler latency) which results in a (potential) buffer
+ underrun. And worse, there is NOTHING we can do to prevent it. */
+ if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
+#ifdef DEBUG
+ printk(KERN_ERR "trident: schedule timeout, "
+ "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+ dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+ dmabuf->hwptr, dmabuf->swptr);
+#endif
+ /* a buffer underrun, we delay the recovery untill next time the
+ while loop begin and we REALLY have data to play */
}
if (signal_pending(current)) {
if (!ret) ret = -ERESTARTSYS;
@@ -2268,18 +1300,17 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count
}
continue;
}
- if (copy_from_user(state->dma_dac.rawbuf + swptr, buffer, cnt)) {
- if (!ret)
- ret = -EFAULT;
+ if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+ if (!ret) ret = -EFAULT;
return ret;
}
- swptr = (swptr + cnt) % state->dma_dac.dmasize;
+ swptr = (swptr + cnt) % dmabuf->dmasize;
spin_lock_irqsave(&state->card->lock, flags);
- state->dma_dac.swptr = swptr;
- state->dma_dac.count += cnt;
- state->dma_dac.endcleared = 0;
+ dmabuf->swptr = swptr;
+ dmabuf->count += cnt;
+ dmabuf->endcleared = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
count -= cnt;
@@ -2304,7 +1335,6 @@ static unsigned int trident_poll(struct file *file, struct poll_table_struct *wa
spin_lock_irqsave(&s->card->lock, flags);
trident_update_ptr(s);
-
if (file->f_mode & FMODE_READ) {
if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
mask |= POLLIN | POLLRDNORM;
@@ -2319,56 +1349,56 @@ static unsigned int trident_poll(struct file *file, struct poll_table_struct *wa
}
}
spin_unlock_irqrestore(&s->card->lock, flags);
+
return mask;
}
-/* this needs to be fixed to deal with the dual apus/buffers */
-#if 0
static int trident_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct trident_state *s = (struct trident_state *)file->private_data;
- struct dmabuf *db;
+ struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf;
int ret;
unsigned long size;
- VALIDATE_STATE(s);
+ VALIDATE_STATE(state);
if (vma->vm_flags & VM_WRITE) {
- if ((ret = prog_dmabuf(s, 0)) != 0)
+ if ((ret = prog_dmabuf(state, 0)) != 0)
return ret;
- db = &s->dma_dac;
+ dmabuf = &state->dma_dac;
} else if (vma->vm_flags & VM_READ) {
- if ((ret = prog_dmabuf(s, 1)) != 0)
+ if ((ret = prog_dmabuf(state, 1)) != 0)
return ret;
- db = &s->dma_adc;
+ dmabuf = &state->dma_adc;
} else
return -EINVAL;
- if (vma->vm_offset != 0)
+
+ if (vma->vm_pgoff != 0)
return -EINVAL;
size = vma->vm_end - vma->vm_start;
- if (size > (PAGE_SIZE << db->buforder))
+ if (size > (PAGE_SIZE << dmabuf->buforder))
return -EINVAL;
- if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+ if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),
+ size, vma->vm_page_prot))
return -EAGAIN;
- db->mapped = 1;
+ dmabuf->mapped = 1;
+
return 0;
}
-#endif
static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct trident_state *s = (struct trident_state *)file->private_data;
+ struct trident_state *state = (struct trident_state *)file->private_data;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int val, mapped, ret;
- unsigned char fmtm, fmtd;
- VALIDATE_STATE(s);
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+ VALIDATE_STATE(state);
+ mapped = ((file->f_mode & FMODE_WRITE) && state->dma_dac.mapped) ||
+ ((file->f_mode & FMODE_READ) && state->dma_adc.mapped);
#ifdef DEBUG
- printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",_IOC_NR(cmd),
- arg ? *(int *)arg : 0);
+ printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
+ _IOC_NR(cmd), arg ? *(int *)arg : 0);
#endif
switch (cmd)
@@ -2378,189 +1408,218 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm
case SNDCTL_DSP_RESET:
if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
+ stop_dac(state);
synchronize_irq();
- s->dma_dac.swptr = s->dma_dac.hwptr = 0;
- s->dma_dac.count = s->dma_dac.total_bytes = 0;
+ state->dma_dac.ready = 0;
+ state->dma_dac.swptr = state->dma_dac.hwptr = 0;
+ state->dma_dac.count = state->dma_dac.total_bytes = 0;
}
if (file->f_mode & FMODE_READ) {
- stop_adc(s);
+ stop_adc(state);
synchronize_irq();
- s->dma_adc.swptr = s->dma_adc.hwptr = 0;
- s->dma_adc.count = s->dma_adc.total_bytes = 0;
+ state->dma_adc.ready = 0;
+ state->dma_adc.swptr = state->dma_adc.hwptr = 0;
+ state->dma_adc.count = state->dma_adc.total_bytes = 0;
}
return 0;
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE)
- return drain_dac(s, file->f_flags & O_NONBLOCK);
+ return drain_dac(state, file->f_flags & O_NONBLOCK);
return 0;
- case SNDCTL_DSP_SPEED:
+ case SNDCTL_DSP_SPEED: /* set smaple rate */
get_user_ret(val, (int *)arg, -EFAULT);
if (val >= 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- trident_set_adc_rate(s, val, 1);
- }
if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
- trident_set_dac_rate(s, val, 1);
+ stop_dac(state);
+ state->dma_dac.ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_set_dac_rate(state, val);
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(state);
+ state->dma_adc.ready = 0;
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_set_adc_rate(state, val);
+ spin_unlock_irqrestore(&state->card->lock, flags);
}
}
- return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac,
+ return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate :
+ state->dma_dac.rate,
(int *)arg);
- case SNDCTL_DSP_STEREO:
+ case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
get_user_ret(val, (int *)arg, -EFAULT);
- fmtd = 0;
- fmtm = ~0;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(state);
+ state->dma_dac.ready = 0;
if (val)
- fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT;
+ state->dma_dac.fmt |= TRIDENT_FMT_STEREO;
else
- fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT);
+ state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO;
}
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(state);
+ state->dma_adc.ready = 0;
if (val)
- fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT;
+ state->dma_adc.fmt |= TRIDENT_FMT_STEREO;
else
- fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT);
+ state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO;
}
- set_fmt(s, fmtm, fmtd);
return 0;
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
- if ((val = prog_dmabuf(s, 0)))
+ if ((val = prog_dmabuf(state, 0)))
return val;
- return put_user(s->dma_dac.fragsize, (int *)arg);
+ return put_user(state->dma_dac.fragsize, (int *)arg);
+ }
+ if (file->f_mode & FMODE_READ) {
+ if ((val = prog_dmabuf(state, 1)))
+ return val;
+ return put_user(state->dma_adc.fragsize, (int *)arg);
}
- if ((val = prog_dmabuf(s, 1)))
- return val;
- return put_user(s->dma_adc.fragsize, (int *)arg);
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_S8|AFMT_S16_LE, (int *)arg);
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+ case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
+ return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
+
+ case SNDCTL_DSP_SETFMT: /* Select sample format */
get_user_ret(val, (int *)arg, -EFAULT);
if (val != AFMT_QUERY) {
- fmtd = 0;
- fmtm = ~0;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- /* fixed at 16bit for now */
- fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT;
-#if 0
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(state);
+ state->dma_dac.ready = 0;
if (val == AFMT_S16_LE)
- fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT;
+ state->dma_dac.fmt |= TRIDENT_FMT_16BIT;
else
- fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT);
-#endif
+ state->dma_dac.fmt &= ~TRIDENT_FMT_16BIT;
}
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(state);
+ state->dma_adc.ready = 0;
if (val == AFMT_S16_LE)
- fmtd |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT;
+ state->dma_adc.fmt |= TRIDENT_FMT_16BIT;
else
- fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT);
+ state->dma_adc.fmt &= ~TRIDENT_FMT_16BIT;
}
- set_fmt(s, fmtm, fmtd);
}
- return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
- (TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) :
- (TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ?
- AFMT_S16_LE : AFMT_S8, (int *)arg);
+ if (file->f_mode & FMODE_WRITE)
+ return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ?
+ AFMT_S16_LE : AFMT_U8, (int *)arg);
+ else
+ return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ?
+ AFMT_S16_LE : AFMT_U8, (int *)arg);
case SNDCTL_DSP_CHANNELS:
get_user_ret(val, (int *)arg, -EFAULT);
if (val != 0) {
- fmtd = 0;
- fmtm = ~0;
-
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(state);
+ state->dma_dac.ready = 0;
if (val >= 2)
- fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT;
+ state->dma_dac.fmt |= TRIDENT_FMT_STEREO;
else
- fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT);
+ state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO;
}
-
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(state);
+ state->dma_adc.ready = 0;
if (val >= 2)
- fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT;
+ state->dma_adc.fmt |= TRIDENT_FMT_STEREO;
else
- fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT);
+ state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO;
}
- set_fmt(s, fmtm, fmtd);
}
- return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
- (TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) :
- (TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+ if (file->f_mode & FMODE_WRITE)
+ return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ (int *)arg);
+ else
+ return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ (int *)arg);
case SNDCTL_DSP_POST:
+ /* FIXME: the same as RESET ?? */
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))
+ if ((file->f_mode & FMODE_READ && state->dma_adc.subdivision) ||
+ (file->f_mode & FMODE_WRITE && state->dma_dac.subdivision))
return -EINVAL;
get_user_ret(val, (int *)arg, -EFAULT);
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
if (file->f_mode & FMODE_READ)
- s->dma_adc.subdivision = val;
+ state->dma_adc.subdivision = val;
if (file->f_mode & FMODE_WRITE)
- s->dma_dac.subdivision = val;
+ state->dma_dac.subdivision = val;
return 0;
case SNDCTL_DSP_SETFRAGMENT:
get_user_ret(val, (int *)arg, -EFAULT);
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;
+ state->dma_adc.ossfragshift = val & 0xffff;
+ state->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+ if (state->dma_adc.ossfragshift < 4)
+ state->dma_adc.ossfragshift = 4;
+ if (state->dma_adc.ossfragshift > 15)
+ state->dma_adc.ossfragshift = 15;
+ if (state->dma_adc.ossmaxfrags < 4)
+ state->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;
+ state->dma_dac.ossfragshift = val & 0xffff;
+ state->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+ if (state->dma_dac.ossfragshift < 4)
+ state->dma_dac.ossfragshift = 4;
+ if (state->dma_dac.ossfragshift > 15)
+ state->dma_dac.ossfragshift = 15;
+ if (state->dma_dac.ossmaxfrags < 4)
+ state->dma_dac.ossmaxfrags = 4;
}
return 0;
- case SNDCTL_DSP_GETCAPS:
- return put_user(0/* DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP */,
- (int *)arg);
-
- case SNDCTL_DSP_SETDUPLEX:
- /* XXX fix */
+ case SNDCTL_DSP_GETOSPACE:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ if (!state->dma_dac.enable && (val = prog_dmabuf(state, 0)) != 0)
+ return val;
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_update_ptr(state);
+ abinfo.fragsize = state->dma_dac.fragsize;
+ abinfo.bytes = state->dma_dac.dmasize - state->dma_dac.count;
+ abinfo.fragstotal = state->dma_dac.numfrag;
+ abinfo.fragments = abinfo.bytes >> state->dma_dac.fragshift;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+ case SNDCTL_DSP_GETISPACE:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ if (!state->dma_adc.enable && (val = prog_dmabuf(state, 1)) != 0)
+ return val;
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_update_ptr(state);
+ abinfo.fragsize = state->dma_adc.fragsize;
+ abinfo.bytes = state->dma_adc.count;
+ abinfo.fragstotal = state->dma_adc.numfrag;
+ abinfo.fragments = abinfo.bytes >> state->dma_adc.fragshift;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
return 0;
+ case SNDCTL_DSP_GETCAPS:
+ return put_user(/* DSP_CAP_DUPLEX|*/DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
+ (int *)arg);
+
case SNDCTL_DSP_GETTRIGGER:
val = 0;
- if (file->f_mode & FMODE_READ && s->enable & TRIDENT_ENABLE_RE)
+ if (file->f_mode & FMODE_READ && state->dma_adc.enable)
val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && s->enable & TRIDENT_ENABLE_PE)
+ if (file->f_mode & FMODE_WRITE && state->dma_dac.enable)
val |= PCM_ENABLE_OUTPUT;
return put_user(val, (int *)arg);
@@ -2568,106 +1627,86 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm
get_user_ret(val, (int *)arg, -EFAULT);
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
- if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+ if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1)))
return ret;
- start_adc(s);
+ start_adc(state);
}
else
- stop_adc(s);
+ stop_adc(state);
}
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT) {
- if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+ if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0)))
return ret;
- start_dac(s);
+ start_dac(state);
}
else
- stop_dac(s);
+ stop_dac(state);
}
return 0;
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if (!(s->enable & TRIDENT_ENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0)
- return val;
- spin_lock_irqsave(&s->card->lock, flags);
- trident_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(&s->card->lock, flags);
- return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- if (!(s->enable & TRIDENT_ENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0)
- return val;
- spin_lock_irqsave(&s->card->lock, flags);
- trident_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(&s->card->lock, flags);
- return copy_to_user((void *)arg, &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(&s->card->lock, flags);
- trident_update_ptr(s);
- val = s->dma_dac.count;
- spin_unlock_irqrestore(&s->card->lock, flags);
- return put_user(val, (int *)arg);
-
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
- spin_lock_irqsave(&s->card->lock, flags);
- trident_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(&s->card->lock, flags);
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_update_ptr(state);
+ cinfo.bytes = state->dma_adc.total_bytes;
+ cinfo.blocks = state->dma_adc.count >> state->dma_adc.fragshift;
+ cinfo.ptr = state->dma_adc.hwptr;
+ if (state->dma_adc.mapped)
+ state->dma_adc.count &= state->dma_adc.fragsize-1;
+ spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
- spin_lock_irqsave(&s->card->lock, flags);
- trident_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(&s->card->lock, flags);
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_update_ptr(state);
+ cinfo.bytes = state->dma_dac.total_bytes;
+ cinfo.blocks = state->dma_dac.count >> state->dma_dac.fragshift;
+ cinfo.ptr = state->dma_dac.hwptr;
+ if (state->dma_dac.mapped)
+ state->dma_dac.count &= state->dma_dac.fragsize-1;
+ spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+ case SNDCTL_DSP_SETDUPLEX:
+ /* XXX fix */
+ return 0;
+
+ case SNDCTL_DSP_GETODELAY:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_update_ptr(state);
+ val = state->dma_dac.count;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ return put_user(val, (int *)arg);
+
case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+ return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate :
+ state->dma_dac.rate, (int *)arg);
case SOUND_PCM_READ_CHANNELS:
- return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
- (TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) :
- (TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+ if (file->f_mode & FMODE_WRITE)
+ return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ (int *)arg);
+ else
+ return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ (int *)arg);
case SOUND_PCM_READ_BITS:
- return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
- (TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) :
- (TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ? 16 : 8, (int *)arg);
-
- case SOUND_PCM_WRITE_FILTER:
+ if (file->f_mode & FMODE_WRITE)
+ return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ?
+ AFMT_S16_LE : AFMT_U8, (int *)arg);
+ else
+ return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ?
+ AFMT_S16_LE : AFMT_U8, (int *)arg);
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
+ case SOUND_PCM_WRITE_FILTER:
case SOUND_PCM_READ_FILTER:
return -EINVAL;
@@ -2681,13 +1720,12 @@ static int trident_open(struct inode *inode, struct file *file)
int minor = MINOR(inode->i_rdev);
struct trident_card *card = devs;
struct trident_state *state = NULL;
- unsigned char fmtm = ~0, fmts = 0;
/* find an avaiable virtual channel (instance of /dev/dsp) */
while (card != NULL) {
- for (i = 0; i < NR_DSPS; i++) {
- if (card->channels[i] == NULL) {
- state = card->channels[i] = (struct trident_state *)
+ for (i = 0; i < NR_HW_CH; i++) {
+ if (card->states[i] == NULL) {
+ state = card->states[i] = (struct trident_state *)
kmalloc(sizeof(struct trident_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
@@ -2704,18 +1742,18 @@ static int trident_open(struct inode *inode, struct file *file)
found_virt:
/* found a free virtual channel, allocate hardware channels */
if (file->f_mode & FMODE_READ)
- if ((state->dma_adc.chan = trident_alloc_pcm_channel(card)) == -1) {
- kfree (card->channels[i]);
- card->channels[i] = NULL;;
+ if ((state->dma_adc.channel = trident_alloc_pcm_channel(card)) == NULL) {
+ kfree (card->states[i]);
+ card->states[i] = NULL;;
return -ENODEV;
}
if (file->f_mode & FMODE_WRITE)
- if ((state->dma_dac.chan = trident_alloc_pcm_channel(card)) == -1) {
- kfree (card->channels[i]);
- card->channels[i] = NULL;
+ if ((state->dma_dac.channel = trident_alloc_pcm_channel(card)) == NULL) {
+ kfree (card->states[i]);
+ card->states[i] = NULL;
if (file->f_mode & FMODE_READ)
/* free previously allocated hardware channel */
- trident_free_pcm_channel(card, state->dma_adc.chan);
+ trident_free_pcm_channel(card, state->dma_adc.channel->num);
return -ENODEV;
}
@@ -2732,30 +1770,27 @@ static int trident_open(struct inode *inode, struct file *file)
/* set default sample format, Refer to OSS Programmer's Guide */
if (file->f_mode & FMODE_READ) {
- /* fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT);
- if ((minor & 0xf) == SND_DEV_DSP16)
- fmts |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; */
- fmtm = (TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT;
+ /* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample */
+ state->dma_adc.fmt = TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT;
state->dma_adc.ossfragshift = 0;
state->dma_adc.ossmaxfrags = 0;
state->dma_adc.subdivision = 0;
- trident_set_adc_rate(state, 8000, 0);
+ trident_set_adc_rate(state, 48000);
}
/* according to OSS document, /dev/dsp should be default to unsigned 8-bits,
mono, with sample rate 8kHz and /dev/dspW will accept 16-bits sample */
if (file->f_mode & FMODE_WRITE) {
- fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_DAC_SHIFT);
+ state->dma_dac.fmt &= ~TRIDENT_FMT_MASK;
if ((minor & 0xf) == SND_DEV_DSP16)
- fmts |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT;
+ state->dma_dac.fmt |= TRIDENT_FMT_16BIT;
state->dma_dac.ossfragshift = 0;
state->dma_dac.ossmaxfrags = 0;
state->dma_dac.subdivision = 0;
- trident_set_dac_rate(state, 8000, 1);
+ trident_set_dac_rate(state, 8000);
}
- set_fmt(state, fmtm, fmts);
- state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+ state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&state->open_sem);
//FIXME put back in
@@ -2768,8 +1803,12 @@ static int trident_release(struct inode *inode, struct file *file)
struct trident_state *state = (struct trident_state *)file->private_data;
VALIDATE_STATE(state);
- if (file->f_mode & FMODE_WRITE)
+
+
+ if (file->f_mode & FMODE_WRITE) {
+ trident_clear_tail(state);
drain_dac(state, file->f_flags & O_NONBLOCK);
+ }
/* stop DMA state machine and free DMA buffers/channels */
down(&state->open_sem);
@@ -2777,16 +1816,16 @@ static int trident_release(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dealloc_dmabuf(&state->dma_dac);
- trident_free_pcm_channel(state->card, state->dma_dac.chan);
+ trident_free_pcm_channel(state->card, state->dma_dac.channel->num);
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dealloc_dmabuf(&state->dma_adc);
- trident_free_pcm_channel(state->card, state->dma_adc.chan);
+ trident_free_pcm_channel(state->card, state->dma_adc.channel->num);
}
- kfree(state->card->channels[state->virt]);
- state->card->channels[state->virt] = NULL;
+ kfree(state->card->states[state->virt]);
+ state->card->states[state->virt] = NULL;
state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
/* we're covered by the open_sem */
@@ -2796,6 +1835,7 @@ static int trident_release(struct inode *inode, struct file *file)
//MOD_DEC_USE_COUNT;
return 0;
}
+
static /*const*/ struct file_operations trident_audio_fops = {
&trident_llseek,
&trident_read,
@@ -2803,7 +1843,7 @@ static /*const*/ struct file_operations trident_audio_fops = {
NULL, /* readdir */
&trident_poll,
&trident_ioctl,
- NULL, /* XXX &trident_mmap, */
+ &trident_mmap,
&trident_open,
NULL, /* flush */
&trident_release,
@@ -2812,15 +1852,238 @@ static /*const*/ struct file_operations trident_audio_fops = {
NULL, /* lock */
};
-/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered
- untill open time */
-static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_info)
+/* trident specific AC97 functions */
+/* Write AC97 mixer registers */
+static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val)
+{
+ struct trident_card *card = (struct trident_card *)codec->private_data;
+ unsigned int address, mask, busy;
+ unsigned short count = 0xffff;
+ unsigned long flags;
+ u32 data;
+
+ data = ((u32) val) << 16;
+
+ switch (card->pci_id)
+ {
+ default:
+ case PCI_DEVICE_ID_SI_7018:
+ address = SI_AC97_WRITE;
+ mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY;
+ if (codec->id)
+ mask |= SI_AC97_SECONDARY;
+ busy = SI_AC97_BUSY_WRITE;
+ break;
+ case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+ address = DX_ACR0_AC97_W;
+ mask = busy = DX_AC97_BUSY_WRITE;
+ break;
+ case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+ address = NX_ACR1_AC97_W;
+ mask = NX_AC97_BUSY_WRITE;
+ if (codec->id)
+ mask |= NX_AC97_WRITE_SECONDARY;
+ busy = NX_AC97_BUSY_WRITE;
+ break;
+ }
+
+ spin_lock_irqsave(&card->lock, flags);
+ do {
+ if ((inw(TRID_REG(card, address)) & busy) == 0)
+ break;
+ } while (count--);
+
+
+ data |= (mask | (reg & AC97_REG_ADDR));
+
+ if (count == 0) {
+ printk(KERN_ERR "trident: AC97 CODEC write timed out.\n");
+ spin_unlock_irqrestore(&card->lock, flags);
+ return;
+ }
+
+ outl(data, TRID_REG(card, address));
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* Read AC97 codec registers */
+static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg)
+{
+ struct trident_card *card = (struct trident_card *)codec->private_data;
+ unsigned int address, mask, busy;
+ unsigned short count = 0xffff;
+ unsigned long flags;
+ u32 data;
+
+ switch (card->pci_id)
+ {
+ default:
+ case PCI_DEVICE_ID_SI_7018:
+ address = SI_AC97_READ;
+ mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY;
+ if (codec->id)
+ mask |= SI_AC97_SECONDARY;
+ busy = SI_AC97_BUSY_READ;
+ break;
+ case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+ address = DX_ACR1_AC97_R;
+ mask = busy = DX_AC97_BUSY_READ;
+ break;
+ case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+ if (codec->id)
+ address = NX_ACR3_AC97_R_SECONDARY;
+ else
+ address = NX_ACR2_AC97_R_PRIMARY;
+ mask = NX_AC97_BUSY_READ;
+ busy = 0x0c00;
+ break;
+ }
+
+ data = (mask | (reg & AC97_REG_ADDR));
+
+ spin_lock_irqsave(&card->lock, flags);
+ outl(data, TRID_REG(card, address));
+ do {
+ data = inl(TRID_REG(card, address));
+ if ((data & busy) == 0)
+ break;
+ } while (count--);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (count == 0) {
+ printk(KERN_ERR "trident: AC97 CODEC read timed out.\n");
+ data = 0;
+ }
+ return ((u16) (data >> 16));
+}
+
+/* OSS /dev/mixer file operation methods */
+static int trident_open_mixdev(struct inode *inode, struct file *file)
{
int i;
+ int minor = MINOR(inode->i_rdev);
+ struct trident_card *card = devs;
+
+ for (card = devs; card != NULL; card = card->next)
+ for (i = 0; i < NR_AC97; i++)
+ if (card->ac97_codec[i] != NULL &&
+ card->ac97_codec[i]->dev_mixer == minor)
+ goto match;
+
+ if (!card)
+ return -ENODEV;
+
+ match:
+ file->private_data = card->ac97_codec[i];
+
+ //FIXME put back in
+ //MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int trident_release_mixdev(struct inode *inode, struct file *file)
+{
+ //struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+
+ //FIXME put back in
+ //MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int trident_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 /*const*/ struct file_operations trident_mixer_fops = {
+ &trident_llseek,
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ &trident_ioctl_mixdev,
+ NULL, /* mmap */
+ &trident_open_mixdev,
+ NULL, /* flush */
+ &trident_release_mixdev,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* lock */
+};
+
+/* AC97 codec initialisation. */
+static int __init trident_ac97_init(struct trident_card *card)
+{
+ int num_ac97 = 0;
+ int ready_2nd = 0;
+ struct ac97_codec *codec;
+
+ /* initialize controller side of AC link, and find out if secondary codes
+ really exist */
+ switch (card->pci_id)
+ {
+ case PCI_DEVICE_ID_SI_7018:
+ /* disable AC97 GPIO interrupt */
+ outl(0x00, TRID_REG(card, SI_AC97_GPIO));
+ /* stop AC97 cold reset process */
+ outl(PCMOUT|SECONDARY_ID, TRID_REG(card, SI_SERIAL_INTF_CTRL));
+ ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
+ ready_2nd &= SI_AC97_SECONDARY_READY;
+ break;
+ case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+ /* playback on */
+ outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+ break;
+ case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+ /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */
+ outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT));
+ ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT));
+ ready_2nd &= NX_AC97_SECONDARY_READY;
+ break;
+ }
+
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+ if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+ return -1;
+ memset(codec, 0, sizeof(struct ac97_codec));
+
+ /* initialize some basic codec information, other fields will be filled
+ in ac97_probe_codec */
+ codec->private_data = card;
+ codec->id = num_ac97;
+ /* controller specific low level AC97 access function */
+ codec->codec_read = trident_ac97_get;
+ codec->codec_write = trident_ac97_set;
+
+ if (ac97_probe_codec(codec) == 0)
+ break;
+
+ if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) {
+ printk(KERN_ERR "trident: couldn't register mixer!\n");
+ kfree(codec);
+ break;
+ }
+
+ card->ac97_codec[num_ac97] = codec;
+
+ /* if there is no secondary codec at all, don't probe any more */
+ if (!ready_2nd)
+ return num_ac97+1;
+ }
+
+ return num_ac97;
+}
+
+/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered
+ untill "ACCESS" time (in prog_dmabuf calles by open/read/write/ioctl/mmap) */
+static int __init trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_info)
+{
u16 w;
unsigned long iobase;
struct trident_card *card;
- u32 ChanDwordCount;
iobase = pcidev->resource[0].start;
if (check_region(iobase, 256)) {
@@ -2847,30 +2110,12 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in
card->irq = pcidev->irq;
card->next = devs;
card->magic = TRIDENT_CARD_MAGIC;
+ card->banks[BANK_A].addresses = &bank_a_addrs;
+ card->banks[BANK_A].bitmap = 0UL;
+ card->banks[BANK_B].addresses = &bank_b_addrs;
+ card->banks[BANK_B].bitmap = 0UL;
spin_lock_init(&card->lock);
- devs = card;
-
- /* ungly stupid thing, remove ASAP */
- ChanDwordCount = card->ChanDwordCount = 2;
- card->ChRegs.lpChStart = card->ChRegs.data;
- card->ChRegs.lpChStop = card->ChRegs.lpChStart + ChanDwordCount;
- card->ChRegs.lpChAint = card->ChRegs.lpChStop + ChanDwordCount;
- card->ChRegs.lpChAinten = card->ChRegs.lpChAint + ChanDwordCount;
- card->ChRegs.lpAChStart = card->ChRegs.lpChAinten + ChanDwordCount;
- card->ChRegs.lpAChStop = card->ChRegs.lpAChStart + ChanDwordCount;
- card->ChRegs.lpAChAint = card->ChRegs.lpAChStop + ChanDwordCount;
- card->ChRegs.lpAChAinten = card->ChRegs.lpAChAint + ChanDwordCount;
- // Assign Bank A addresses.
- card->ChRegs.lpAChStart[0] = T4D_START_A;
- card->ChRegs.lpAChStop[0] = T4D_STOP_A;
- card->ChRegs.lpAChAint[0] = T4D_AINT_A;
- card->ChRegs.lpAChAinten[0] = T4D_AINTEN_A;
- /* Assign Bank B addresses */
- card->ChRegs.lpAChStart[1] = T4D_START_B;
- card->ChRegs.lpAChStop[1] = T4D_STOP_B;
- card->ChRegs.lpAChAint[1] = T4D_AINT_B;
- card->ChRegs.lpAChAinten[1] = T4D_AINTEN_B;
-
+ devs = card;
printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n",
card->pci_info->name, card->iobase, card->irq);
@@ -2883,11 +2128,6 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in
kfree(card);
return 0;
}
-
- /* initilize AC97 codec */
- trident_ac97_init(card);
- outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
-
/* register /dev/dsp */
if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) {
printk(KERN_ERR "trident: coundn't register DSP device!\n");
@@ -2896,29 +2136,18 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in
kfree(card);
return 0;
}
- /* register /dev/mixer */
- if ((card->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) {
- printk(KERN_ERR "trident: couldn't register mixer!\n");
+ /* initilize AC97 codec and register /dev/mixer */
+ if (trident_ac97_init(card) <= 0) {
unregister_sound_dsp(card->dev_audio);
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
return 0;
- } else {
- /* initilize mixer channels */
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- struct mixer_defaults *md = &mixer_defaults[i];
- if (md->mixer == -1)
- break;
- if (!supported_mixer(card, md->mixer))
- continue;
- set_mixer(card, md->mixer, md->value);
- }
}
+ outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
/* Enable Address Engine Interrupts */
- trident_enable_end_interrupts(card);
- trident_enable_middle_interrupts(card);
+ trident_enable_loop_interrupts(card);
return 1;
}
@@ -2952,23 +2181,23 @@ static int __init init_trident(void)
MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho");
MODULE_DESCRIPTION("Trident 4DWave/SiS 7018 PCI Audio Driver");
-#ifdef DEBUG
-MODULE_PARM(debug,"i");
-#endif
-
static void __exit cleanup_trident(void)
{
while (devs != NULL) {
+ int i;
/* Kill interrupts, and SP/DIF */
- trident_disable_end_interrupts(devs);
- trident_enable_middle_interrupts(devs);
+ trident_disable_loop_interrupts(devs);
/* free hardware resources */
free_irq(devs->irq, devs);
release_region(devs->iobase, 256);
/* unregister audio devices */
- unregister_sound_mixer(devs->dev_mixer);
+ for (i = 0; i < NR_AC97; i++)
+ if (devs->ac97_codec[i] != NULL) {
+ unregister_sound_mixer(devs->ac97_codec[i]->dev_mixer);
+ kfree (devs->ac97_codec[i]);
+ }
unregister_sound_dsp(devs->dev_audio);
kfree(devs);
@@ -2978,5 +2207,3 @@ static void __exit cleanup_trident(void)
module_init(init_trident);
module_exit(cleanup_trident);
-
-