patch-2.4.1 linux/drivers/sound/ymfpci.c

Next file: linux/drivers/sound/ymfpci.h
Previous file: linux/drivers/sound/via82cxxx_audio.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0/linux/drivers/sound/ymfpci.c linux/drivers/sound/ymfpci.c
@@ -24,19 +24,28 @@
  *
  * TODO:
  *  - Use P44Slot for 44.1 playback.
- *  - Capture and duplex
  *  - 96KHz playback for DVD - use pitch of 2.0.
  *  - uLaw for Sun apps.
+ *     : Alan says firmly "no software format conversion in kernel".
  *  - Retain DMA buffer on close, do not wait the end of frame.
  *  - Cleanup
- *      ? merge ymf_pcm and state
- *      ? pcm interrupt no pointer
  *      ? underused structure members
  *      - Remove remaining P3 tags (debug messages).
  *  - Resolve XXX tagged questions.
  *  - Cannot play 5133Hz.
+ *  - 2001/01/07 Consider if we can remove voice_lock, like so:
+ *     : Allocate/deallocate voices in open/close under semafore.
+ *     : We access voices in interrupt, that only for pcms that open.
+ *    voice_lock around playback_prepare closes interrupts for insane duration.
+ *  - Revisit the way voice_alloc is done - too confusing, overcomplicated.
+ *    Should support various channel types, however.
+ *  - Remove prog_dmabuf from read/write, leave it in open.
+ *  - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_YMFPCI_LEGACY code with
+ *    native synthesizer through a playback slot.
+ *  - Use new 2.3.x cache coherent PCI DMA routines instead of virt_to_bus.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
@@ -50,19 +59,19 @@
 #include <asm/dma.h>
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+# include "sound_config.h"
+# include "mpu401.h"
+#endif
 #include "ymfpci.h"
 
-#define snd_magic_cast(t, p, err)	((t *)(p))
-
-/* Channels, such as play and record. I do only play a.t.m. XXX */
-#define NR_HW_CH	1
-
-static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd);
-static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type,
-    int pair, ymfpci_voice_t **rvoice);
-static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
-static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state);
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt);
+static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd);
+static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);
+static void ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
+static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank);
+static int ymf_playback_prepare(struct ymf_state *state);
+static int ymf_capture_prepare(struct ymf_state *state);
+static struct ymf_state *ymf_state_alloc(ymfpci_t *unit);
 
 static LIST_HEAD(ymf_devs);
 
@@ -87,7 +96,7 @@
 /*
  * Mindlessly copied from cs46xx XXX
  */
-extern __inline__ unsigned ld2(unsigned int x)
+static inline unsigned ld2(unsigned int x)
 {
 	unsigned r = 0;
 	
@@ -281,18 +290,13 @@
 		f->shift++;
 }
 
-/*
- * Whole OSS-style DMA machinery is taken from cs46xx.
- */
-
 /* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */
 #define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
 #define DMABUF_MINORDER 1
 
 /* allocate DMA buffer, playback and recording buffer should be allocated seperately */
-static int alloc_dmabuf(struct ymf_state *state)
+static int alloc_dmabuf(struct ymf_dmabuf *dmabuf)
 {
-	struct ymf_dmabuf *dmabuf = &state->dmabuf;
 	void *rawbuf = NULL;
 	int order;
 	struct page * map,  * mapend;
@@ -323,9 +327,8 @@
 }
 
 /* free DMA buffer */
-static void dealloc_dmabuf(struct ymf_state *state)
+static void dealloc_dmabuf(struct ymf_dmabuf *dmabuf)
 {
-	struct ymf_dmabuf *dmabuf = &state->dmabuf;
 	struct page *map, *mapend;
 
 	if (dmabuf->rawbuf) {
@@ -339,9 +342,9 @@
 	dmabuf->mapped = dmabuf->ready = 0;
 }
 
-static int prog_dmabuf(struct ymf_state *state, unsigned rec)
+static int prog_dmabuf(struct ymf_state *state, int rec)
 {
-	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	struct ymf_dmabuf *dmabuf;
 	int w_16;
 	unsigned bytepersec;
 	unsigned bufsize;
@@ -350,6 +353,7 @@
 	int ret;
 
 	w_16 = ymf_pcm_format_width(state->format.format) == 16;
+	dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf;
 
 	spin_lock_irqsave(&state->unit->reg_lock, flags);
 	dmabuf->hwptr = dmabuf->swptr = 0;
@@ -359,7 +363,7 @@
 
 	/* allocate DMA buffer if not allocated yet */
 	if (!dmabuf->rawbuf)
-		if ((ret = alloc_dmabuf(state)))
+		if ((ret = alloc_dmabuf(dmabuf)))
 			return ret;
 
 	bytepersec = state->format.rate << state->format.shift;
@@ -383,7 +387,6 @@
 		dmabuf->numfrag = bufsize >> dmabuf->fragshift;
 	}
 	dmabuf->fragsize = 1 << dmabuf->fragshift;
-	dmabuf->fragsamples = dmabuf->fragsize >> state->format.shift;
 	dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
 
 	/*
@@ -414,15 +417,20 @@
 	 *	Now set up the ring 
 	 */
 
-	spin_lock_irqsave(&state->unit->reg_lock, flags);
+	/* XXX   ret = rec? cap_pre(): pbk_pre();  */
+	spin_lock_irqsave(&state->unit->voice_lock, flags);
 	if (rec) {
-		/* ymf_rec_setup(state); */
+		if ((ret = ymf_capture_prepare(state)) != 0) {
+			spin_unlock_irqrestore(&state->unit->voice_lock, flags);
+			return ret;
+		}
 	} else {
-		if ((ret = ymf_playback_prepare(state->unit, state)) != 0) {
+		if ((ret = ymf_playback_prepare(state)) != 0) {
+			spin_unlock_irqrestore(&state->unit->voice_lock, flags);
 			return ret;
 		}
 	}
-	spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+	spin_unlock_irqrestore(&state->unit->voice_lock, flags);
 
 	/* set the ready flag for the dma buffer (this comment is not stupid) */
 	dmabuf->ready = 1;
@@ -439,9 +447,14 @@
 
 static void ymf_start_dac(struct ymf_state *state)
 {
-	ymf_playback_trigger(state->unit, &state->ypcm, 1);
+	ymf_playback_trigger(state->unit, &state->wpcm, 1);
 }
 
+// static void ymf_start_adc(struct ymf_state *state)
+// {
+// 	ymf_capture_trigger(state->unit, &state->rpcm, 1);
+// }
+
 /*
  * Wait until output is drained.
  * This does not kill the hardware for the sake of ioctls.
@@ -449,14 +462,14 @@
 static void ymf_wait_dac(struct ymf_state *state)
 {
 	struct ymf_unit *unit = state->unit;
-	ymfpci_pcm_t *ypcm = &state->ypcm;
+	struct ymf_pcm *ypcm = &state->wpcm;
 	DECLARE_WAITQUEUE(waita, current);
 	unsigned long flags;
 
-	add_wait_queue(&state->dmabuf.wait, &waita);
+	add_wait_queue(&ypcm->dmabuf.wait, &waita);
 
 	spin_lock_irqsave(&unit->reg_lock, flags);
-	if (state->dmabuf.count != 0 && !state->ypcm.running) {
+	if (ypcm->dmabuf.count != 0 && !ypcm->running) {
 		ymf_playback_trigger(unit, ypcm, 1);
 	}
 
@@ -479,7 +492,7 @@
 	spin_unlock_irqrestore(&unit->reg_lock, flags);
 
 	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&state->dmabuf.wait, &waita);
+	remove_wait_queue(&ypcm->dmabuf.wait, &waita);
 
 	/*
 	 * This function may take up to 4 seconds to reach this point
@@ -487,6 +500,17 @@
 	 */
 }
 
+/* Can just stop, without wait. Or can we? */
+static void ymf_stop_adc(struct ymf_state *state)
+{
+	struct ymf_unit *unit = state->unit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+	ymf_capture_trigger(unit, &state->rpcm, 0);
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+}
+
 /*
  *  Hardware start management
  */
@@ -523,12 +547,11 @@
  *  Playback voice management
  */
 
-static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[])
 {
 	ymfpci_voice_t *voice, *voice2;
 	int idx;
-	
-	*rvoice = NULL;
+
 	for (idx = 0; idx < 64; idx += pair ? 2 : 1) {
 		voice = &codec->voices[idx];
 		voice2 = pair ? &codec->voices[idx+1] : NULL;
@@ -551,52 +574,29 @@
 			break;
 		}
 		ymfpci_hw_start(codec);
-		if (voice2)
+		rvoice[0] = voice;
+		if (voice2) {
 			ymfpci_hw_start(codec);
-		*rvoice = voice;
+			rvoice[1] = voice2;
+		}
 		return 0;
 	}
