patch-2.3.50 linux/drivers/sound/trident.c
Next file: linux/drivers/sound/trident.h
Previous file: linux/drivers/sound/sscape.c
Back to the patch index
Back to the overall index
- Lines: 1691
- Date:
Thu Mar 2 22:44:07 2000
- Orig file:
v2.3.49/linux/drivers/sound/trident.c
- Orig date:
Sat Feb 26 22:31:50 2000
diff -u --recursive --new-file v2.3.49/linux/drivers/sound/trident.c linux/drivers/sound/trident.c
@@ -29,6 +29,12 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.13 Mar 03 2000 Ollie Lho
+ * new pci_* for 2.4 kernel, back ported to 2.2
+ * v0.12 Feb 23 2000 Ollie Lho
+ * Preliminary Recording support
+ * v0.11.2 Feb 19 2000 Ollie Lho
+ * removed incomplete full-dulplex support
* v0.11.1 Jan 28 2000 Ollie Lho
* small bug in setting sample rate for 4d-nx (reported by Aaron)
* v0.11 Jan 27 2000 Ollie Lho
@@ -58,9 +64,10 @@
* 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
+ * Recording support (done)
* Mmap support
* "Channel Binding" ioctl extension
+ * new pci device driver interface for 2.4 kernel
*/
#include <linux/module.h>
@@ -85,27 +92,15 @@
#include "trident.h"
-#undef DEBUG
-
-#define DRIVER_VERSION "0.11.1"
+#define DRIVER_VERSION "0.13"
/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */
-/* 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 TRIDENT_DMA_MASK 0x3fffffff /* DMA buffer mask for pci_alloc_consist */
+
#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) */
@@ -119,18 +114,29 @@
static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n";
-struct pci_audio_info {
- u16 vendor;
- u16 device;
- char *name;
+enum {
+ TRIDENT_4D_DX = 0,
+ TRIDENT_4D_NX,
+ SIS_7018
};
-static struct pci_audio_info pci_audio_devices[] = {
- {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX, "Trident 4DWave DX"},
- {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX, "Trident 4DWave NX"},
- {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, "SiS 7018 PCI Audio"}
+static char * card_names[] = {
+ "Trident 4DWave DX",
+ "Trident 4DWave NX",
+ "SiS 7018 PCI Audio"
};
+static struct pci_device_id trident_pci_tbl [] __devinitdata = {
+ {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX},
+ {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX},
+ {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018},
+};
+
+MODULE_DEVICE_TABLE (pci, trident_pci_tbl);
+
/* "software" or virtual channel, an instance of opened /dev/dsp */
struct trident_state {
unsigned int magic;
@@ -156,14 +162,15 @@
/* OSS buffer manangemeent stuff */
void *rawbuf;
+ dma_addr_t dma_handle;
unsigned buforder;
unsigned numfrag;
unsigned fragshift;
/* our buffer acts like a circular ring */
- unsigned hwptr; /* where dma last started, update by update_ptr */
+ unsigned hwptr; /* where dma last started, updated by update_ptr */
unsigned swptr; /* where driver last clear/filled, updated by read/write */
- int count; /* bytes to be comsumed by dma machine */
+ int count; /* bytes to be comsumed or been generated by dma machine */
unsigned total_bytes; /* total bytes dmaed by hardware */
unsigned error; /* number of over/underruns */
@@ -177,22 +184,21 @@
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
- unsigned endcleared:1;
unsigned ossfragshift;
int ossmaxfrags;
unsigned subdivision;
- } dma_dac, dma_adc;
+ } dmabuf;
};
/* hardware channels */
struct trident_channel {
int num; /* channel number */
- u32 lba; /* reg 0xe4 */
- u32 eso; /* reg 0xe8 */
- u32 delta;
- u16 attribute; /* reg 0xec */
+ u32 lba; /* Loop Begine Address, where dma buffer starts */
+ u32 eso; /* End Sample Offset, wehre dma buffer ends (in the unit of samples) */
+ u32 delta; /* delta value, sample rate / 48k for playback, 48k/sample rate for recording */
+ u16 attribute; /* control where PCM data go and come */
u16 fm_vol;
- u32 control; /* reg 0xf0 */
+ u32 control; /* signed/unsigned, 8/16 bits, mono/stereo */
};
struct trident_pcm_bank_address {
@@ -234,7 +240,6 @@
spinlock_t lock;
/* PCI device stuff */
- struct pci_audio_info *pci_info;
struct pci_dev * pci_dev;
u16 pci_id;
@@ -422,9 +427,6 @@
if (bank->bitmap == ~0UL) {
/* no more free channels avaliable */
printk(KERN_ERR "trident: no more channels available on Bank B.\n");
-#ifdef ABUSE_BANK_A
- goto bank_a;
-#endif
return NULL;
}
for (idx = 31; idx >= 0; idx--) {
@@ -435,26 +437,6 @@
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) */
- bank_a:
- bank = &card->banks[BANK_A];
- if (bank->bitmap == ~0UL) {
- /* no more free channels avaliable */
- printk(KERN_ERR "trident: no more channels available on Bank A.\n");
- return NULL;
- }
- for (idx = 31; idx >= 0; idx--) {
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- banks->bitmap |= 1 << idx;
- channel->num = idx;
- return channels;
- }
- }
-#endif
return NULL;
}
@@ -462,13 +444,8 @@
{
int bank;
-#ifdef ABUSE_BANK_A
- if (channel < 0 || channel > 63)
- return;
-#else
if (channel < 31 || channel > 63)
return;
-#endif
bank = channel >> 5;
channel = channel & 0x1f;
@@ -497,15 +474,12 @@
}
/* called with spin lock held */
-static int trident_write_voice_regs(struct trident_state *state, unsigned int rec)
+static int trident_write_voice_regs(struct trident_state *state)
{
unsigned int data[CHANNEL_REGS + 1];
struct trident_channel *channel;
- if (rec)
- channel = state->dma_adc.channel;
- else
- channel = state->dma_dac.channel;
+ channel = state->dmabuf.channel;
data[1] = channel->lba;
data[4] = channel->control;
@@ -534,7 +508,7 @@
return trident_load_channel_registers(state->card, data, channel->num);
}
-static int compute_rate(u32 rate)
+static int compute_rate_play(u32 rate)
{
int delta;
/* We special case 44100 and 8000 since rounding with the equation
@@ -552,10 +526,25 @@
return delta;
}
+static int compute_rate_rec(u32 rate)
+{
+ int delta;
+
+ if (rate == 44100)
+ delta = 0x116a;
+ else if (rate == 8000)
+ delta = 0x6000;
+ else if (rate == 48000)
+ delta = 0x1000;
+ else
+ delta = ((48000 << 12) / rate) & 0x0000ffff;
+
+ return 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;
+ struct dmabuf *dmabuf = &state->dmabuf;
if (rate > 48000)
rate = 48000;
@@ -563,9 +552,9 @@
rate = 4000;
dmabuf->rate = rate;
- dmabuf->channel->delta = compute_rate(rate);
+ dmabuf->channel->delta = compute_rate_play(rate);
- trident_write_voice_regs(state, 0);
+ trident_write_voice_regs(state);
#ifdef DEBUG
printk("trident: called trident_set_dac_rate : rate = %d\n", rate);
@@ -577,16 +566,17 @@
/* set recording sample rate */
static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate)
{
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
+
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
dmabuf->rate = rate;
- dmabuf->channel->delta = compute_rate(rate);
+ dmabuf->channel->delta = compute_rate_rec(rate);
- trident_write_voice_regs(state, 1);
+ trident_write_voice_regs(state);
#ifdef DEBUG
printk("trident: called trident_set_adc_rate : rate = %d\n", rate);
@@ -597,18 +587,18 @@
/* prepare channel attributes for playback */
static void trident_play_setup(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
struct trident_channel *channel = dmabuf->channel;
channel->lba = virt_to_bus(dmabuf->rawbuf);
- channel->delta = compute_rate(dmabuf->rate);
+ channel->delta = compute_rate_play(dmabuf->rate);
channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
channel->eso -= 1;
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 */
+ /* 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;
@@ -631,7 +621,7 @@
"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);
+ trident_write_voice_regs(state);
}
/* prepare channel attributes for recording */
@@ -639,7 +629,7 @@
{
u16 w;
struct trident_card *card = state->card;
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
struct trident_channel *channel = dmabuf->channel;
/* Enable AC-97 ADC (capture) */
@@ -651,25 +641,30 @@
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT));
outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+ /* enable and set record channel */
+ outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
w = inw(TRID_REG(card, T4D_MISCINT));
outw(w | 0x1000, TRID_REG(card, T4D_MISCINT));
+ /* enable and set record channel */
+ outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
break;
default:
return;
}
channel->lba = virt_to_bus(dmabuf->rawbuf);
- channel->delta = compute_rate(dmabuf->rate);
+ channel->delta = compute_rate_rec(dmabuf->rate);
channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
channel->eso -= 1;
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;
+ /* FIXME: channel attributes are configured by ioctls, but it is not
+ implemented so just set to 0x8a80 for the moment, record from PCM L/R
+ input and mono = (left + right + 1)/2*/
+ channel->attribute = 0x8A80;
} else {
channel->attribute = 0;
}
@@ -691,21 +686,16 @@
"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);
+ trident_write_voice_regs(state);
}
/* 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)
+extern __inline__ unsigned trident_get_dma_addr(struct trident_state *state)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
u32 cso;
- if (rec)
- dmabuf = &state->dma_adc;
- else
- dmabuf = &state->dma_dac;
-
if (!dmabuf->enable)
return 0;
@@ -727,7 +717,8 @@
}
#ifdef DEBUG
- printk("trident: trident_get_dma_addr: chip reported channel: %d, cso = %d\n",
+ printk("trident: trident_get_dma_addr: chip reported channel: %d, "
+ "cso = 0x%04x\n",
dmabuf->channel->num, cso);
#endif
/* ESO and CSO are in units of Samples, convert to byte offset */
@@ -739,11 +730,11 @@
/* Stop recording (lock held) */
extern __inline__ void __stop_adc(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
- dmabuf->enable &= ~DMA_RUNNING;
+ dmabuf->enable &= ~ADC_RUNNING;
trident_stop_voice(card, chan_num);
trident_disable_voice_irq(card, chan_num);
}
@@ -760,16 +751,14 @@
static void start_adc(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
- if ((dmabuf->mapped ||
- dmabuf->count < (signed)(dmabuf->dmasize - 2*dmabuf->fragsize))
- && dmabuf->ready) {
- dmabuf->enable |= DMA_RUNNING;
+ if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) {
+ dmabuf->enable |= ADC_RUNNING;
trident_enable_voice_irq(card, chan_num);
trident_start_voice(card, chan_num);
}
@@ -779,11 +768,11 @@
/* stop playback (lock held) */
extern __inline__ void __stop_dac(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
- dmabuf->enable &= ~DMA_RUNNING;
+ dmabuf->enable &= ~DAC_RUNNING;
trident_stop_voice(card, chan_num);
trident_disable_voice_irq(card, chan_num);
}
@@ -800,14 +789,14 @@
static void start_dac(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
- dmabuf->enable |= DMA_RUNNING;
+ dmabuf->enable |= DAC_RUNNING;
trident_enable_voice_irq(card, chan_num);
trident_start_voice(card, chan_num);
}
@@ -818,39 +807,27 @@
#define DMABUF_MINORDER 1
/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
-static int alloc_dmabuf(struct trident_state *state, unsigned rec)
+static int alloc_dmabuf(struct trident_state *state)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->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)))
+ if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,
+ PAGE_SIZE << order,
+ &dmabuf->dma_handle)))
break;
if (!rawbuf)
return -ENOMEM;
+
#ifdef DEBUG
printk("trident: allocated %ld (order = %d) bytes at %p\n",
PAGE_SIZE << order, order, rawbuf);
#endif
- /* for 4DWave and 7018, there are only 30 (31) siginifcan bits for Loop Begin Address
- (LBA) which limits the address space to 1 (2) GB, bad T^2 design */
- if ((virt_to_bus(rawbuf) + (PAGE_SIZE << order) - 1) & ~0x3fffffff) {
- printk(KERN_ERR "trident: DMA buffer beyond 1 GB; "
- "bus address = 0x%lx, size = %ld\n",
- virt_to_bus(rawbuf), PAGE_SIZE << order);
- free_pages((unsigned long)rawbuf, order);
- return -ENOMEM;
- }
-
dmabuf->ready = dmabuf->mapped = 0;
dmabuf->rawbuf = rawbuf;
dmabuf->buforder = order;
@@ -864,16 +841,18 @@
}
/* free DMA buffer */
-static void dealloc_dmabuf(struct dmabuf *dmabuf)
+static void dealloc_dmabuf(struct trident_state *state)
{
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long map, mapend;
if (dmabuf->rawbuf) {
/* undo marking the pages as reserved */
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)dmabuf->rawbuf, dmabuf->buforder);
+ clear_bit(PG_reserved, &mem_map[map].flags);
+ pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,
+ dmabuf->rawbuf, dmabuf->dma_handle);
}
dmabuf->rawbuf = NULL;
dmabuf->mapped = dmabuf->ready = 0;
@@ -881,25 +860,20 @@
static int prog_dmabuf(struct trident_state *state, unsigned rec)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned bytepersec;
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);
- dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
- dmabuf->count = dmabuf->error = dmabuf->endcleared = 0;
+ dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->error = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
/* allocate DMA buffer if not allocated yet */
if (!dmabuf->rawbuf)
- if ((ret = alloc_dmabuf(state, rec)))
+ if ((ret = alloc_dmabuf(state)))
return ret;
/* FIXME: figure out all this OSS fragment stuff */
@@ -958,7 +932,7 @@
*/
static void trident_clear_tail(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned swptr;
unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80;
unsigned int len;
@@ -971,7 +945,6 @@
if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize)
return;
-
if (swptr < dmabuf->dmasize/2)
len = dmabuf->dmasize/2 - swptr;
else
@@ -979,7 +952,7 @@
memset(dmabuf->rawbuf + swptr, silence, len);
- spin_lock_irqsave(&state->card->lock, flags);
+ spin_lock_irqsave(&state->card->lock, flags);
dmabuf->swptr += len;
dmabuf->count += len;
spin_unlock_irqrestore(&state->card->lock, flags);
@@ -991,7 +964,7 @@
static int drain_dac(struct trident_state *state, int nonblock)
{
DECLARE_WAITQUEUE(wait, current);
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
unsigned long tmo;
int count;
@@ -1038,49 +1011,49 @@
return 0;
}
-/* call with spinlock held! */
+/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
static void trident_update_ptr(struct trident_state *state)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned hwptr;
int diff;
- /* update ADC pointer */
- 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;
+ /* update hardware pointer */
+ hwptr = trident_get_dma_addr(state);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += 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);
+ /* error handling and process wake up for DAC */
+ if (dmabuf->enable == ADC_RUNNING) {
+ if (dmabuf->mapped) {
+ dmabuf->count -= diff;
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
+ wake_up(&dmabuf->wait);
+ } else {
+ 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_adc(state);
dmabuf->error++;
}
+ /* 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);
}
}
-
- /* update DAC pointer */
- 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;
-
+ /* error handling and process wake up for DAC */
+ if (dmabuf->enable == DAC_RUNNING) {
if (dmabuf->mapped) {
dmabuf->count += diff;
- if (dmabuf->count >= (signed)dmabuf->fragsize)
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
wake_up(&dmabuf->wait);
- }
- else {
+ } else {
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
@@ -1088,7 +1061,7 @@
__stop_dac(state);
dmabuf->error++;
}
- /* since dma machine only interrupts at ESO and ESO/2, we sure have at
+ /* 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);
}
@@ -1120,17 +1093,13 @@
} else {
printk("trident: spurious channel irq %d.\n",
63 - i);
- trident_stop_voice(card, i);
- trident_disable_voice_irq(card, i);
+ trident_stop_voice(card, 63 - i);
+ trident_disable_voice_irq(card, 63 - i);
}
}
}
}
- if (event & SB_IRQ){
- /* Midi - TODO */
- }
-
/* manually clear interrupt status, bad hardware design, blame T^2 */
outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
TRID_REG(card, T4D_MISCINT));
@@ -1142,18 +1111,21 @@
return -ESPIPE;
}
-/* 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. */
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
+ the user's buffer. it is filled by the dma machine and drained by this loop. */
static ssize_t 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;
+ struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
+#ifdef DEBUG
+ printk("trident: trident_read called, count = %d\n", count);
+#endif
+
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
return -ESPIPE;
@@ -1167,6 +1139,12 @@
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
+ if (dmabuf->count > (signed) dmabuf->dmasize) {
+ /* buffer overrun, we are recovering from sleep_on_timeout,
+ resync hwptr and swptr, make process flush the buffer */
+ dmabuf->count = dmabuf->dmasize;
+ dmabuf->swptr = dmabuf->hwptr;
+ }
swptr = dmabuf->swptr;
cnt = dmabuf->dmasize - swptr;
if (dmabuf->count < cnt)
@@ -1175,25 +1153,35 @@
if (cnt > count)
cnt = count;
-
if (cnt <= 0) {
+ unsigned long tmo;
+ /* buffer is empty, start the dma machine and wait for data to be
+ recorded */
start_adc(state);
if (file->f_flags & O_NONBLOCK) {
- ret = ret ? ret : -EAGAIN;
+ if (!ret) ret = -EAGAIN;
return ret;
}
- if (!interruptible_sleep_on_timeout(&dmabuf->wait, HZ)) {
- printk(KERN_ERR
- "(trident) read: chip lockup? "
+ /* No matter how much space 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 overrun. 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: recording schedule timeout, "
"dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
dmabuf->hwptr, dmabuf->swptr);
- stop_adc(state);
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->count = 0;
- dmabuf->hwptr = 0;
- dmabuf->swptr = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);
+#endif
+ /* a buffer overrun, we delay the recovery untill next time the
+ while loop begin and we REALLY have space to record */
}
if (signal_pending(current)) {
ret = ret ? ret : -ERESTARTSYS;
@@ -1203,7 +1191,7 @@
}
if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
- ret = ret ? ret : -EFAULT;
+ if (!ret) ret = -EFAULT;
return ret;
}
@@ -1219,14 +1207,15 @@
ret += cnt;
start_adc(state);
}
-
return ret;
}
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
+ the soundcard. it is drained by the dma machine and filled by this loop. */
static ssize_t 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;
+ struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
unsigned swptr;
@@ -1234,7 +1223,7 @@
#ifdef DEBUG
printk("trident: trident_write called, count = %d\n", count);
-#endif
+#endif
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
@@ -1265,7 +1254,8 @@
cnt = count;
if (cnt <= 0) {
unsigned long tmo;
- /* buffer is full, start the dma machine and wait for data to be played */
+ /* 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;
@@ -1275,15 +1265,16 @@
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. */
+ /* 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, "
+ printk(KERN_ERR "trident: playback schedule timeout, "
"dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
dmabuf->hwptr, dmabuf->swptr);
@@ -1307,7 +1298,6 @@
spin_lock_irqsave(&state->card->lock, flags);
dmabuf->swptr = swptr;
dmabuf->count += cnt;
- dmabuf->endcleared = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
count -= cnt;
@@ -1320,32 +1310,33 @@
static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait)
{
- struct trident_state *s = (struct trident_state *)file->private_data;
+ struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
unsigned int mask = 0;
- VALIDATE_STATE(s);
+ VALIDATE_STATE(state);
if (file->f_mode & FMODE_WRITE)
- poll_wait(file, &s->dma_dac.wait, wait);
+ poll_wait(file, &dmabuf->wait, wait);
if (file->f_mode & FMODE_READ)
- poll_wait(file, &s->dma_adc.wait, wait);
+ poll_wait(file, &dmabuf->wait, wait);
- spin_lock_irqsave(&s->card->lock, flags);
- trident_update_ptr(s);
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_update_ptr(state);
if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLIN | POLLRDNORM;
}
if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+ if (dmabuf->mapped) {
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
} else {
- if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+ if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
}
}
- spin_unlock_irqrestore(&s->card->lock, flags);
+ spin_unlock_irqrestore(&state->card->lock, flags);
return mask;
}
@@ -1353,7 +1344,7 @@
static int trident_mmap(struct file *file, struct vm_area_struct *vma)
{
struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
int ret;
unsigned long size;
@@ -1361,11 +1352,9 @@
if (vma->vm_flags & VM_WRITE) {
if ((ret = prog_dmabuf(state, 0)) != 0)
return ret;
- dmabuf = &state->dma_dac;
} else if (vma->vm_flags & VM_READ) {
if ((ret = prog_dmabuf(state, 1)) != 0)
return ret;
- dmabuf = &state->dma_adc;
} else
return -EINVAL;
@@ -1385,14 +1374,15 @@
static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int val, mapped, ret;
VALIDATE_STATE(state);
- mapped = ((file->f_mode & FMODE_WRITE) && state->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && state->dma_adc.mapped);
+ mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
+ ((file->f_mode & FMODE_READ) && dmabuf->mapped);
#ifdef DEBUG
printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
_IOC_NR(cmd), arg ? *(int *)arg : 0);
@@ -1404,19 +1394,20 @@
return put_user(SOUND_VERSION, (int *)arg);
case SNDCTL_DSP_RESET:
+ /* FIXME: spin_lock ? */
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
synchronize_irq();
- state->dma_dac.ready = 0;
- state->dma_dac.swptr = state->dma_dac.hwptr = 0;
- state->dma_dac.count = state->dma_dac.total_bytes = 0;
+ dmabuf->ready = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->count = dmabuf->total_bytes = 0;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
synchronize_irq();
- state->dma_adc.ready = 0;
- state->dma_adc.swptr = state->dma_adc.hwptr = 0;
- state->dma_adc.count = state->dma_adc.total_bytes = 0;
+ dmabuf->ready = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->count = dmabuf->total_bytes = 0;
}
return 0;
@@ -1430,40 +1421,38 @@
if (val >= 0) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->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;
+ dmabuf->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) ? state->dma_adc.rate :
- state->dma_dac.rate,
- (int *)arg);
+ return put_user(dmabuf->rate, (int *)arg);
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
get_user_ret(val, (int *)arg, -EFAULT);
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->ready = 0;
if (val)
- state->dma_dac.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- state->dma_adc.ready = 0;
+ dmabuf->ready = 0;
if (val)
- state->dma_adc.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
return 0;
@@ -1471,12 +1460,12 @@
if (file->f_mode & FMODE_WRITE) {
if ((val = prog_dmabuf(state, 0)))
return val;
- return put_user(state->dma_dac.fragsize, (int *)arg);
+ return put_user(dmabuf->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);
+ return put_user(dmabuf->fragsize, (int *)arg);
}
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
@@ -1487,120 +1476,99 @@
if (val != AFMT_QUERY) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->ready = 0;
if (val == AFMT_S16_LE)
- state->dma_dac.fmt |= TRIDENT_FMT_16BIT;
+ dmabuf->fmt |= TRIDENT_FMT_16BIT;
else
- state->dma_dac.fmt &= ~TRIDENT_FMT_16BIT;
+ dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- state->dma_adc.ready = 0;
+ dmabuf->ready = 0;
if (val == AFMT_S16_LE)
- state->dma_adc.fmt |= TRIDENT_FMT_16BIT;
+ dmabuf->fmt |= TRIDENT_FMT_16BIT;
else
- state->dma_adc.fmt &= ~TRIDENT_FMT_16BIT;
+ dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
}
}
- 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);
+ return put_user((dmabuf->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) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->ready = 0;
if (val >= 2)
- state->dma_dac.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- state->dma_adc.ready = 0;
+ dmabuf->ready = 0;
if (val >= 2)
- state->dma_adc.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
}
- 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);
+ return put_user((dmabuf->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 && state->dma_adc.subdivision) ||
- (file->f_mode & FMODE_WRITE && state->dma_dac.subdivision))
+ if (dmabuf->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)
- state->dma_adc.subdivision = val;
- if (file->f_mode & FMODE_WRITE)
- state->dma_dac.subdivision = val;
+ dmabuf->subdivision = val;
return 0;
case SNDCTL_DSP_SETFRAGMENT:
get_user_ret(val, (int *)arg, -EFAULT);
- if (file->f_mode & FMODE_READ) {
- 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) {
- 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;
- }
+
+ dmabuf->ossfragshift = val & 0xffff;
+ dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+ if (dmabuf->ossfragshift < 4)
+ dmabuf->ossfragshift = 4;
+ if (dmabuf->ossfragshift > 15)
+ dmabuf->ossfragshift = 15;
+ if (dmabuf->ossmaxfrags < 4)
+ dmabuf->ossmaxfrags = 4;
+
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
- if (!state->dma_dac.enable && (val = prog_dmabuf(state, 0)) != 0)
+ if (!dmabuf->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;
+ abinfo.fragsize = dmabuf->fragsize;
+ abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+ abinfo.fragstotal = dmabuf->numfrag;
+ abinfo.fragments = abinfo.bytes >> dmabuf->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)
+ if (!dmabuf->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;
+ abinfo.fragsize = dmabuf->fragsize;
+ abinfo.bytes = dmabuf->count;
+ abinfo.fragstotal = dmabuf->numfrag;
+ abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
@@ -1609,14 +1577,13 @@
return 0;
case SNDCTL_DSP_GETCAPS:
- return put_user(/* DSP_CAP_DUPLEX|*/DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
- (int *)arg);
+ return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, (int *)arg);
case SNDCTL_DSP_GETTRIGGER:
val = 0;
- if (file->f_mode & FMODE_READ && state->dma_adc.enable)
+ if (file->f_mode & FMODE_READ && dmabuf->enable)
val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && state->dma_dac.enable)
+ if (file->f_mode & FMODE_WRITE && dmabuf->enable)
val |= PCM_ENABLE_OUTPUT;
return put_user(val, (int *)arg);
@@ -1624,20 +1591,18 @@
get_user_ret(val, (int *)arg, -EFAULT);
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
- if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1)))
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
return ret;
start_adc(state);
- }
- else
+ } else
stop_adc(state);
}
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT) {
- if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0)))
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
return ret;
start_dac(state);
- }
- else
+ } else
stop_dac(state);
}
return 0;
@@ -1647,59 +1612,50 @@
return -EINVAL;
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;
+ cinfo.bytes = dmabuf->total_bytes;
+ cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+ cinfo.ptr = dmabuf->hwptr;
+ if (dmabuf->mapped)
+ dmabuf->count &= dmabuf->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(&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;
+ cinfo.bytes = dmabuf->total_bytes;
+ cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+ cinfo.ptr = dmabuf->hwptr;
+ if (dmabuf->mapped)
+ dmabuf->count &= dmabuf->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;
+ return -EINVAL;
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;
+ val = dmabuf->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) ? state->dma_adc.rate :
- state->dma_dac.rate, (int *)arg);
+ return put_user(dmabuf->rate, (int *)arg);
case SOUND_PCM_READ_CHANNELS:
- 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);
-
+ return put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ (int *)arg);
+
case SOUND_PCM_READ_BITS:
- 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);
+ return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
+ AFMT_S16_LE : AFMT_U8, (int *)arg);
+
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
@@ -1738,70 +1694,61 @@
found_virt:
/* found a free virtual channel, allocate hardware channels */
- if (file->f_mode & FMODE_READ)
- 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.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.channel->num);
- return -ENODEV;
- }
+ if ((state->dmabuf.channel = trident_alloc_pcm_channel(card)) == NULL) {
+ kfree (card->states[i]);
+ card->states[i] = NULL;;
+ return -ENODEV;
+ }
/* initialize the virtual channel */
state->virt = i;
state->card = card;
state->magic = TRIDENT_STATE_MAGIC;
- init_waitqueue_head(&state->dma_adc.wait);
- init_waitqueue_head(&state->dma_dac.wait);
+ init_waitqueue_head(&state->dmabuf.wait);
init_MUTEX(&state->open_sem);
file->private_data = state;
down(&state->open_sem);
- /* set default sample format, Refer to OSS Programmer's Guide */
- if (file->f_mode & FMODE_READ) {
- /* 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, 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 */
+ /* set default sample format. According to OSS Programmer's Guide /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) {
- state->dma_dac.fmt &= ~TRIDENT_FMT_MASK;
+ state->dmabuf.fmt &= ~TRIDENT_FMT_MASK;
if ((minor & 0xf) == SND_DEV_DSP16)
- state->dma_dac.fmt |= TRIDENT_FMT_16BIT;
- state->dma_dac.ossfragshift = 0;
- state->dma_dac.ossmaxfrags = 0;
- state->dma_dac.subdivision = 0;
+ state->dmabuf.fmt |= TRIDENT_FMT_16BIT;
+ state->dmabuf.ossfragshift = 0;
+ state->dmabuf.ossmaxfrags = 0;
+ state->dmabuf.subdivision = 0;
trident_set_dac_rate(state, 8000);
}
+ if (file->f_mode & FMODE_READ) {
+ /* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample,
+ to be dealed with in trident_set_adc_rate() ?? */
+ state->dmabuf.fmt &= ~TRIDENT_FMT_MASK;
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ state->dmabuf.fmt |= TRIDENT_FMT_16BIT;
+ state->dmabuf.ossfragshift = 0;
+ state->dmabuf.ossmaxfrags = 0;
+ state->dmabuf.subdivision = 0;
+ trident_set_adc_rate(state, 8000);
+ }
+
state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&state->open_sem);
- //FIXME put back in
- //MOD_INC_USE_COUNT;
+ MOD_INC_USE_COUNT;
return 0;
}
static int trident_release(struct inode *inode, struct file *file)
{
struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
VALIDATE_STATE(state);
-
if (file->f_mode & FMODE_WRITE) {
trident_clear_tail(state);
drain_dac(state, file->f_flags & O_NONBLOCK);
@@ -1812,13 +1759,13 @@
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- dealloc_dmabuf(&state->dma_dac);
- trident_free_pcm_channel(state->card, state->dma_dac.channel->num);
+ dealloc_dmabuf(state);
+ trident_free_pcm_channel(state->card, dmabuf->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.channel->num);
+ dealloc_dmabuf(state);
+ trident_free_pcm_channel(state->card, dmabuf->channel->num);
}
kfree(state->card->states[state->virt]);
@@ -1828,8 +1775,7 @@
/* we're covered by the open_sem */
up(&state->open_sem);
- //FIXME put back in
- //MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -1845,7 +1791,7 @@
};
/* trident specific AC97 functions */
-/* Write AC97 mixer registers */
+/* Write AC97 codec registers */
static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val)
{
struct trident_card *card = (struct trident_card *)codec->private_data;
@@ -1863,7 +1809,7 @@
address = SI_AC97_WRITE;
mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY;
if (codec->id)
- mask |= SI_AC97_SECONDARY;
+ mask |= SI_AC97_SECONDARY;
busy = SI_AC97_BUSY_WRITE;
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
@@ -1874,7 +1820,7 @@
address = NX_ACR1_AC97_W;
mask = NX_AC97_BUSY_WRITE;
if (codec->id)
- mask |= NX_AC97_WRITE_SECONDARY;
+ mask |= NX_AC97_WRITE_SECONDARY;
busy = NX_AC97_BUSY_WRITE;
break;
}
@@ -1968,17 +1914,13 @@
match:
file->private_data = card->ac97_codec[i];
- //FIXME put back in
- //MOD_INC_USE_COUNT;
+ 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;
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -2061,36 +2003,35 @@
}
/* 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)
+ untill "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
- u16 w;
unsigned long iobase;
struct trident_card *card;
- iobase = pcidev->resource[0].start;
+ if (!pci_dma_supported(pci_dev, TRIDENT_DMA_MASK)) {
+ printk(KERN_ERR "trident: architecture does not support"
+ " 30bit PCI busmaster DMA\n");
+ return -1;
+ }
+
+ iobase = pci_dev->resource[0].start;
if (check_region(iobase, 256)) {
printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n",
iobase);
- return 0;
+ return -1;
}
- /* just to be sure that IO space and bus master is on */
- pci_set_master(pcidev);
- pci_read_config_word(pcidev, PCI_COMMAND, &w);
- w |= PCI_COMMAND_IO|PCI_COMMAND_MASTER;
- pci_write_config_word(pcidev, PCI_COMMAND, w);
-
if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "trident: out of memory\n");
- return 0;
+ return -1;
}
memset(card, 0, sizeof(*card));
card->iobase = iobase;
- card->pci_info = pci_info;
- card->pci_id = pci_info->device;
- card->irq = pcidev->irq;
+ card->pci_dev = pci_dev;
+ card->pci_id = pci_id->device;
+ card->irq = pci_dev->irq;
card->next = devs;
card->magic = TRIDENT_CARD_MAGIC;
card->banks[BANK_A].addresses = &bank_a_addrs;
@@ -2098,14 +2039,18 @@
card->banks[BANK_B].addresses = &bank_b_addrs;
card->banks[BANK_B].bitmap = 0UL;
spin_lock_init(&card->lock);
- devs = card;
+ devs = card;
+
+ pci_set_master(pci_dev);
+ pci_enable_device(pci_dev);
printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n",
- card->pci_info->name, card->iobase, card->irq);
+ card_names[pci_id->driver_data], card->iobase, card->irq);
/* claim our iospace and irq */
- request_region(card->iobase, 256, card->pci_info->name);
- if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, card->pci_info->name, card)) {
+ request_region(card->iobase, 256, card_names[pci_id->driver_data]);
+ if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ,
+ card_names[pci_id->driver_data], card)) {
printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq);
release_region(card->iobase, 256);
kfree(card);
@@ -2117,7 +2062,7 @@
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
- return 0;
+ return -1;
}
/* initilize AC97 codec and register /dev/mixer */
if (trident_ac97_init(card) <= 0) {
@@ -2125,68 +2070,66 @@
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
- return 0;
+ return -1;
}
outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
+ pci_dev->driver_data = card;
+ pci_dev->dma_mask = TRIDENT_DMA_MASK;
+
/* Enable Address Engine Interrupts */
trident_enable_loop_interrupts(card);
- return 1;
+ return 0;
}
-static int __init init_trident(void)
+static void __devexit trident_remove(struct pci_dev *pci_dev)
{
- struct pci_dev *pcidev = NULL;
- int foundone = 0;
int i;
+ struct trident_card *card = pci_dev->driver_data;
- if (!pci_present()) /* No PCI bus in this machine! */
- return -ENODEV;
+ /* Kill interrupts, and SP/DIF */
+ trident_disable_loop_interrupts(card);
- printk(KERN_INFO "Trident 4DWave/SiS 7018 PCI Audio, version "
- DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
+ /* free hardware resources */
+ free_irq(card->irq, devs);
+ release_region(card->iobase, 256);
- for (i = 0; i < sizeof (pci_audio_devices); i++) {
- pcidev = NULL;
- while ((pcidev = pci_find_device(pci_audio_devices[i].vendor,
- pci_audio_devices[i].device,
- pcidev)) != NULL) {
- foundone += trident_install(pcidev, pci_audio_devices + i);
+ /* unregister audio devices */
+ for (i = 0; i < NR_AC97; i++)
+ if (devs->ac97_codec[i] != NULL) {
+ unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+ kfree (card->ac97_codec[i]);
}
- }
+ unregister_sound_dsp(card->dev_audio);
- if (!foundone)
- return -ENODEV;
- return 0;
+ kfree(card);
}
MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho");
MODULE_DESCRIPTION("Trident 4DWave/SiS 7018 PCI Audio Driver");
-static void __exit cleanup_trident(void)
+#define TRIDENT_MODULE_NAME "trident"
+
+static struct pci_driver trident_pci_driver = {
+ name: TRIDENT_MODULE_NAME,
+ id_table: trident_pci_tbl,
+ probe: trident_probe,
+ remove: trident_remove,
+};
+
+static int __init trident_init_module (void)
{
- while (devs != NULL) {
- int i;
- /* Kill interrupts, and SP/DIF */
- trident_disable_loop_interrupts(devs);
-
- /* free hardware resources */
- free_irq(devs->irq, devs);
- release_region(devs->iobase, 256);
+ printk(KERN_INFO "Trident 4DWave/SiS 7018 PCI Audio, version "
+ DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
- /* unregister audio devices */
- 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);
+ return pci_module_init (&trident_pci_driver);
+}
- kfree(devs);
- devs = devs->next;
- }
+static void __exit trident_cleanup_module (void)
+{
+ pci_unregister_driver (&trident_pci_driver);
}
-module_init(init_trident);
-module_exit(cleanup_trident);
+module_init(trident_init_module);
+module_exit(trident_cleanup_module);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)