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

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)