-	return -ENOMEM;
+	return -EBUSY;	/* Your audio channel is open by someone else. */
 }
 
-static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type,
-    int pair, ymfpci_voice_t **rvoice)
+static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice)
 {
-	unsigned long flags;
-	int result;
-
-	spin_lock_irqsave(&codec->voice_lock, flags);
-	for (;;) {
-		result = voice_alloc(codec, type, pair, rvoice);
-		if (result == 0 || type != YMFPCI_PCM)
-			break;
-		/* TODO: synth/midi voice deallocation */
-		break;
-	}
-	spin_unlock_irqrestore(&codec->voice_lock, flags);	
-	return result;		
-}
-
-static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice)
-{
-	unsigned long flags;
-	
-	ymfpci_hw_stop(codec);
-	spin_lock_irqsave(&codec->voice_lock, flags);
+	ymfpci_hw_stop(unit);
 	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
 	pvoice->ypcm = NULL;
-	pvoice->interrupt = NULL;
-	spin_unlock_irqrestore(&codec->voice_lock, flags);
-	return 0;
 }
 
 /*
- *  PCM part
  */
 
 static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
 {
-	ymfpci_pcm_t *ypcm;
+	struct ymf_pcm *ypcm;
 	int redzone;
 	int pos, delta, swptr;
 	int played, distance;
@@ -611,7 +611,7 @@
 		ypcm->running = 0;	// lock it
 		return;
 	}
-	dmabuf = &state->dmabuf;
+	dmabuf = &ypcm->dmabuf;
 	spin_lock(&codec->reg_lock);
 	if (ypcm->running) {
 /* P3 */ /** printk("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n",
@@ -627,10 +627,9 @@
 		pos = voice->bank[codec->active_bank].start;
 		pos <<= state->format.shift;
 		if (pos < 0 || pos >= dmabuf->dmasize) {	/* ucode bug */
-			printk(KERN_ERR
-			    "ymfpci%d: %d: runaway: hwptr %d dmasize %d\n",
+			printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n",
 			    codec->dev_audio, voice->number,
-			    dmabuf->hwptr, dmabuf->dmasize);
+			    dmabuf->hwptr, pos, dmabuf->dmasize);
 			pos = 0;
 		}
 		if (pos < dmabuf->hwptr) {
@@ -706,36 +705,66 @@
 	spin_unlock(&codec->reg_lock);
 }
 
-#if HAVE_RECORD
-static void ymfpci_pcm_capture_interrupt(snd_pcm_subchn_t *substream)
+static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap)
 {
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, );
-	ymfpci_t *codec = ypcm->codec;
-	u32 pos, delta;
-	
-	spin_lock(&codec->reg_lock);
+	struct ymf_pcm *ypcm;
+	int redzone;
+	struct ymf_state *state;
+	struct ymf_dmabuf *dmabuf;
+	int pos, delta;
+	int cnt;
+
+	if ((ypcm = cap->ypcm) == NULL) {
+		return;
+	}
+	if ((state = ypcm->state) == NULL) {
+		ypcm->running = 0;	// lock it
+		return;
+	}
+	dmabuf = &ypcm->dmabuf;
+	spin_lock(&unit->reg_lock);
 	if (ypcm->running) {
-		pos = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset;
-		if (pos < ypcm->last_pos)  // <--  dmabuf->hwptr
-			delta = pos + (ypcm->buffer_size - ypcm->last_pos);
-		else
-			delta = pos - ypcm->last_pos;
-		ypcm->frag_pos += delta;
-		ypcm->last_pos = pos;
-		while (ypcm->frag_pos >= ypcm->frag_size) {
-			ypcm->frag_pos -= ypcm->frag_size;
-			// printk("done - active_bank = 0x%x, start = 0x%x\n", codec->active_bank, voice->bank[codec->active_bank].start);
-			spin_unlock(&codec->reg_lock);
-			snd_pcm_transfer_done(substream);
-			spin_lock(&codec->reg_lock);
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= (state->format.shift + 1);
+
+		pos = cap->bank[unit->active_bank].start;
+		// pos <<= state->format.shift;
+		if (pos < 0 || pos >= dmabuf->dmasize) {	/* ucode bug */
+			printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n",
+			    unit->dev_audio, ypcm->capture_bank_number,
+			    dmabuf->hwptr, pos, dmabuf->dmasize);
+			pos = 0;
+		}
+		if (pos < dmabuf->hwptr) {
+			delta = dmabuf->dmasize - dmabuf->hwptr;
+			delta += pos;
+		} else {
+			delta = pos - dmabuf->hwptr;
+		}
+		dmabuf->hwptr = pos;
+
+		cnt = dmabuf->count;
+		cnt += delta;
+		if (cnt + redzone > dmabuf->dmasize) {
+			/* Overflow - bump swptr */
+			dmabuf->count = dmabuf->dmasize - redzone;
+			dmabuf->swptr = dmabuf->hwptr + redzone;
+			if (dmabuf->swptr >= dmabuf->dmasize) {
+				dmabuf->swptr -= dmabuf->dmasize;
+			}
+		} else {
+			dmabuf->count = cnt;
+		}
+
+		dmabuf->total_bytes += delta;
+		if (dmabuf->count) {		/* && is_sleeping  XXX */
+			wake_up(&dmabuf->wait);
 		}
 	}
-	spin_unlock(&codec->reg_lock);
+	spin_unlock(&unit->reg_lock);
 }
-#endif
 
-static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd)
+static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd)
 {
 
 	if (ypcm->voices[0] == NULL) {
@@ -755,40 +784,29 @@
 	return 0;
 }
 
-#if HAVE_RECORD
-static int ymfpci_capture_trigger(void *private_data,	
-				      snd_pcm_subchn_t * substream,
-				      int cmd)
+static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd)
 {
-	unsigned long flags;
-	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
-	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, -ENXIO);
-	int result = 0;
 	u32 tmp;
 
-	spin_lock_irqsave(&codec->reg_lock, flags);
-	if (cmd == SND_PCM_TRIGGER_GO) {
+	if (cmd != 0) {
 		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
 		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
 		ypcm->running = 1;
-	} else if (cmd == SND_PCM_TRIGGER_STOP) {
+	} else {
 		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
 		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
 		ypcm->running = 0;
-	} else {
-		result = -EINVAL;
 	}
-	spin_unlock_irqrestore(&codec->reg_lock, flags);
-	return result;
 }
-#endif
 
-static int ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
+static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices)
 {
+	struct ymf_unit *unit;
 	int err;
 
+	unit = ypcm->state->unit;
 	if (ypcm->voices[1] != NULL && voices < 2) {
-		ymfpci_voice_free(ypcm->codec, ypcm->voices[1]);
+		ymfpci_voice_free(unit, ypcm->voices[1]);
 		ypcm->voices[1] = NULL;
 	}
 	if (voices == 1 && ypcm->voices[0] != NULL)
@@ -797,18 +815,17 @@
 		return 0;		/* already allocated */
 	if (voices > 1) {
 		if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
-			ymfpci_voice_free(ypcm->codec, ypcm->voices[0]);
+			ymfpci_voice_free(unit, ypcm->voices[0]);
 			ypcm->voices[0] = NULL;
 		}		
-	}
-	err = ymfpci_voice_alloc(ypcm->codec, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);
-	if (err < 0)
-		return err;
-	ypcm->voices[0]->ypcm = ypcm;
-	ypcm->voices[0]->interrupt = ymf_pcm_interrupt;
-	if (voices > 1) {
-		ypcm->voices[1] = &ypcm->codec->voices[ypcm->voices[0]->number + 1];
+		if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0)
+			return err;
+		ypcm->voices[0]->ypcm = ypcm;
 		ypcm->voices[1]->ypcm = ypcm;
+	} else {
+		if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0)
+			return err;
+		ypcm->voices[0]->ypcm = ypcm;
 	}
 	return 0;
 }
@@ -901,17 +918,32 @@
 }
 
 /*
- * XXX Use new cache coherent PCI DMA routines instead of virt_to_bus.
+ * XXX Capture channel allocation is entirely fake at the moment.
+ * We use only one channel and mark it busy as required.
  */
-static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state)
+static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank)
 {
-	ymfpci_pcm_t *ypcm = &state->ypcm;
+	struct ymf_capture *cap;
+	int cbank;
+
+	cbank = 1;		/* Only ADC slot is used for now. */
+	cap = &unit->capture[cbank];
+	if (cap->use)
+		return -EBUSY;
+	cap->use = 1;
+	*pbank = cbank;
+	return 0;
+}
+
+static int ymf_playback_prepare(struct ymf_state *state)
+{
+	struct ymf_pcm *ypcm = &state->wpcm;
 	int err, nvoice;
 
 	if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {
-		/* Cannot be unless we leak voices in ymf_release! */
-		printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n",
-		    codec->dev_audio);
+		/* Somebody started 32 mpg123's in parallel? */
+		/* P3 */ printk("ymfpci%d: cannot allocate voice\n",
+		    state->unit->dev_audio);
 		return err;
 	}
 
@@ -919,101 +951,76 @@
 		ymf_pcm_init_voice(ypcm->voices[nvoice],
 		    state->format.voices == 2, state->format.rate,
 		    ymf_pcm_format_width(state->format.format) == 16,
-		    virt_to_bus(state->dmabuf.rawbuf), state->dmabuf.dmasize,
+		    virt_to_bus(ypcm->dmabuf.rawbuf), ypcm->dmabuf.dmasize,
 		    ypcm->spdif);
 	}
 	return 0;
 }
 
-#if 0  /* old */
-static int ymfpci_capture_prepare(void *private_data,
-				      snd_pcm_subchn_t * substream)
-{
-	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
+static int ymf_capture_prepare(struct ymf_state *state)
+{
+	ymfpci_t *unit = state->unit;
+	struct ymf_pcm *ypcm = &state->rpcm;
 	ymfpci_capture_bank_t * bank;
-	int nbank;
+	/* XXX This is confusing, gotta rename one of them banks... */
+	int nbank;		/* flip-flop bank */
+	int cbank;		/* input [super-]bank */
+	struct ymf_capture *cap;
 	u32 rate, format;
 
-	ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream);
-	ypcm->buffer_size = snd_pcm_lib_transfer_size(substream);
-	ypcm->frag_pos = 0;
-	ypcm->last_pos = 0;
-	ypcm->shift_offset = 0;
-	rate = ((48000 * 4096) / runtime->format.rate) - 1;
+	if (ypcm->capture_bank_number == -1) {
+		if (ymf_capture_alloc(unit, &cbank) != 0)
+			return -EBUSY;
+
+		ypcm->capture_bank_number = cbank;
+
+		cap = &unit->capture[cbank];
+		cap->bank = unit->bank_capture[cbank][0];
+		cap->ypcm = ypcm;
+		ymfpci_hw_start(unit);
+	}
+
+	// ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream);
+	// frag_size is replaced with nonfragged byte-aligned rolling buffer
+	rate = ((48000 * 4096) / state->format.rate) - 1;
 	format = 0;
-	if (runtime->format.voices == 2)
+	if (state->format.voices == 2)
 		format |= 2;
-	if (snd_pcm_format_width(runtime->format.format) == 8)
+	if (ymf_pcm_format_width(state->format.format) == 8)
 		format |= 1;
 	switch (ypcm->capture_bank_number) {
 	case 0:
-		ymfpci_writel(codec, YDSXGR_RECFORMAT, format);
-		ymfpci_writel(codec, YDSXGR_RECSLOTSR, rate);
+		ymfpci_writel(unit, YDSXGR_RECFORMAT, format);
+		ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate);
 		break;
 	case 1:
-		ymfpci_writel(codec, YDSXGR_ADCFORMAT, format);
-		ymfpci_writel(codec, YDSXGR_ADCSLOTSR, rate);
+		ymfpci_writel(unit, YDSXGR_ADCFORMAT, format);
+		ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate);
 		break;
 	}
 	for (nbank = 0; nbank < 2; nbank++) {
-		bank = codec->bank_capture[ypcm->capture_bank_number][nbank];
-		bank->base = virt_to_bus(runtime->dma_area->buf);
-		bank->loop_end = ypcm->buffer_size;
+		bank = unit->bank_capture[ypcm->capture_bank_number][nbank];
+		bank->base = virt_to_bus(ypcm->dmabuf.rawbuf);
+		// bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift;
+		bank->loop_end = ypcm->dmabuf.dmasize;
 		bank->start = 0;
 		bank->num_of_loops = 0;
 	}
-	if (runtime->digital.dig_valid)
-		/*runtime->digital.type == SND_PCM_DIG_AES_IEC958*/
-		ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, runtime->digital.dig_status[0] |
-								(runtime->digital.dig_status[1] << 8));
+#if 0 /* s/pdif */
+	if (state->digital.dig_valid)
+		/*state->digital.type == SND_PCM_DIG_AES_IEC958*/
+		ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS,
+		    state->digital.dig_status[0] | (state->digital.dig_status[1] << 8));
+#endif
 	return 0;
 }
 
-static unsigned int ymfpci_playback_pointer(void *private_data,
-						snd_pcm_subchn_t * substream)
-{
-	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
-	ymfpci_voice_t *voice = ypcm->voices[0];
-	unsigned long flags;
-	unsigned int result;
-
-	spin_lock_irqsave(&codec->reg_lock, flags);
-	if (ypcm->running && voice)
-		result = voice->bank[codec->active_bank].start << ypcm->shift_offset;
-	else
-		result = 0;
-	spin_unlock_irqrestore(&codec->reg_lock, flags);
-	return result;
-}
-
-static unsigned int ymfpci_capture_pointer(void *private_data,
-					       snd_pcm_subchn_t * substream)
-{
-	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
-	unsigned long flags;
-	unsigned int result;
-
-	spin_lock_irqsave(&codec->reg_lock, flags);
-	if (ypcm->running)
-		result = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset;
-	else
-		result = 0;
-	spin_unlock_irqrestore(&codec->reg_lock, flags);
-	return result;
-}
-#endif /* old */
-
 void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	ymfpci_t *codec = dev_id;
 	u32 status, nvoice, mode;
-	ymfpci_voice_t *voice;
+	struct ymf_voice *voice;
+	struct ymf_capture *cap;
 
 	status = ymfpci_readl(codec, YDSXGR_STATUS);
 	if (status & 0x80000000) {
@@ -1026,8 +1033,13 @@
 		spin_lock(&codec->voice_lock);
 		for (nvoice = 0; nvoice < 64; nvoice++) {
 			voice = &codec->voices[nvoice];
-			if (voice->interrupt)
-				voice->interrupt(codec, voice);
+			if (voice->use)
+				ymf_pcm_interrupt(codec, voice);
+		}
+		for (nvoice = 0; nvoice < 5; nvoice++) {
+			cap = &codec->capture[nvoice];
+			if (cap->use)
+				ymf_cap_interrupt(codec, cap);
 		}
 		spin_unlock(&codec->voice_lock);
 	}
@@ -1039,22 +1051,32 @@
 	}
 }
 
-static void ymf_pcm_free_substream(ymfpci_pcm_t *ypcm)
+static void ymf_pcm_free_substream(struct ymf_pcm *ypcm)
 {
-	ymfpci_t *codec;
+	unsigned long flags;
+	struct ymf_unit *unit;
+
+	unit = ypcm->state->unit;
 
-	if (ypcm) {
-		codec = ypcm->codec;
+	if (ypcm->type == PLAYBACK_VOICE) {
+		spin_lock_irqsave(&unit->voice_lock, flags);
 		if (ypcm->voices[1])
-			ymfpci_voice_free(codec, ypcm->voices[1]);
+			ymfpci_voice_free(unit, ypcm->voices[1]);
 		if (ypcm->voices[0])
-			ymfpci_voice_free(codec, ypcm->voices[0]);
+			ymfpci_voice_free(unit, ypcm->voices[0]);
+		spin_unlock_irqrestore(&unit->voice_lock, flags);
+	} else {
+		if (ypcm->capture_bank_number != -1) {
+			unit->capture[ypcm->capture_bank_number].use = 0;
+			ypcm->capture_bank_number = -1;
+			ymfpci_hw_stop(unit);
+		}
 	}
 }
 
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt)
+static struct ymf_state *ymf_state_alloc(ymfpci_t *unit)
 {
-	ymfpci_pcm_t *ypcm;
+	struct ymf_pcm *ypcm;
 	struct ymf_state *state;
 
 	if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) {
@@ -1062,61 +1084,31 @@
 	}
 	memset(state, 0, sizeof(struct ymf_state));
 
-	init_waitqueue_head(&state->dmabuf.wait);
-
-	ypcm = &state->ypcm;
+	ypcm = &state->wpcm;
 	ypcm->state = state;
-	ypcm->codec = unit;
 	ypcm->type = PLAYBACK_VOICE;
+	ypcm->capture_bank_number = -1;
+	init_waitqueue_head(&ypcm->dmabuf.wait);
+
+	ypcm = &state->rpcm;
+	ypcm->state = state;
+	ypcm->type = CAPTURE_AC97;
+	ypcm->capture_bank_number = -1;
+	init_waitqueue_head(&ypcm->dmabuf.wait);
 
 	state->unit = unit;
-	state->virt = nvirt;
 
 	state->format.format = AFMT_U8;
 	state->format.rate = 8000;
 	state->format.voices = 1;
 	ymf_pcm_update_shift(&state->format);
 
-	unit->states[nvirt] = state;
-	return 0;
+	return state;
 
 out0:
-	return -ENOMEM;
+	return NULL;
 }
 
-#if HAVE_RECORD
-
-static int ymfpci_capture_open(void *private_data,
-				   snd_pcm_subchn_t * substream,
-				   u32 capture_bank_number)
-{
-	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ymfpci_pcm_t *ypcm;
-	int err;
-
-	if ((err = snd_pcm_dma_alloc(substream, !capture_bank_number ? codec->dma2ptr : codec->dma3ptr, "YMFPCI - ADC")) < 0)
-		return err;
-	ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL);
-	if (ypcm == NULL) {
-		snd_pcm_dma_free(substream);
-		return -ENOMEM;
-	}
-	ypcm->codec = codec;
-	ypcm->type = capture_bank_number + CAPTURE_REC;
-	ypcm->substream = substream;	
-	ypcm->capture_bank_number = capture_bank_number;
-	codec->capture_substream[capture_bank_number] = substream;
-	runtime->hw = &ymfpci_capture;
-	snd_pcm_set_mixer(substream, codec->mixer->device, codec->ac97->me_capture);
-	runtime->private_data = ypcm;
-	runtime->private_free = ymfpci_pcm_free_substream;
-	ymfpci_hw_start(codec);
-	return 0;
-}
-
-#endif  /* old */
-
 /* AES/IEC958 channel status bits */
 #define SND_PCM_AES0_PROFESSIONAL	(1<<0)	/* 0 = consumer, 1 = professional */
 #define SND_PCM_AES0_NONAUDIO		(1<<1)	/* 0 = audio, 1 = non-audio */
@@ -1202,24 +1194,6 @@
 #define SND_PCM_AES3_CON_CLOCK_50PPM	(1<<4)	/* 50 ppm */
 #define SND_PCM_AES3_CON_CLOCK_VARIABLE	(2<<4)	/* variable pitch */
 
-#if HAVE_RECORD  /* old */
-
-static int ymfpci_capture_close(void *private_data,
-				    snd_pcm_subchn_t * substream)
-{
-	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
-
-	if (ypcm != NULL) {
-		codec->capture_substream[ypcm->capture_bank_number] = NULL;
-		ymfpci_hw_stop(codec);
-	}
-	snd_pcm_dma_free(substream);
-	return 0;
-}
-#endif
-
 /*
  * User interface
  */
@@ -1229,21 +1203,21 @@
 	return -ESPIPE;
 }
 
-/* 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 ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
-{
-#if HAVE_RECORD
-	struct cs_state *state = (struct cs_state *)file->private_data;
-	struct dmabuf *dmabuf = &state->dmabuf;
+/*
+ * 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
+ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf;
+	DECLARE_WAITQUEUE(waita, current);
 	ssize_t ret;
 	unsigned long flags;
-	unsigned swptr;
-	int cnt;
-
-#ifdef DEBUG
-	printk("cs461x: cs_read called, count = %d\n", count);
-#endif
+	unsigned int swptr;
+	int cnt;			/* This many to go in this revolution */
 
 	if (ppos != &file->f_pos)
 		return -ESPIPE;
@@ -1255,19 +1229,14 @@
 		return -EFAULT;
 	ret = 0;
 
+	add_wait_queue(&dmabuf->wait, &waita);
 	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;
-		}
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
 		swptr = dmabuf->swptr;
 		cnt = dmabuf->dmasize - swptr;
 		if (dmabuf->count < cnt)
 			cnt = dmabuf->count;
-		spin_unlock_irqrestore(&state->card->lock, flags);
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 
 		if (cnt > count)
 			cnt = count;
@@ -1275,14 +1244,18 @@
 			unsigned long tmo;
 			/* buffer is empty, start the dma machine and wait for data to be
 			   recorded */
-			start_adc(state);
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			if (!state->rpcm.running) {
+				ymf_capture_trigger(state->unit, &state->rpcm, 1);
+			}
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
-				return ret;
+				break;
 			}
 			/* This isnt strictly right for the 810  but it'll do */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= sample_shift[dmabuf->fmt];
+			tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2);
+			tmo >>= state->format.shift;
 			/* 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
@@ -1290,50 +1263,56 @@
 			   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 "cs461x: recording schedule timeout, "
-				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
-				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
-				       dmabuf->hwptr, dmabuf->swptr);
-#endif
-				/* a buffer overrun, we delay the recovery untill next time the
-				   while loop begin and we REALLY have space to record */
+			set_current_state(TASK_INTERRUPTIBLE);
+			tmo = schedule_timeout(tmo);
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			if (tmo == 0 && dmabuf->count == 0) {
+				printk(KERN_ERR "ymfpci%d: recording schedule timeout, "
+				    "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				    state->unit->dev_audio,
+				    dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+				    dmabuf->hwptr, dmabuf->swptr);
 			}
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			if (signal_pending(current)) {
 				ret = ret ? ret : -ERESTARTSYS;
-				return ret;
+				break;
 			}
 			continue;
 		}
 
 		if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
 			if (!ret) ret = -EFAULT;
-			return ret;
+			break;
 		}
 
 		swptr = (swptr + cnt) % dmabuf->dmasize;
 
-		spin_lock_irqsave(&state->card->lock, flags);
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
 		dmabuf->swptr = swptr;
 		dmabuf->count -= cnt;
-		spin_unlock_irqrestore(&state->card->lock, flags);
+		// spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
-		start_adc(state);
+		// spin_lock_irqsave(&state->unit->reg_lock, flags);
+		if (!state->rpcm.running) {
+			ymf_capture_trigger(state->unit, &state->rpcm, 1);
+		}
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dmabuf->wait, &waita);
+
 	return ret;
-#else
-	return -EINVAL;
-#endif
 }
 
-static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+static ssize_t
+ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct ymf_state *state = (struct ymf_state *)file->private_data;
-	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf;
 	DECLARE_WAITQUEUE(waita, current);
 	ssize_t ret;
 	unsigned long flags;
@@ -1375,7 +1354,7 @@
 		}
 		if (dmabuf->count == 0) {
 			swptr = dmabuf->hwptr;
-			if (state->ypcm.running) {
+			if (state->wpcm.running) {
 				/*
 				 * Add uncertainty reserve.
 				 */
@@ -1410,8 +1389,8 @@
 			 * wait for data to be played
 			 */
 			spin_lock_irqsave(&state->unit->reg_lock, flags);
-			if (!state->ypcm.running) {
-				ymf_playback_trigger(state->unit, &state->ypcm, 1);
+			if (!state->wpcm.running) {
+				ymf_playback_trigger(state->unit, &state->wpcm, 1);
 			}
 			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			if (file->f_flags & O_NONBLOCK) {
@@ -1448,8 +1427,8 @@
 		 */
 		delay = state->format.rate / 20;	/* 50ms */
 		delay <<= state->format.shift;
-		if (dmabuf->count >= delay && !state->ypcm.running) {
-			ymf_playback_trigger(state->unit, &state->ypcm, 1);
+		if (dmabuf->count >= delay && !state->wpcm.running) {
+			ymf_playback_trigger(state->unit, &state->wpcm, 1);
 		}
 
 		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
@@ -1469,19 +1448,23 @@
 static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait)
 {
 	struct ymf_state *state = (struct ymf_state *)file->private_data;
-	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	struct ymf_dmabuf *dmabuf;
 	unsigned long flags;
 	unsigned int mask = 0;
 
-	if (file->f_mode & (FMODE_WRITE | FMODE_READ))
-		poll_wait(file, &dmabuf->wait, wait);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &state->wpcm.dmabuf.wait, wait);
+	// if (file->f_mode & FMODE_READ)
+	// 	poll_wait(file, &dmabuf->wait, wait);
 
 	spin_lock_irqsave(&state->unit->reg_lock, flags);
 	if (file->f_mode & FMODE_READ) {
+		dmabuf = &state->rpcm.dmabuf;
 		if (dmabuf->count >= (signed)dmabuf->fragsize)
 			mask |= POLLIN | POLLRDNORM;
 	}
 	if (file->f_mode & FMODE_WRITE) {
+		dmabuf = &state->wpcm.dmabuf;
 		if (dmabuf->mapped) {
 			if (dmabuf->count >= (signed)dmabuf->fragsize)
 				mask |= POLLOUT | POLLWRNORM;
@@ -1498,10 +1481,9 @@
 static int ymf_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct ymf_state *state = (struct ymf_state *)file->private_data;
-	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf;
 	int ret;
 	unsigned long size;
-	
 
 	if (vma->vm_flags & VM_WRITE) {
 		if ((ret = prog_dmabuf(state, 0)) != 0)
@@ -1529,7 +1511,7 @@
     unsigned int cmd, unsigned long arg)
 {
 	struct ymf_state *state = (struct ymf_state *)file->private_data;
-	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	struct ymf_dmabuf *dmabuf;
 	unsigned long flags;
 	audio_buf_info abinfo;
 	count_info cinfo;
@@ -1542,28 +1524,30 @@
 	case SNDCTL_DSP_RESET:
 		if (file->f_mode & FMODE_WRITE) {
 			ymf_wait_dac(state);
+			dmabuf = &state->wpcm.dmabuf;
 			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			dmabuf->ready = 0;
-			dmabuf->swptr = dmabuf->hwptr = 0;
+			dmabuf->swptr = dmabuf->hwptr;
 			dmabuf->count = dmabuf->total_bytes = 0;
 			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
-#if HAVE_RECORD
 		if (file->f_mode & FMODE_READ) {
-			stop_adc(state);
-			synchronize_irq();
+			ymf_stop_adc(state);
+			dmabuf = &state->rpcm.dmabuf;
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			dmabuf->ready = 0;
-			dmabuf->swptr = dmabuf->hwptr = 0;
+			dmabuf->swptr = dmabuf->hwptr;
 			dmabuf->count = dmabuf->total_bytes = 0;
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
-#endif
 		return 0;
 
 	case SNDCTL_DSP_SYNC:
 		if (file->f_mode & FMODE_WRITE) {
+			dmabuf = &state->wpcm.dmabuf;
 			if (file->f_flags & O_NONBLOCK) {
 				spin_lock_irqsave(&state->unit->reg_lock, flags);
-				if (dmabuf->count != 0 && !state->ypcm.running) {
+				if (dmabuf->count != 0 && !state->wpcm.running) {
 					ymf_start_dac(state);
 				}
 				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
@@ -1571,6 +1555,7 @@
 				ymf_wait_dac(state);
 			}
 		}
+		/* XXX What does this do for reading? dmabuf->count=0; ? */
 		return 0;
 
 	case SNDCTL_DSP_SPEED: /* set smaple rate */
@@ -1579,17 +1564,22 @@
 		if (val >= 8000 && val <= 48000) {
 			if (file->f_mode & FMODE_WRITE) {
 				ymf_wait_dac(state);
+				dmabuf = &state->wpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.rate = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			}
-#if HAVE_RECORD
 			if (file->f_mode & FMODE_READ) {
-				stop_adc(state);
+				ymf_stop_adc(state);
+				dmabuf = &state->rpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.rate = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			}
-#endif
-			spin_lock_irqsave(&state->unit->reg_lock, flags);
-			dmabuf->ready = 0;
-			state->format.rate = val;
-			ymf_pcm_update_shift(&state->format);
-			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
 		return put_user(state->format.rate, (int *)arg);
 
@@ -1597,41 +1587,41 @@
 	 * OSS manual does not mention SNDCTL_DSP_STEREO at all.
 	 * All channels are mono and if you want stereo, you
 	 * play into two channels with SNDCTL_DSP_CHANNELS.
-	 * However, mpg123 uses it. I wonder, why Michael Hipp uses it.
+	 * However, mpg123 calls it. I wonder, why Michael Hipp used it.
 	 */
 	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 		if (file->f_mode & FMODE_WRITE) {
 			ymf_wait_dac(state); 
+			dmabuf = &state->wpcm.dmabuf;
 			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			dmabuf->ready = 0;
 			state->format.voices = val ? 2 : 1;
 			ymf_pcm_update_shift(&state->format);
 			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
-#if HAVE_RECORD
 		if (file->f_mode & FMODE_READ) {
-			/* stop_adc(state); */
+			ymf_stop_adc(state);
+			dmabuf = &state->rpcm.dmabuf;
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
 			dmabuf->ready = 0;
-			if(val)
-				dmabuf->fmt |= CS_FMT_STEREO;
-			else
-				dmabuf->fmt &= ~CS_FMT_STEREO;
+			state->format.voices = val ? 2 : 1;
+			ymf_pcm_update_shift(&state->format);
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
-#endif
 		return 0;
 
 	case SNDCTL_DSP_GETBLKSIZE:
 		if (file->f_mode & FMODE_WRITE) {
 			if ((val = prog_dmabuf(state, 0)))
 				return val;
-			return put_user(dmabuf->fragsize, (int *)arg);
+			return put_user(state->wpcm.dmabuf.fragsize, (int *)arg);
 		}
 		if (file->f_mode & FMODE_READ) {
 			if ((val = prog_dmabuf(state, 1)))
 				return val;
-			return put_user(dmabuf->fragsize, (int *)arg);
+			return put_user(state->rpcm.dmabuf.fragsize, (int *)arg);
 		}
 		return -EINVAL;
 
@@ -1644,17 +1634,22 @@
 		if (val == AFMT_S16_LE || val == AFMT_U8) {
 			if (file->f_mode & FMODE_WRITE) {
 				ymf_wait_dac(state);
+				dmabuf = &state->wpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.format = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			}
-#if HAVE_RECORD
 			if (file->f_mode & FMODE_READ) {
-				stop_adc(state);
+				ymf_stop_adc(state);
+				dmabuf = &state->rpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.format = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			}
-#endif
-			spin_lock_irqsave(&state->unit->reg_lock, flags);
-			dmabuf->ready = 0;
-			state->format.format = val;
-			ymf_pcm_update_shift(&state->format);
-			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		}
 		return put_user(state->format.format, (int *)arg);
 
@@ -1667,20 +1662,24 @@
 				ymf_wait_dac(state);
 				if (val == 1 || val == 2) {
 					spin_lock_irqsave(&state->unit->reg_lock, flags);
+					dmabuf = &state->wpcm.dmabuf;
 					dmabuf->ready = 0;
 					state->format.voices = val;
 					ymf_pcm_update_shift(&state->format);
 					spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 				}
 			}
-#if HAVE_RECORD
 			if (file->f_mode & FMODE_READ) {
-				spin_lock_irqsave(&state->unit->reg_lock, flags);
-				stop_adc(state);
-				dmabuf->ready = 0;
-				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+				ymf_stop_adc(state);
+				if (val == 1 || val == 2) {
+					spin_lock_irqsave(&state->unit->reg_lock, flags);
+					dmabuf = &state->rpcm.dmabuf;
+					dmabuf->ready = 0;
+					state->format.voices = val;
+					ymf_pcm_update_shift(&state->format);
+					spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+				}
 			}
-#endif
 		}
 		return put_user(state->format.voices, (int *)arg);
 
@@ -1696,15 +1695,16 @@
 		 * The paragraph above is a clumsy way to say "flush ioctl".
 		 * This ioctl is used by mpg123.
 		 */
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_POST\n"); */
 		spin_lock_irqsave(&state->unit->reg_lock, flags);
-		if (dmabuf->count != 0 && !state->ypcm.running) {
+		if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) {
 			ymf_start_dac(state);
 		}
 		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		return 0;
 
+#if 0 /* XXX Was dummy implementation anyways. Make sense of this. */
 	case SNDCTL_DSP_SUBDIVIDE:
+		dmabuf = &state->wpcm.dmabuf;
 		if (dmabuf->subdivision)
 			return -EINVAL;
 		if (get_user(val, (int *)arg))
@@ -1713,6 +1713,7 @@
 			return -EINVAL;
 		dmabuf->subdivision = val;
 		return 0;
+#endif
 
 	case SNDCTL_DSP_SETFRAGMENT:
 		if (get_user(val, (int *)arg))
@@ -1720,6 +1721,7 @@
 	/* P3: these frags are for Doom. Amasingly, it sets [2,2**11]. */
 	/* P3 */ // printk("ymfpci: ioctl SNDCTL_DSP_SETFRAGMENT 0x%x\n", val);
 
+		dmabuf = &state->wpcm.dmabuf;
 		dmabuf->ossfragshift = val & 0xffff;
 		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
 		switch (dmabuf->ossmaxfrags) {
@@ -1736,10 +1738,10 @@
 	case SNDCTL_DSP_GETOSPACE:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		dmabuf = &state->wpcm.dmabuf;
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
 			return val;
 		spin_lock_irqsave(&state->unit->reg_lock, flags);
-		/* cs_update_ptr(state); */  /* XXX Always up to date? */
 		abinfo.fragsize = dmabuf->fragsize;
 		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
 		abinfo.fragstotal = dmabuf->numfrag;
@@ -1747,21 +1749,19 @@
 		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
 
-#if HAVE_RECORD
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
+		dmabuf = &state->rpcm.dmabuf;
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
 			return val;
-		spin_lock_irqsave(&state->card->lock, flags);
-		cs_update_ptr(state);
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
 		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);
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-#endif
 
 	case SNDCTL_DSP_NONBLOCK:
 		file->f_flags |= O_NONBLOCK;
@@ -1772,9 +1772,10 @@
 			    (int *)arg); */
 		return put_user(0, (int *)arg);
 
-#if 0 /* old */
+#if 0 /* not implememnted, yet? */
 	case SNDCTL_DSP_GETTRIGGER:
 		val = 0;
+		dmabuf = &state->wpcm.dmabuf;
 		if (file->f_mode & FMODE_READ && dmabuf->enable)
 			val |= PCM_ENABLE_INPUT;
 		if (file->f_mode & FMODE_WRITE && dmabuf->enable)
@@ -1785,6 +1786,7 @@
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 		if (file->f_mode & FMODE_READ) {
+			dmabuf = &state->rpcm.dmabuf;
 			if (val & PCM_ENABLE_INPUT) {
 				if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
 					return ret;
@@ -1793,6 +1795,7 @@
 				stop_adc(state);
 		}
 		if (file->f_mode & FMODE_WRITE) {
+			dmabuf = &state->wpcm.dmabuf;
 			if (val & PCM_ENABLE_OUTPUT) {
 				if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
 					return ret;
@@ -1801,48 +1804,48 @@
 				stop_dac(state);
 		}
 		return 0;
-
 #endif
 
-#if HAVE_RECORD
 	case SNDCTL_DSP_GETIPTR:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
+		dmabuf = &state->rpcm.dmabuf;
 		spin_lock_irqsave(&state->unit->reg_lock, flags);
-		cs_update_ptr(state);
 		cinfo.bytes = dmabuf->total_bytes;
 		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
 		cinfo.ptr = dmabuf->hwptr;
+		/* XXX fishy - breaks invariant  count=hwptr-swptr */
 		if (dmabuf->mapped)
 			dmabuf->count &= dmabuf->fragsize-1;
 		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
-		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
-#endif
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
 
 	case SNDCTL_DSP_GETOPTR:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
+		dmabuf = &state->wpcm.dmabuf;
 		spin_lock_irqsave(&state->unit->reg_lock, flags);
-		/* cs_update_ptr(state); */  /* Always up to date */
 		cinfo.bytes = dmabuf->total_bytes;
 		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
 		cinfo.ptr = dmabuf->hwptr;
+		/* XXX fishy - breaks invariant  count=swptr-hwptr */
 		if (dmabuf->mapped)
 			dmabuf->count &= dmabuf->fragsize-1;
 		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
-		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
 
 	case SNDCTL_DSP_SETDUPLEX:	/* XXX TODO */
 		return -EINVAL;
 
-#if 0 /* old */
+#if 0 /* XXX implement when an app found that uses it. */
 	case SNDCTL_DSP_GETODELAY:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
 		spin_lock_irqsave(&state->unit->reg_lock, flags);
 		cs_update_ptr(state);
+		dmabuf = &state->wpcm.dmabuf;
 		val = dmabuf->count;
-		spin_unlock_irqrestore(&state->card->lock, flags);
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		return put_user(val, (int *)arg);
 #endif
 
@@ -1850,7 +1853,7 @@
 		return put_user(state->format.rate, (int *)arg);
 
 	case SOUND_PCM_READ_CHANNELS:
-		return put_user(state->format.voices,	(int *)arg);
+		return put_user(state->format.voices, (int *)arg);
 
 	case SOUND_PCM_READ_BITS:
 		return put_user(AFMT_S16_LE, (int *)arg);
@@ -1873,28 +1876,25 @@
 	return -ENOTTY;
 }
 
+/*
+ * open(2)
+ * We use upper part of the minor to distinguish between soundcards.
+ * Channels are opened with a clone open.
+ */
 static int ymf_open(struct inode *inode, struct file *file)
 {
 	struct list_head *list;
 	ymfpci_t *unit;
 	int minor;
 	struct ymf_state *state;
-	int nvirt;
 	int err;
 
-	/*
-	 * This is how we do it currently: only one channel is used
-	 * in every board, so that we could use several boards in one machine.
-	 * We waste 63 out of 64 playback slots, but so what.
-	 * OSS model is constructed for devices with single playback channel.
-	 */
 	minor = MINOR(inode->i_rdev);
 	if ((minor & 0x0F) == 3) {	/* /dev/dspN */
 		;
 	} else {
 		return -ENXIO;
 	}
-	nvirt = 0;			/* Such is the partitioning of minor */
 
 	for (list = ymf_devs.next; list != &ymf_devs; list = list->next) {
 		unit = list_entry(list, ymfpci_t, ymf_devs);
@@ -1905,34 +1905,36 @@
 		return -ENODEV;
 
 	down(&unit->open_sem);
-	if (unit->states[nvirt] != NULL) {
-		up(&unit->open_sem);
-		return -EBUSY;
-	}
 
-	if ((err = ymf_state_alloc(unit, nvirt)) != 0) {
+	if ((state = ymf_state_alloc(unit)) == NULL) {
 		up(&unit->open_sem);
-		return err;
+		return -ENOMEM;
 	}
-	state = unit->states[nvirt];
+	list_add_tail(&state->chain, &unit->states);
 
 	file->private_data = state;
 
 	/*
-	 * XXX This ymf_playback_prepare is totally unneeded here.
-	 * The question is if we want to allow write to fail if
-	 * prog_dmabuf fails... Say, no memory in DMA zone?
+	 * ymf_read and ymf_write that we borrowed from cs46xx
+	 * allocate buffers with prog_dmabuf(). We call prog_dmabuf
+	 * here so that in case of DMA memory exhaustion open
+	 * fails rather than write.
+	 *
+	 * XXX prog_dmabuf allocates voice. Should allocate explicitly, above.
 	 */
-	if ((err = ymf_playback_prepare(unit, state)) != 0) {
-		/* XXX This recovery is ugly as hell. */
-
-		ymf_pcm_free_substream(&state->ypcm);
-
-		unit->states[state->virt] = NULL;
-		kfree(state);
-
-		up(&unit->open_sem);
-		return err;
+	if (file->f_mode & FMODE_WRITE) {
+		if (!state->wpcm.dmabuf.ready) {
+			if ((err = prog_dmabuf(state, 0)) != 0) {
+				goto out_nodma;
+			}
+		}
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!state->rpcm.dmabuf.ready) {
+			if ((err = prog_dmabuf(state, 1)) != 0) {
+				goto out_nodma;
+			}
+		}
 	}
 
 #if 0 /* test if interrupts work */
@@ -1941,10 +1943,26 @@
 	    (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));
 #endif
 	up(&unit->open_sem);
-	/* XXX Is it correct to have MOD_INC_USE_COUNT outside of sem.? */
 
 	MOD_INC_USE_COUNT;
 	return 0;
+
+out_nodma:
+	/*
+	 * XXX Broken custom: "goto out_xxx" in other place is
+	 * a nestable exception, but here it is not nestable due to semaphore.
+	 * XXX Doubtful technique of self-describing objects....
+	 */
+	dealloc_dmabuf(&state->wpcm.dmabuf);
+	dealloc_dmabuf(&state->rpcm.dmabuf);
+	ymf_pcm_free_substream(&state->wpcm);
+	ymf_pcm_free_substream(&state->rpcm);
+
+	list_del(&state->chain);
+	kfree(state);
+
+	up(&unit->open_sem);
+	return err;
 }
 
 static int ymf_release(struct inode *inode, struct file *file)
@@ -1956,12 +1974,6 @@
 	ymfpci_writeb(codec, YDSXGR_TIMERCTRL, 0);
 #endif
 
-	if (state != codec->states[state->virt]) {
-		printk(KERN_ERR "ymfpci%d.%d: state mismatch\n",
-		    state->unit->dev_audio, state->virt);
-		return -EIO;
-	}
-
 	down(&codec->open_sem);
 
 	/*
@@ -1969,10 +1981,14 @@
 	 * Deallocate when unloading the driver and we can wait.
 	 */
 	ymf_wait_dac(state);
-	dealloc_dmabuf(state);
-	ymf_pcm_free_substream(&state->ypcm);
+	ymf_stop_adc(state);		/* fortunately, it's immediate */
+	dealloc_dmabuf(&state->wpcm.dmabuf);
+	dealloc_dmabuf(&state->rpcm.dmabuf);
+	ymf_pcm_free_substream(&state->wpcm);
+	ymf_pcm_free_substream(&state->rpcm);
 
-	codec->states[state->virt] = NULL;
+	list_del(&state->chain);
+	file->private_data = NULL;	/* Can you tell I programmed Solaris */
 	kfree(state);
 
 	up(&codec->open_sem);
@@ -2045,6 +2061,83 @@
  *  initialization routines
  */
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+
+static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev)
+{
+	int v;
+	int mpuio = -1, oplio = -1;
+
+	switch (unit->iomidi) {
+	case 0x330:
+		mpuio = 0;
+		break;
+	case 0x300:
+		mpuio = 1;
+		break;
+	case 0x332:
+		mpuio = 2;
+		break;
+	case 0x334:
+		mpuio = 3;
+		break;
+	default: ;
+	}
+
+	switch (unit->iosynth) {
+	case 0x388:
+		oplio = 0;
+		break;
+	case 0x398:
+		oplio = 1;
+		break;
+	case 0x3a0:
+		oplio = 2;
+		break;
+	case 0x3a8:
+		oplio = 3;
+		break;
+	default: ;
+	}
+
+	if (mpuio >= 0 || oplio >= 0) {
+		v = 0x003e;
+		pci_write_config_word(pcidev, PCIR_LEGCTRL, v);
+	
+		switch (pcidev->device) {
+		case PCI_DEVICE_ID_YAMAHA_724:
+		case PCI_DEVICE_ID_YAMAHA_740:
+		case PCI_DEVICE_ID_YAMAHA_724F:
+		case PCI_DEVICE_ID_YAMAHA_740C:
+			v = 0x8800;
+			if (mpuio >= 0) { v |= mpuio<<4; }
+			if (oplio >= 0) { v |= oplio; }
+			pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+			break;
+
+		case PCI_DEVICE_ID_YAMAHA_744:
+		case PCI_DEVICE_ID_YAMAHA_754:
+			v = 0x8800;
+			pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+			if (oplio >= 0) {
+				pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth);
+			}
+			if (mpuio >= 0) {
+				pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi);
+			}
+			break;
+
+		default:
+			printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n",
+			    pcidev->device);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
 static void ymfpci_aclink_reset(struct pci_dev * pci)
 {
 	u8 cmd;
@@ -2149,6 +2242,12 @@
 	ptr += 0x00ff;
 	(long)ptr &= ~0x00ff;
 
+	/*
+	 * Hardware requires only ptr[playback_ctrl_size] zeroed,
+	 * but in our judgement it is a wrong kind of savings, so clear it all.
+	 */
+	memset(ptr, 0, size);
+
 	codec->bank_base_playback = ptr;
 	codec->ctrl_playback = (u32 *)ptr;
 	codec->ctrl_playback[0] = YDSXG_PLAYBACK_VOICES;
@@ -2213,7 +2312,7 @@
 	kfree(codec->work_ptr);
 }
 
-static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
+static int ymf_ac97_init(ymfpci_t *unit, int num_ac97)
 {
 	struct ac97_codec *codec;
 	u16 eid;
@@ -2224,7 +2323,7 @@
 
 	/* initialize some basic codec information, other fields will be filled
 	   in ac97_probe_codec */
-	codec->private_data = card;
+	codec->private_data = unit;
 	codec->id = num_ac97;
 
 	codec->codec_read = ymfpci_codec_read;
@@ -2241,14 +2340,14 @@
 		goto out_kfree;
 	}
 
-	card->ac97_features = eid;
+	unit->ac97_features = eid;
 
 	if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) {
 		printk(KERN_ERR "ymfpci: couldn't register mixer!\n");
 		goto out_kfree;
 	}
 
-	card->ac97_codec[num_ac97] = codec;
+	unit->ac97_codec[num_ac97] = codec;
 
 	return 0;
  out_kfree:
@@ -2256,6 +2355,19 @@
 	return -ENODEV;
 }
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+# ifdef MODULE
+static int mpu_io     = 0;
+static int synth_io   = 0;
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(synth_io, "i");
+# else
+static int mpu_io     = 0x330;
+static int synth_io   = 0x388;
+# endif
+static int assigned;
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
 static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent)
 {
 	u16 ctrl;
@@ -2277,10 +2389,15 @@
 	spin_lock_init(&codec->reg_lock);
 	spin_lock_init(&codec->voice_lock);
 	init_MUTEX(&codec->open_sem);
+	INIT_LIST_HEAD(&codec->states);
 	codec->pci = pcidev;
 
 	pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev);
 	codec->reg_area_virt = ioremap(pci_resource_start(pcidev, 0), 0x8000);
+	if (codec->reg_area_virt == NULL) {
+		printk(KERN_ERR "ymfpci: unable to map registers\n");
+		goto out_free;
+	}
 
 	printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n",
 	    (char *)ent->driver_data, pci_resource_start(pcidev, 0), pcidev->irq);
@@ -2289,6 +2406,16 @@
 	if (ymfpci_codec_ready(codec, 0, 1) < 0)
 		goto out_unmap;
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+	if (assigned == 0) {
+		codec->iomidi = mpu_io;
+		codec->iosynth = synth_io;
+		if (ymfpci_setup_legacy(codec, pcidev) < 0)
+			goto out_unmap;
+		assigned = 1;
+	}
+#endif
+
 	ymfpci_download_image(codec);
 
 	udelay(100); /* seems we need some delay after downloading image.. */
@@ -2296,11 +2423,11 @@
 	if (ymfpci_memalloc(codec) < 0)
 		goto out_disable_dsp;
 
-	/* ymfpci_proc_init(card, codec); */
+	/* ymfpci_proc_init(unit, codec); */
 
 	if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
-		printk(KERN_ERR "ymfpci%d: unable to request IRQ %d\n",
-		       codec->dev_audio, pcidev->irq);
+		printk(KERN_ERR "ymfpci: unable to request IRQ %d\n",
+		    pcidev->irq);
 		goto out_memfree;
 	}
 
@@ -2317,6 +2444,23 @@
 	if ((err = ymf_ac97_init(codec, 0)) != 0)
 		goto out_unregister_sound_dsp;
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+	codec->opl3_data.name = "ymfpci";
+	codec->mpu_data.name  = "ymfpci";
+
+	codec->opl3_data.io_base = codec->iosynth;
+	codec->opl3_data.irq     = -1;
+
+	codec->mpu_data.io_base  = codec->iomidi;
+	codec->mpu_data.irq      = -1;	/* XXX Make it ours. */
+
+	if (codec->iomidi) {
+		if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) {
+			codec->iomidi = 0;	/* XXX kludge */
+		}
+	}
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
+
 	/* put it into driver list */
 	list_add_tail(&codec->ymf_devs, &ymf_devs);
 	pci_set_drvdata(pcidev, codec);
@@ -2336,6 +2480,7 @@
 	ymfpci_writel(codec, YDSXGR_STATUS, ~0);
  out_unmap:
 	iounmap(codec->reg_area_virt);
+ out_free:
 	kfree(codec);
 	return -ENODEV;
 }
@@ -2358,6 +2503,11 @@
 	ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
 	ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
 	iounmap(codec->reg_area_virt);
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+	if (codec->iomidi) {
+		unload_uart401(&codec->mpu_data);
+	}
+#endif /* CONFIG_SOUND_YMFPCI_LEGACY */
 	kfree(codec);
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)