## Automatically generated incremental diff
## From:   linux-2.4.1-pre10
## To:     linux-2.4.1-pre11
## Robot:  $Id: make-incremental-diff,v 1.8 2001/12/04 06:05:04 hpa Exp $

diff -urN linux-2.4.1-pre10/Makefile linux/Makefile
--- linux-2.4.1-pre10/Makefile	Mon Dec  3 22:26:27 2001
+++ linux/Makefile	Mon Dec  3 22:26:35 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 1
-EXTRAVERSION =-pre10
+EXTRAVERSION =-pre11
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.1-pre10/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile
--- linux-2.4.1-pre10/arch/alpha/kernel/Makefile	Fri Dec 29 14:07:19 2000
+++ linux/arch/alpha/kernel/Makefile	Mon Dec  3 22:26:35 2001
@@ -23,7 +23,7 @@
 # FIXME!
 # These should be made conditional on the stuff that needs them!
 #
-obj-y	 += irq_i8259.o irq_srm.o irq_pyxis.o \
+obj-y	 += irq_i8259.o irq_srm.o \
 	    es1888.o smc37c669.o smc37c93x.o ns87312.o
 
 ifdef CONFIG_VGA_HOSE
@@ -43,7 +43,7 @@
 	    sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o sys_titan.o \
 	    sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \
 	    sys_sable.o sys_sio.o sys_sx164.o sys_takara.o sys_rx164.o \
-	    sys_wildfire.o core_wildfire.o
+	    sys_wildfire.o core_wildfire.o irq_pyxis.o
 
 else
 
@@ -93,6 +93,10 @@
 obj-$(CONFIG_ALPHA_SX164) += sys_sx164.o
 obj-$(CONFIG_ALPHA_TAKARA) += sys_takara.o
 obj-$(CONFIG_ALPHA_WILDFIRE) += sys_wildfire.o
+
+ifneq ($(CONFIG_ALPHA_MIATA)$(CONFIG_ALPHA_RUFFIAN)$(CONFIG_ALPHA_SX164),)
+obj-y    += irq_pyxis.o
+endif
 
 endif # GENERIC
 
diff -urN linux-2.4.1-pre10/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- linux-2.4.1-pre10/arch/alpha/kernel/osf_sys.c	Fri Aug 11 14:29:04 2000
+++ linux/arch/alpha/kernel/osf_sys.c	Mon Dec  3 22:26:35 2001
@@ -904,7 +904,6 @@
 extern int do_getitimer(int which, struct itimerval *value);
 extern int do_setitimer(int which, struct itimerval *, struct itimerval *);
 asmlinkage int sys_utimes(char *, struct timeval *);
-extern int sys_wait4(pid_t, int *, int, struct rusage *);
 extern int do_adjtimex(struct timex *);
 
 struct timeval32
diff -urN linux-2.4.1-pre10/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c
--- linux-2.4.1-pre10/arch/alpha/kernel/signal.c	Sun Sep  3 11:48:33 2000
+++ linux/arch/alpha/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -30,7 +30,6 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-asmlinkage int sys_wait4(int, int *, int, struct rusage *);
 asmlinkage void ret_from_sys_call(void);
 asmlinkage int do_signal(sigset_t *, struct pt_regs *,
 			 struct switch_stack *, unsigned long, unsigned long);
diff -urN linux-2.4.1-pre10/arch/alpha/kernel/sys_cabriolet.c linux/arch/alpha/kernel/sys_cabriolet.c
--- linux-2.4.1-pre10/arch/alpha/kernel/sys_cabriolet.c	Fri Oct 27 10:55:01 2000
+++ linux/arch/alpha/kernel/sys_cabriolet.c	Mon Dec  3 22:26:35 2001
@@ -42,7 +42,7 @@
 cabriolet_update_irq_hw(unsigned int irq, unsigned long mask)
 {
 	int ofs = (irq - 16) / 8;
-	outb(mask >> (16 + ofs*3), 0x804 + ofs);
+	outb(mask >> (16 + ofs * 8), 0x804 + ofs);
 }
 
 static inline void
diff -urN linux-2.4.1-pre10/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c
--- linux-2.4.1-pre10/arch/arm/kernel/signal.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -32,8 +32,6 @@
 #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn))
 #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn))
 
-asmlinkage int sys_wait4(pid_t pid, unsigned long * stat_addr,
-			 int options, unsigned long *ru);
 asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
 
 int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
diff -urN linux-2.4.1-pre10/arch/ia64/ia32/sys_ia32.c linux/arch/ia64/ia32/sys_ia32.c
--- linux-2.4.1-pre10/arch/ia64/ia32/sys_ia32.c	Thu Jan  4 12:50:17 2001
+++ linux/arch/ia64/ia32/sys_ia32.c	Mon Dec  3 22:26:35 2001
@@ -1957,9 +1957,6 @@
 	return err;
 }
 
-extern asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr,
-				int options, struct rusage * ru);
-
 asmlinkage long
 sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options,
 	    struct rusage32 *ru)
diff -urN linux-2.4.1-pre10/arch/m68k/kernel/signal.c linux/arch/m68k/kernel/signal.c
--- linux-2.4.1-pre10/arch/m68k/kernel/signal.c	Mon Nov 27 18:02:06 2000
+++ linux/arch/m68k/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -47,8 +47,6 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-			 int options, unsigned long *ru);
 asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
 
 const int frame_extra_sizes[16] = {
diff -urN linux-2.4.1-pre10/arch/mips/kernel/irixsig.c linux/arch/mips/kernel/irixsig.c
--- linux-2.4.1-pre10/arch/mips/kernel/irixsig.c	Sun Sep  3 11:50:03 2000
+++ linux/arch/mips/kernel/irixsig.c	Mon Dec  3 22:26:35 2001
@@ -16,8 +16,6 @@
 #include <asm/ptrace.h>
 #include <asm/uaccess.h>
 
-asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-                         int options, unsigned long *ru);
 extern asmlinkage void syscall_trace(void);
 
 #undef DEBUG_SIG
diff -urN linux-2.4.1-pre10/arch/mips/kernel/signal.c linux/arch/mips/kernel/signal.c
--- linux-2.4.1-pre10/arch/mips/kernel/signal.c	Sun Sep  3 11:49:09 2000
+++ linux/arch/mips/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -30,8 +30,6 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-extern asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-                         int options, unsigned long *ru);
 extern asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
 extern asmlinkage int save_fp_context(struct sigcontext *sc);
 extern asmlinkage int restore_fp_context(struct sigcontext *sc);
diff -urN linux-2.4.1-pre10/arch/mips64/kernel/linux32.c linux/arch/mips64/kernel/linux32.c
--- linux-2.4.1-pre10/arch/mips64/kernel/linux32.c	Tue Nov 28 21:42:04 2000
+++ linux/arch/mips64/kernel/linux32.c	Mon Dec  3 22:26:35 2001
@@ -585,9 +585,6 @@
 	return err;
 }
 
-extern asmlinkage int sys_wait4(pid_t pid, unsigned int * stat_addr,
-				int options, struct rusage * ru);
-
 asmlinkage int
 sys32_wait4(__kernel_pid_t32 pid, unsigned int * stat_addr, int options,
 	    struct rusage32 * ru)
diff -urN linux-2.4.1-pre10/arch/mips64/kernel/signal.c linux/arch/mips64/kernel/signal.c
--- linux-2.4.1-pre10/arch/mips64/kernel/signal.c	Sun Sep  3 11:53:42 2000
+++ linux/arch/mips64/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -31,8 +31,6 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-extern asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-                         int options, unsigned long *ru);
 extern asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
 extern asmlinkage int save_fp_context(struct sigcontext *sc);
 extern asmlinkage int restore_fp_context(struct sigcontext *sc);
diff -urN linux-2.4.1-pre10/arch/mips64/kernel/signal32.c linux/arch/mips64/kernel/signal32.c
--- linux-2.4.1-pre10/arch/mips64/kernel/signal32.c	Tue Nov 28 21:42:04 2000
+++ linux/arch/mips64/kernel/signal32.c	Mon Dec  3 22:26:35 2001
@@ -30,8 +30,6 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-extern asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-                         int options, unsigned long *ru);
 extern asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs *regs);
 extern asmlinkage int save_fp_context(struct sigcontext *sc);
 extern asmlinkage int restore_fp_context(struct sigcontext *sc);
diff -urN linux-2.4.1-pre10/arch/ppc/kernel/open_pic.h linux/arch/ppc/kernel/open_pic.h
--- linux-2.4.1-pre10/arch/ppc/kernel/open_pic.h	Mon Dec  3 22:26:27 2001
+++ linux/arch/ppc/kernel/open_pic.h	Mon Dec  3 22:26:35 2001
@@ -12,6 +12,8 @@
 #ifndef _PPC_KERNEL_OPEN_PIC_H
 #define _PPC_KERNEL_OPEN_PIC_H
 
+#include <linux/config.h>
+
 #define OPENPIC_SIZE	0x40000
 
 /* OpenPIC IRQ controller structure */
diff -urN linux-2.4.1-pre10/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- linux-2.4.1-pre10/arch/ppc/kernel/pci.c	Mon Dec  3 22:26:27 2001
+++ linux/arch/ppc/kernel/pci.c	Mon Dec  3 22:26:35 2001
@@ -3,6 +3,7 @@
  * Common pmac/prep/chrp pci routines. -- Cort
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
diff -urN linux-2.4.1-pre10/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- linux-2.4.1-pre10/arch/ppc/kernel/prep_pci.c	Mon Dec  3 22:26:27 2001
+++ linux/arch/ppc/kernel/prep_pci.c	Mon Dec  3 22:26:35 2001
@@ -7,6 +7,7 @@
  * The motherboard routes/maps will disappear shortly. -- Cort
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
diff -urN linux-2.4.1-pre10/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c
--- linux-2.4.1-pre10/arch/ppc/kernel/signal.c	Sun Sep 17 09:48:07 2000
+++ linux/arch/ppc/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -53,8 +53,6 @@
 #define MSR_USERCHANGE	(MSR_FE0 | MSR_FE1)
 
 int do_signal(sigset_t *oldset, struct pt_regs *regs);
-extern int sys_wait4(pid_t pid, unsigned long *stat_addr,
-		     int options, unsigned long *ru);
 
 int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
 {
diff -urN linux-2.4.1-pre10/arch/s390/kernel/signal.c linux/arch/s390/kernel/signal.c
--- linux-2.4.1-pre10/arch/s390/kernel/signal.c	Sun Sep  3 11:53:42 2000
+++ linux/arch/s390/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -52,8 +52,6 @@
 	struct ucontext uc;
 } rt_sigframe;
 
-asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-			 int options, unsigned long *ru);
 asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
 
 /*
diff -urN linux-2.4.1-pre10/arch/sh/kernel/signal.c linux/arch/sh/kernel/signal.c
--- linux-2.4.1-pre10/arch/sh/kernel/signal.c	Sun Sep  3 11:53:42 2000
+++ linux/arch/sh/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -29,8 +29,6 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-			 int options, unsigned long *ru);
 asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
 
 int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
diff -urN linux-2.4.1-pre10/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c
--- linux-2.4.1-pre10/arch/sparc/kernel/signal.c	Thu Sep  7 08:32:00 2000
+++ linux/arch/sparc/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.107 2000/09/05 21:44:54 davem Exp $
+/*  $Id: signal.c,v 1.108 2001/01/24 21:05:12 davem Exp $
  *  linux/arch/sparc/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -27,9 +27,6 @@
 #include <asm/pgtable.h>
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
-asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-			 int options, unsigned long *ru);
 
 extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
 		   void *fpqueue, unsigned long *fpqdepth);
diff -urN linux-2.4.1-pre10/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- linux-2.4.1-pre10/arch/sparc/kernel/sys_sunos.c	Sat Aug 12 12:08:49 2000
+++ linux/arch/sparc/kernel/sys_sunos.c	Mon Dec  3 22:26:35 2001
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.130 2000/08/12 13:25:41 davem Exp $
+/* $Id: sys_sunos.c,v 1.131 2001/01/24 21:05:12 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -834,7 +834,6 @@
 }
 
 /* So stupid... */
-extern asmlinkage int sys_wait4(pid_t, unsigned int *, int, struct rusage *);
 asmlinkage int sunos_wait4(pid_t pid, unsigned int *stat_addr, int options, struct rusage *ru)
 {
 	int ret;
diff -urN linux-2.4.1-pre10/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c
--- linux-2.4.1-pre10/arch/sparc64/kernel/signal.c	Thu Sep  7 08:32:00 2000
+++ linux/arch/sparc64/kernel/signal.c	Mon Dec  3 22:26:35 2001
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.54 2000/09/05 21:44:54 davem Exp $
+/*  $Id: signal.c,v 1.55 2001/01/24 21:05:13 davem Exp $
  *  arch/sparc64/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -30,9 +30,6 @@
 #include <asm/visasm.h>
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
-asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-			 int options, unsigned long *ru);
 
 asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs,
 			 unsigned long orig_o0, int ret_from_syscall);
diff -urN linux-2.4.1-pre10/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c
--- linux-2.4.1-pre10/arch/sparc64/kernel/signal32.c	Thu Sep  7 08:32:00 2000
+++ linux/arch/sparc64/kernel/signal32.c	Mon Dec  3 22:26:35 2001
@@ -1,4 +1,4 @@
-/*  $Id: signal32.c,v 1.67 2000/09/05 21:44:54 davem Exp $
+/*  $Id: signal32.c,v 1.68 2001/01/24 21:05:13 davem Exp $
  *  arch/sparc64/kernel/signal32.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -28,9 +28,6 @@
 #include <asm/visasm.h>
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
-asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
-			 int options, unsigned long *ru);
 
 asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs *regs,
 			 unsigned long orig_o0, int ret_from_syscall);
diff -urN linux-2.4.1-pre10/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.1-pre10/arch/sparc64/kernel/sys_sparc32.c	Fri Dec 29 14:07:21 2000
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Mon Dec  3 22:26:35 2001
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.171 2000/12/13 16:34:55 davem Exp $
+/* $Id: sys_sparc32.c,v 1.172 2001/01/24 21:05:13 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -1793,9 +1793,6 @@
 	err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw);
 	return err;
 }
-
-extern asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr,
-				int options, struct rusage * ru);
 
 asmlinkage int sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options, struct rusage32 *ru)
 {
diff -urN linux-2.4.1-pre10/drivers/acpi/acpi_ksyms.c linux/drivers/acpi/acpi_ksyms.c
--- linux-2.4.1-pre10/drivers/acpi/acpi_ksyms.c	Mon Dec  3 22:26:28 2001
+++ linux/drivers/acpi/acpi_ksyms.c	Mon Dec  3 22:26:35 2001
@@ -18,7 +18,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
diff -urN linux-2.4.1-pre10/drivers/acpi/include/aclinux.h linux/drivers/acpi/include/aclinux.h
--- linux-2.4.1-pre10/drivers/acpi/include/aclinux.h	Mon Dec  3 22:26:28 2001
+++ linux/drivers/acpi/include/aclinux.h	Mon Dec  3 22:26:35 2001
@@ -29,7 +29,6 @@
 
 #define ACPI_OS_NAME                "Linux"
 
-#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/ctype.h>
diff -urN linux-2.4.1-pre10/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-2.4.1-pre10/drivers/block/ll_rw_blk.c	Mon Dec  3 22:26:28 2001
+++ linux/drivers/block/ll_rw_blk.c	Mon Dec  3 22:26:35 2001
@@ -630,15 +630,8 @@
 		    && atomic_read(&queued_sectors) < low_queued_sectors)
 			wake_up(&blk_buffers_wait);
 
-		if (!list_empty(&q->request_freelist[rw])) {
-			blk_refill_freelist(q, rw);
-			list_add(&req->table, &q->request_freelist[rw]);
-			return;
-		}
-
 		/*
-		 * free list is empty, add to pending free list and
-		 * batch wakeups
+		 * Add to pending free list and batch wakeups
 		 */
 		list_add(&req->table, &q->pending_freelist[rw]);
 
diff -urN linux-2.4.1-pre10/drivers/char/drm/radeon_state.c linux/drivers/char/drm/radeon_state.c
--- linux-2.4.1-pre10/drivers/char/drm/radeon_state.c	Mon Dec  3 22:26:28 2001
+++ linux/drivers/char/drm/radeon_state.c	Mon Dec  3 22:26:35 2001
@@ -778,7 +778,7 @@
 
 	radeon_update_ring_snapshot( dev_priv );
 
-	if ( 1 )
+	if ( 0 )
 		radeon_print_dirty( "dispatch_vertex", sarea_priv->dirty );
 
 	if ( buf->used ) {
diff -urN linux-2.4.1-pre10/drivers/ide/hpt366.c linux/drivers/ide/hpt366.c
--- linux-2.4.1-pre10/drivers/ide/hpt366.c	Tue Jan  2 16:58:45 2001
+++ linux/drivers/ide/hpt366.c	Mon Dec  3 22:26:35 2001
@@ -55,6 +55,15 @@
 };
 
 const char *bad_ata66_4[] = {
+	"IBM-DTLA-307075",
+	"IBM-DTLA-307060",
+	"IBM-DTLA-307045",
+	"IBM-DTLA-307030",
+	"IBM-DTLA-307020",
+	"IBM-DTLA-307015",
+	"IBM-DTLA-305040",
+	"IBM-DTLA-305030",
+	"IBM-DTLA-305020",
 	"WDC AC310200R",
 	NULL
 };
diff -urN linux-2.4.1-pre10/drivers/ide/via82cxxx.c linux/drivers/ide/via82cxxx.c
--- linux-2.4.1-pre10/drivers/ide/via82cxxx.c	Tue Nov  7 11:02:24 2000
+++ linux/drivers/ide/via82cxxx.c	Mon Dec  3 22:26:35 2001
@@ -602,7 +602,9 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->dmaproc = &via82cxxx_dmaproc;
+#ifdef CONFIG_IDEDMA_AUTO
 		hwif->autodma = 1;
+#endif
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 }
diff -urN linux-2.4.1-pre10/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- linux-2.4.1-pre10/drivers/net/eepro100.c	Mon Nov 27 17:58:32 2000
+++ linux/drivers/net/eepro100.c	Mon Dec  3 22:26:35 2001
@@ -29,7 +29,7 @@
 
 static const char *version =
 "eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n"
-"eepro100.c: $Revision: 1.35 $ 2000/11/17 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
+"eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
 
 /* A few user-configurable values that apply to all boards.
    First set is undocumented and spelled per Intel recommendations. */
@@ -698,6 +698,7 @@
 	   This takes less than 10usec and will easily finish before the next
 	   action. */
 	outl(PortReset, ioaddr + SCBPort);
+	inl(ioaddr + SCBPort);
 	udelay(10);
 
 	if (eeprom[3] & 0x0100)
@@ -785,6 +786,7 @@
 #endif  /* kernel_bloat */
 
 	outl(PortReset, ioaddr + SCBPort);
+	inl(ioaddr + SCBPort);
 	udelay(10);
 
 	/* Return the chip to its original power state. */
@@ -801,7 +803,7 @@
 	sp->tx_ring = tx_ring_space;
 	sp->tx_ring_dma = tx_ring_dma;
 	sp->lstats = (struct speedo_stats *)(sp->tx_ring + TX_RING_SIZE);
-	sp->lstats_dma = cpu_to_le32(TX_RING_ELEM_DMA(sp, TX_RING_SIZE));
+	sp->lstats_dma = TX_RING_ELEM_DMA(sp, TX_RING_SIZE);
 	init_timer(&sp->timer); /* used in ioctl() */
 
 	sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
@@ -1002,7 +1004,9 @@
 	/* Set the segment registers to '0'. */
 	wait_for_cmd_done(ioaddr + SCBCmd);
 	outl(0, ioaddr + SCBPointer);
-	inl(ioaddr + SCBPointer); /* XXX */
+	/* impose a delay to avoid a bug */
+	inl(ioaddr + SCBPointer);
+	udelay(10);
 	outb(RxAddrLoad, ioaddr + SCBCmd);
 	wait_for_cmd_done(ioaddr + SCBCmd);
 	outb(CUCmdBase, ioaddr + SCBCmd);
@@ -1010,7 +1014,6 @@
 	/* Load the statistics block and rx ring addresses. */
 	wait_for_cmd_done(ioaddr + SCBCmd);
 	outl(sp->lstats_dma, ioaddr + SCBPointer);
-	inl(ioaddr + SCBPointer); /* XXX */
 	outb(CUStatsAddr, ioaddr + SCBCmd);
 	sp->lstats->done_marker = 0;
 
@@ -1045,7 +1048,7 @@
 
 	/* Start the chip's Tx process and unmask interrupts. */
 	wait_for_cmd_done(ioaddr + SCBCmd);
-	outl(cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->dirty_tx % TX_RING_SIZE)),
+	outl(TX_RING_ELEM_DMA(sp, sp->dirty_tx % TX_RING_SIZE),
 		 ioaddr + SCBPointer);
 	/* We are not ACK-ing FCP and ER in the interrupt handler yet so they should
 	   remain masked --Dragan */
@@ -1274,7 +1277,7 @@
 		/* Only the command unit has stopped. */
 		printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
 			   dev->name);
-		outl(cpu_to_le32(TX_RING_ELEM_DMA(sp, dirty_tx % TX_RING_SIZE])),
+		outl(TX_RING_ELEM_DMA(sp, dirty_tx % TX_RING_SIZE]),
 			 ioaddr + SCBPointer);
 		outw(CUStart, ioaddr + SCBCmd);
 		reset_mii(dev);
diff -urN linux-2.4.1-pre10/drivers/sound/Config.in linux/drivers/sound/Config.in
--- linux-2.4.1-pre10/drivers/sound/Config.in	Wed Dec  6 11:43:05 2000
+++ linux/drivers/sound/Config.in	Mon Dec  3 22:26:35 2001
@@ -142,9 +142,9 @@
    dep_tristate '    Yamaha FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS
    dep_tristate '    Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS
    dep_tristate '    Yamaha OPL3-SA2, SA3, and SAx based PnP cards' CONFIG_SOUND_OPL3SA2 $CONFIG_SOUND_OSS
-   dep_tristate '    Yamaha YMF7xx PCI audio (legacy mode)' CONFIG_SOUND_YMPCI $CONFIG_SOUND_OSS $CONFIG_PCI
-   if [ "$CONFIG_SOUND_YMPCI" = "n" ]; then
-      dep_tristate '    Yamaha YMF7xx PCI audio (native mode) (EXPERIMENTAL)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS $CONFIG_PCI $CONFIG_EXPERIMENTAL
+   dep_tristate '    Yamaha YMF7xx PCI audio (native mode)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS $CONFIG_PCI
+   if [ "$CONFIG_SOUND_YMFPCI" != "n" ]; then
+      bool '      Yamaha PCI legacy ports support' CONFIG_SOUND_YMFPCI_LEGACY
    fi
    dep_tristate '    6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS
   
diff -urN linux-2.4.1-pre10/drivers/sound/Makefile linux/drivers/sound/Makefile
--- linux-2.4.1-pre10/drivers/sound/Makefile	Fri Dec 29 14:07:22 2000
+++ linux/drivers/sound/Makefile	Mon Dec  3 22:26:35 2001
@@ -47,8 +47,10 @@
 obj-$(CONFIG_SOUND_AWE32_SYNTH)	+= awe_wave.o
 
 obj-$(CONFIG_SOUND_VIA82CXXX)	+= via82cxxx_audio.o ac97_codec.o
-obj-$(CONFIG_SOUND_YMPCI)       += ymf_sb.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_YMFPCI)	+= ymfpci.o ac97_codec.o
+ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y)
+  obj-$(CONFIG_SOUND_YMFPCI)    += opl3.o uart401.o
+endif
 obj-$(CONFIG_SOUND_MSNDCLAS)	+= msnd.o msnd_classic.o
 obj-$(CONFIG_SOUND_MSNDPIN)	+= msnd.o msnd_pinnacle.o
 obj-$(CONFIG_SOUND_VWSND)	+= vwsnd.o
diff -urN linux-2.4.1-pre10/drivers/sound/via82cxxx_audio.c linux/drivers/sound/via82cxxx_audio.c
--- linux-2.4.1-pre10/drivers/sound/via82cxxx_audio.c	Fri Dec 29 20:25:47 2000
+++ linux/drivers/sound/via82cxxx_audio.c	Mon Dec  3 22:26:35 2001
@@ -15,7 +15,7 @@
  */
 
 
-#define VIA_VERSION	"1.1.14"
+#define VIA_VERSION	"1.1.14a"
 
 
 #include <linux/config.h>
@@ -76,8 +76,16 @@
 #define VIA_COUNTER_LIMIT	100000
 
 /* size of DMA buffers */
-#define VIA_DMA_BUFFERS		16
-#define VIA_DMA_BUF_SIZE	PAGE_SIZE
+#define VIA_MAX_BUFFER_DMA_PAGES	32
+
+/* buffering default values in ms */
+#define VIA_DEFAULT_FRAG_TIME		20
+#define VIA_DEFAULT_BUFFER_TIME		500
+
+#define VIA_MAX_FRAG_SIZE		PAGE_SIZE
+#define VIA_MIN_FRAG_SIZE		64
+
+#define VIA_MIN_FRAG_NUMBER		2	
 
 #ifndef AC97_PCM_LR_ADC_RATE
 #  define AC97_PCM_LR_ADC_RATE AC97_PCM_LR_DAC_RATE
@@ -102,7 +110,6 @@
 #define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00
 #define VIA_BASE0_PCM_OUT_CHAN_CTRL	0x01
 #define VIA_BASE0_PCM_OUT_CHAN_TYPE	0x02
-#define VIA_BASE0_PCM_OUT_BLOCK_COUNT	0x0C
 
 #define VIA_BASE0_PCM_IN_CHAN		0x10 /* input PCM from user */
 #define VIA_BASE0_PCM_IN_CHAN_STATUS	0x10
@@ -114,6 +121,7 @@
 #define VIA_PCM_CONTROL			0x01
 #define VIA_PCM_TYPE			0x02
 #define VIA_PCM_TABLE_ADDR		0x04
+#define VIA_PCM_BLOCK_COUNT		0x0C
 
 /* XXX unused DMA channel for FM PCM data */
 #define VIA_BASE0_FM_OUT_CHAN		0x20
@@ -223,14 +231,14 @@
 };
 
 
-struct via_sgd_data {
+struct via_buffer_pgtbl {
 	dma_addr_t handle;
 	void *cpuaddr;
 };
 
 
 struct via_channel {
-	atomic_t n_bufs;
+	atomic_t n_frags;
 	atomic_t hw_ptr;
 	wait_queue_head_t wait;
 
@@ -246,11 +254,14 @@
 	u8 pcm_fmt;		/* VIA_PCM_FMT_xxx */
 
 	unsigned rate;		/* sample rate */
+	unsigned int frag_size;
+	unsigned int frag_number;
 
 	volatile struct via_sgd_table *sgtable;
 	dma_addr_t sgt_handle;
 
-	struct via_sgd_data sgbuf [VIA_DMA_BUFFERS];
+	unsigned int page_number;
+	struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES];
 
 	long iobase;
 
@@ -301,17 +312,16 @@
 static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
 static int via_dsp_open (struct inode *inode, struct file *file);
 static int via_dsp_release(struct inode *inode, struct file *file);
-#ifdef VIA_SUPPORT_MMAP
 static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma);
-#endif
 
 static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg);
 static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value);
 static u8 via_ac97_wait_idle (struct via_info *card);
 
 static void via_chan_free (struct via_info *card, struct via_channel *chan);
-static void via_chan_clear (struct via_channel *chan);
+static void via_chan_clear (struct via_info *card, struct via_channel *chan);
 static void via_chan_pcm_fmt (struct via_channel *chan, int reset);
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan);
 
 #ifdef VIA_PROC_FS
 static int via_init_proc (void);
@@ -569,25 +579,53 @@
 	chan->pcm_fmt = VIA_PCM_FMT_MASK;
 	chan->is_enabled = 1;
 
-	if (chan->is_record)
-		atomic_set (&chan->n_bufs, 0);
-	else
-		atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
+	chan->frag_number = 0;
+        chan->frag_size = 0;
+	atomic_set(&chan->n_frags, 0);
 	atomic_set (&chan->hw_ptr, 0);
 }
 
+/**
+ *      via_chan_init - Initialize PCM channel
+ *      @card: Private audio chip info
+ *      @chan: Channel to be initialized
+ *
+ *      Performs some of the preparations necessary to begin
+ *      using a PCM channel.
+ *
+ *      Currently the preparations consist in 
+ *      setting the
+ *      PCM channel to a known state.
+ */
+
+
+static void via_chan_init (struct via_info *card, struct via_channel *chan)
+{
+
+        DPRINTK ("ENTER\n");
+
+	/* bzero channel structure, and init members to defaults */
+        via_chan_init_defaults (card, chan);
+
+        /* stop any existing channel output */
+        via_chan_clear (card, chan);
+        via_chan_status_clear (chan->iobase);
+        via_chan_pcm_fmt (chan, 1);
+
+	DPRINTK ("EXIT\n");
+}
 
 /**
- *	via_chan_init - Initialize PCM channel
+ *	via_chan_buffer_init - Initialize PCM channel buffer
  *	@card: Private audio chip info
  *	@chan: Channel to be initialized
  *
- *	Performs all the preparations necessary to begin
+ *	Performs some of the preparations necessary to begin
  *	using a PCM channel.
  *
  *	Currently the preparations include allocating the
- *	scatter-gather DMA table and buffers, setting the
- *	PCM channel to a known state, and passing the
+ *	scatter-gather DMA table and buffers,
+ *	and passing the
  *	address of the DMA table to the hardware.
  *
  *	Note that special care is taken when passing the
@@ -596,18 +634,21 @@
  *	always "take" the address.
  */
 
-static int via_chan_init (struct via_info *card, struct via_channel *chan)
+static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan)
 {
+	int page, offset;
 	int i;
 
 	DPRINTK ("ENTER\n");
 
-	/* bzero channel structure, and init members to defaults */
-	via_chan_init_defaults (card, chan);
+	if (chan->sgtable != NULL) {
+		DPRINTK ("EXIT\n");
+		return 0;
+	}
 
 	/* alloc DMA-able memory for scatter-gather table */
 	chan->sgtable = pci_alloc_consistent (card->pdev,
-		(sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+		(sizeof (struct via_sgd_table) * chan->frag_number),
 		&chan->sgt_handle);
 	if (!chan->sgtable) {
 		printk (KERN_ERR PFX "DMA table alloc fail, aborting\n");
@@ -616,45 +657,54 @@
 	}
 
 	memset ((void*)chan->sgtable, 0,
-		(sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS));
+		(sizeof (struct via_sgd_table) * chan->frag_number));
 
 	/* alloc DMA-able memory for scatter-gather buffers */
-	for (i = 0; i < VIA_DMA_BUFFERS; i++) {
-		chan->sgbuf[i].cpuaddr =
-			pci_alloc_consistent (card->pdev, VIA_DMA_BUF_SIZE,
-					      &chan->sgbuf[i].handle);
 
-		if (!chan->sgbuf[i].cpuaddr)
-			goto err_out_nomem;
+	chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE + 
+			    (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0);
+
+	for (i = 0; i < chan->page_number; i++) {
+		chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE,
+					      &chan->pgtbl[i].handle);
 
-		if (i < (VIA_DMA_BUFFERS - 1))
-			chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
-		else
-			chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
-		chan->sgtable[i].addr = cpu_to_le32 (chan->sgbuf[i].handle);
+		if (!chan->pgtbl[i].cpuaddr) {
+			chan->page_number = i;
+			goto err_out_nomem;
+		}
 
 #ifndef VIA_NDEBUG
-		memset (chan->sgbuf[i].cpuaddr, 0xBC, VIA_DMA_BUF_SIZE);
+                memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size);
 #endif
 
 #if 1
-		DPRINTK ("dmabuf #%d (h=%lx, 32(h)=%lx, v2p=%lx, a=%p)\n",
-			 i, (long)chan->sgbuf[i].handle,
-			 (long)chan->sgtable[i].addr,
-			 virt_to_phys(chan->sgbuf[i].cpuaddr),
-			 chan->sgbuf[i].cpuaddr);
+                DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n",
+			i, (long)chan->pgtbl[i].handle,
+			virt_to_phys(chan->pgtbl[i].cpuaddr),
+			chan->pgtbl[i].cpuaddr);
 #endif
-
-		assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
 	}
 
-	/* stop any existing channel output */
-	via_chan_clear (chan);
-	via_chan_status_clear (chan->iobase);
-	via_chan_pcm_fmt (chan, 1);
+	for (i = 0; i < chan->frag_number; i++) {
+
+		page = i / (PAGE_SIZE / chan->frag_size);
+		offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size;
+
+		chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
+		chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset);
+
+#if 1
+		DPRINTK ("dmabuf #%d (32(h)=%lx)\n",
+			 i,
+			 (long)chan->sgtable[i].addr);
+#endif
+	}	
+
+	/* overwrite the last buffer information */
+	chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
 
 	/* set location of DMA-able scatter-gather info table */
-	DPRINTK("outl (0x%X, 0x%04lX)\n",
+	DPRINTK ("outl (0x%X, 0x%04lX)\n",
 		cpu_to_le32 (chan->sgt_handle),
 		chan->iobase + VIA_PCM_TABLE_ADDR);
 
@@ -664,7 +714,7 @@
 	udelay (20);
 	via_ac97_wait_idle (card);
 
-	DPRINTK("inl (0x%lX) = %x\n",
+	DPRINTK ("inl (0x%lX) = %x\n",
 		chan->iobase + VIA_PCM_TABLE_ADDR,
 		inl(chan->iobase + VIA_PCM_TABLE_ADDR));
 
@@ -673,7 +723,7 @@
 
 err_out_nomem:
 	printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n");
-	via_chan_free (card, chan);
+	via_chan_buffer_free (card, chan);
 	DPRINTK ("EXIT\n");
 	return -ENOMEM;
 }
@@ -695,8 +745,6 @@
 
 static void via_chan_free (struct via_info *card, struct via_channel *chan)
 {
-	int i;
-
 	DPRINTK ("ENTER\n");
 
 	synchronize_irq();
@@ -710,23 +758,33 @@
 
 	spin_unlock_irq (&card->lock);
 
+	DPRINTK ("EXIT\n");
+}
+
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan)
+{
+	int i;
+
+        DPRINTK ("ENTER\n");
+
 	/* zero location of DMA-able scatter-gather info table */
 	via_ac97_wait_idle(card);
 	outl (0, chan->iobase + VIA_PCM_TABLE_ADDR);
 
-	for (i = 0; i < VIA_DMA_BUFFERS; i++)
-		if (chan->sgbuf[i].cpuaddr) {
-			assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0);
-			pci_free_consistent (card->pdev, VIA_DMA_BUF_SIZE,
-					     chan->sgbuf[i].cpuaddr,
-					     chan->sgbuf[i].handle);
-			chan->sgbuf[i].cpuaddr = NULL;
-			chan->sgbuf[i].handle = 0;
+	for (i = 0; i < chan->page_number; i++)
+		if (chan->pgtbl[i].cpuaddr) {
+			pci_free_consistent (card->pdev, PAGE_SIZE,
+					     chan->pgtbl[i].cpuaddr,
+					     chan->pgtbl[i].handle);
+			chan->pgtbl[i].cpuaddr = NULL;
+			chan->pgtbl[i].handle = 0;
 		}
 
+	chan->page_number = 0;
+
 	if (chan->sgtable) {
 		pci_free_consistent (card->pdev,
-			(sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+			(sizeof (struct via_sgd_table) * chan->frag_number),
 			(void*)chan->sgtable, chan->sgt_handle);
 		chan->sgtable = NULL;
 	}
@@ -771,11 +829,11 @@
 	if (!chan->is_record)
 		chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;
 
-	outb (chan->pcm_fmt, chan->iobase + 2);
+	outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE);
 
 	DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n",
 		 chan->pcm_fmt,
-		 inb (chan->iobase + 2));
+		 inb (chan->iobase + VIA_PCM_TYPE));
 }
 
 
@@ -787,10 +845,11 @@
  *	all software pointers which track DMA operation.
  */
 
-static void via_chan_clear (struct via_channel *chan)
+static void via_chan_clear (struct via_info *card, struct via_channel *chan)
 {
 	DPRINTK ("ENTER\n");
 	via_chan_stop (chan->iobase);
+	via_chan_buffer_free(card, chan);
 	chan->is_active = 0;
 	chan->is_mapped = 0;
 	chan->is_enabled = 1;
@@ -798,10 +857,6 @@
 	chan->sw_ptr = 0;
 	chan->n_irqs = 0;
 	atomic_set (&chan->hw_ptr, 0);
-	if (chan->is_record)
-		atomic_set (&chan->n_bufs, 0);
-	else
-		atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS);
 	DPRINTK ("EXIT\n");
 }
 
@@ -826,7 +881,7 @@
 {
 	DPRINTK ("ENTER, requested rate = %d\n", val);
 
-	via_chan_clear (chan);
+	via_chan_clear (card, chan);
 
 	val = via_set_rate (&card->ac97, chan, val);
 
@@ -858,7 +913,7 @@
 	 	 val == AFMT_S16_LE ? "AFMT_S16_LE" :
 		 "unknown");
 
-	via_chan_clear (chan);
+	via_chan_clear (card, chan);
 
 	assert (val != AFMT_QUERY); /* this case is handled elsewhere */
 
@@ -907,7 +962,7 @@
 {
 	DPRINTK ("ENTER, channels = %d\n", val);
 
-	via_chan_clear (chan);
+	via_chan_clear (card, chan);
 
 	switch (val) {
 
@@ -934,6 +989,78 @@
 	return val;
 }
 
+static int via_chan_set_buffering (struct via_info *card,
+                                struct via_channel *chan, int val)
+{
+	int shift;
+
+        DPRINTK ("ENTER\n");
+
+	/* in both cases the buffer cannot be changed */
+	if (chan->is_active || chan->is_mapped) { 
+		DPRINTK ("EXIT\n");
+		return -EINVAL;
+	}
+
+	/* called outside SETFRAGMENT */
+	/* set defaults or do nothing */
+	if (val < 0) {
+
+		if (chan->frag_size && chan->frag_number)
+			goto out;
+
+		DPRINTK ("\n");
+
+		chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate *
+				   ((chan->pcm_fmt & VIA_PCM_FMT_STEREO) ? 2 : 1) *
+				   ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1;
+
+		shift = 0;
+		while (chan->frag_size) {
+			chan->frag_size >>= 1;
+			shift++;
+		}
+		chan->frag_size = 1 << shift;
+
+		chan->frag_number = (VIA_DEFAULT_BUFFER_TIME / VIA_DEFAULT_FRAG_TIME);
+
+		DPRINTK ("setting default values %d %d\n", chan->frag_size, chan->frag_number);
+	} else {
+		chan->frag_size = 1 << (val & 0xFFFF);
+		chan->frag_number = (val >> 16) & 0xFFFF;
+
+		DPRINTK ("using user values %d %d\n", chan->frag_size, chan->frag_number);
+	}
+
+	/* quake3 wants frag_number to be a power of two */
+	shift = 0;
+	while (chan->frag_number) {
+		chan->frag_number >>= 1;
+		shift++;
+	}
+	chan->frag_number = 1 << shift;
+
+	if (chan->frag_size > VIA_MAX_FRAG_SIZE)
+		chan->frag_size = VIA_MAX_FRAG_SIZE;
+	else if (chan->frag_size < VIA_MIN_FRAG_SIZE)
+		chan->frag_size = VIA_MIN_FRAG_SIZE;
+
+	if (chan->frag_number < VIA_MIN_FRAG_NUMBER)
+                chan->frag_number = VIA_MIN_FRAG_NUMBER;
+
+	if ((chan->frag_number * chan->frag_size) / PAGE_SIZE > VIA_MAX_BUFFER_DMA_PAGES)
+		chan->frag_number = (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE) / chan->frag_size;
+
+out:
+	if (chan->is_record)
+		atomic_set (&chan->n_frags, 0);
+	else
+		atomic_set (&chan->n_frags, chan->frag_number);
+
+	DPRINTK ("EXIT\n");
+
+	return 0;
+}
 
 #ifdef VIA_CHAN_DUMP_BUFS
 /**
@@ -948,7 +1075,7 @@
 {
 	int i;
 
-	for (i = 0; i < VIA_DMA_BUFFERS; i++) {
+	for (i = 0; i < chan->frag_number; i++) {
 		DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n",
 			 i, chan->sgtable[i].addr,
 			 chan->sgtable[i].count & 0x00FFFFFF,
@@ -975,15 +1102,15 @@
 
 	assert (chan->slop_len > 0);
 
-	if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+	if (chan->sw_ptr == (chan->frag_number - 1))
 		chan->sw_ptr = 0;
 	else
 		chan->sw_ptr++;
 
 	chan->slop_len = 0;
 
-	assert (atomic_read (&chan->n_bufs) > 0);
-	atomic_dec (&chan->n_bufs);
+	assert (atomic_read (&chan->n_frags) > 0);
+	atomic_dec (&chan->n_frags);
 
 	DPRINTK ("EXIT\n");
 }
@@ -1003,7 +1130,7 @@
 	if (!chan->is_active && chan->is_enabled) {
 		chan->is_active = 1;
 		sg_begin (chan);
-		DPRINTK("starting channel %s\n", chan->name);
+		DPRINTK ("starting channel %s\n", chan->name);
 	}
 }
 
@@ -1213,7 +1340,7 @@
 {
 	DPRINTK ("ENTER\n");
 
-	DPRINTK("EXIT, returning -ESPIPE\n");
+	DPRINTK ("EXIT, returning -ESPIPE\n");
 	return -ESPIPE;
 }
 
@@ -1245,7 +1372,7 @@
 		pci_read_config_byte (card->pdev, 0x43, &r43);
 		pci_read_config_byte (card->pdev, 0x44, &r44);
 		pci_read_config_byte (card->pdev, 0x48, &r48);
-		DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+		DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
 			r40,r41,r42,r43,r44,r48);
 
 		spin_lock_irq (&card->lock);
@@ -1334,7 +1461,7 @@
 	card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
 	if (card->ac97.dev_mixer < 0) {
 		printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
-		DPRINTK("EXIT, returning -EIO\n");
+		DPRINTK ("EXIT, returning -EIO\n");
 		return -EIO;
 	}
 
@@ -1359,21 +1486,21 @@
 
 err_out:
 	unregister_sound_mixer (card->ac97.dev_mixer);
-	DPRINTK("EXIT, returning %d\n", rc);
+	DPRINTK ("EXIT, returning %d\n", rc);
 	return rc;
 }
 
 
 static void via_ac97_cleanup (struct via_info *card)
 {
-	DPRINTK("ENTER\n");
+	DPRINTK ("ENTER\n");
 
 	assert (card != NULL);
 	assert (card->ac97.dev_mixer >= 0);
 
 	unregister_sound_mixer (card->ac97.dev_mixer);
 
-	DPRINTK("EXIT\n");
+	DPRINTK ("EXIT\n");
 }
 
 
@@ -1414,24 +1541,24 @@
 
 	/* sanity check: make sure our h/w ptr doesn't have a weird value */
 	assert (n >= 0);
-	assert (n < VIA_DMA_BUFFERS);
+	assert (n < chan->frag_number);
 
 	/* reset SGD data structure in memory to reflect a full buffer,
 	 * and advance the h/w ptr, wrapping around to zero if needed
 	 */
-	if (n == (VIA_DMA_BUFFERS - 1)) {
-		chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_EOL);
+	if (n == (chan->frag_number - 1)) {
+		chan->sgtable[n].count = (chan->frag_size | VIA_EOL);
 		atomic_set (&chan->hw_ptr, 0);
 	} else {
-		chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_FLAG);
+		chan->sgtable[n].count = (chan->frag_size | VIA_FLAG);
 		atomic_inc (&chan->hw_ptr);
 	}
 
 	/* accounting crap for SNDCTL_DSP_GETxPTR */
 	chan->n_irqs++;
-	chan->bytes += VIA_DMA_BUF_SIZE;
+	chan->bytes += chan->frag_size;
 	if (chan->bytes < 0) /* handle overflow of 31-bit value */
-		chan->bytes = VIA_DMA_BUF_SIZE;
+		chan->bytes = chan->frag_size;
 
 	/* wake up anyone listening to see when interrupts occur */
 	if (waitqueue_active (&chan->wait))
@@ -1445,25 +1572,25 @@
 	if (chan->is_mapped)
 		return;
 
-	/* If we are recording, then n_bufs represents the number
-	 * of buffers waiting to be handled by userspace.
-	 * If we are playback, then n_bufs represents the number
-	 * of buffers remaining to be filled by userspace.
-	 * We increment here.  If we reach max buffers (VIA_DMA_BUFFERS),
+	/* If we are recording, then n_frags represents the number
+	 * of fragments waiting to be handled by userspace.
+	 * If we are playback, then n_frags represents the number
+	 * of fragments remaining to be filled by userspace.
+	 * We increment here.  If we reach max number of fragments,
 	 * this indicates an underrun/overrun.  For this case under OSS,
 	 * we stop the record/playback process.
 	 */
-	if (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS)
-		atomic_inc (&chan->n_bufs);
-	assert (atomic_read (&chan->n_bufs) <= VIA_DMA_BUFFERS);
+	if (atomic_read (&chan->n_frags) < chan->frag_number)
+		atomic_inc (&chan->n_frags);
+	assert (atomic_read (&chan->n_frags) <= chan->frag_number);
 
-	if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS) {
+	if (atomic_read (&chan->n_frags) == chan->frag_number) {
 		chan->is_active = 0;
 		via_chan_stop (chan->iobase);
 	}
 
-	DPRINTK ("%s intr, channel n_bufs == %d\n", chan->name,
-		 atomic_read (&chan->n_bufs));
+	DPRINTK ("%s intr, channel n_frags == %d\n", chan->name,
+		 atomic_read (&chan->n_frags));
 }
 
 
@@ -1618,9 +1745,7 @@
 	poll:		via_dsp_poll,
 	llseek: 	via_llseek,
 	ioctl:		via_dsp_ioctl,
-#ifdef VIA_SUPPORT_MMAP
 	mmap:		via_dsp_mmap,
-#endif
 };
 
 
@@ -1668,7 +1793,6 @@
 }
 
 
-#ifdef VIA_SUPPORT_MMAP
 static struct page * via_mm_nopage (struct vm_area_struct * vma,
 				    unsigned long address, int write_access)
 {
@@ -1685,8 +1809,6 @@
 		 address,
 		 write_access);
 
-	assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
-
         if (address > vma->vm_end) {
 		DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n");
 		return NOPAGE_SIGBUS; /* Disallow mremap */
@@ -1702,7 +1824,7 @@
 
 #ifndef VIA_NDEBUG
 	{
-	unsigned long max_bufs = VIA_DMA_BUFFERS;
+	unsigned long max_bufs = chan->frag_number;
 	if (rd && wr) max_bufs *= 2;
 	/* via_dsp_mmap() should ensure this */
 	assert (pgoff < max_bufs);
@@ -1711,17 +1833,17 @@
 
 	/* if full-duplex (read+write) and we have two sets of bufs,
 	 * then the playback buffers come first, sez soundcard.c */
-	if (pgoff >= VIA_DMA_BUFFERS) {
-		pgoff -= VIA_DMA_BUFFERS;
+	if (pgoff >= chan->page_number) {
+		pgoff -= chan->page_number;
 		chan = &card->ch_in;
 	} else if (!wr)
 		chan = &card->ch_in;
 
-	assert ((((unsigned long)chan->sgbuf[pgoff].cpuaddr) % PAGE_SIZE) == 0);
+	assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0);
 
-	dmapage = virt_to_page (chan->sgbuf[pgoff].cpuaddr);
+	dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr);
 	DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n",
-		 dmapage, (unsigned long) chan->sgbuf[pgoff].cpuaddr);
+		 dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr);
 	get_page (dmapage);
 	return dmapage;
 }
@@ -1761,16 +1883,18 @@
 		 vma->vm_end - vma->vm_start,
 		 vma->vm_pgoff);
 
-	assert (VIA_DMA_BUF_SIZE == PAGE_SIZE);
-
 	max_size = 0;
-	if (file->f_mode & FMODE_READ) {
+	if (vma->vm_flags & VM_READ) {
 		rd = 1;
-		max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+		via_chan_set_buffering(card, &card->ch_in, -1);
+		via_chan_buffer_init (card, &card->ch_in);
+		max_size += card->ch_in.page_number << PAGE_SHIFT;
 	}
-	if (file->f_mode & FMODE_WRITE) {
+	if (vma->vm_flags & VM_WRITE) {
 		wr = 1;
-		max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE);
+		via_chan_set_buffering(card, &card->ch_out, -1);
+		via_chan_buffer_init (card, &card->ch_out);
+		max_size += card->ch_out.page_number << PAGE_SHIFT;
 	}
 
 	start = vma->vm_start;
@@ -1802,10 +1926,9 @@
 	rc = 0;
 
 out:
-	DPRINTK("EXIT, returning %d\n", rc);
+	DPRINTK ("EXIT, returning %d\n", rc);
 	return rc;
 }
-#endif /* VIA_SUPPORT_MMAP */
 
 
 static ssize_t via_dsp_do_read (struct via_info *card,
@@ -1831,13 +1954,13 @@
 	 */
 	n = chan->sw_ptr;
 
-	/* n_bufs represents the number of buffers waiting
+	/* n_frags represents the number of fragments waiting
 	 * to be copied to userland.  sleep until at least
 	 * one buffer has been read from the audio hardware.
 	 */
-	tmp = atomic_read (&chan->n_bufs);
+	tmp = atomic_read (&chan->n_frags);
 	assert (tmp >= 0);
-	assert (tmp <= VIA_DMA_BUFFERS);
+	assert (tmp <= chan->frag_number);
 	while (tmp == 0) {
 		if (nonblock || !chan->is_active)
 			return -EAGAIN;
@@ -1848,18 +1971,18 @@
 		if (signal_pending (current))
 			return -ERESTARTSYS;
 
-		tmp = atomic_read (&chan->n_bufs);
+		tmp = atomic_read (&chan->n_frags);
 	}
 
 	/* Now that we have a buffer we can read from, send
 	 * as much as sample data possible to userspace.
 	 */
-	while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
-		size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
+	while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+		size_t slop_left = chan->frag_size - chan->slop_len;
 
 		size = (count < slop_left) ? count : slop_left;
 		if (copy_to_user (userbuf,
-				  chan->sgbuf[n].cpuaddr + chan->slop_len,
+				  chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + n % (PAGE_SIZE / chan->frag_size) + chan->slop_len,
 				  size))
 			return -EFAULT;
 
@@ -1871,7 +1994,7 @@
 	/* If we didn't copy the buffer completely to userspace,
 	 * stop now.
 	 */
-	if (chan->slop_len < VIA_DMA_BUF_SIZE)
+	if (chan->slop_len < chan->frag_size)
 		goto out;
 
 	/*
@@ -1882,20 +2005,20 @@
 	/* advance channel software pointer to point to
 	 * the next buffer from which we will copy
 	 */
-	if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+	if (chan->sw_ptr == (chan->frag_number - 1))
 		chan->sw_ptr = 0;
 	else
 		chan->sw_ptr++;
 
 	/* mark one less buffer waiting to be processed */
-	assert (atomic_read (&chan->n_bufs) > 0);
-	atomic_dec (&chan->n_bufs);
+	assert (atomic_read (&chan->n_frags) > 0);
+	atomic_dec (&chan->n_frags);
 
 	/* we are at a block boundary, there is no fragment data */
 	chan->slop_len = 0;
 
-	DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
-		n, chan->sw_ptr, atomic_read (&chan->n_bufs));
+	DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+		n, chan->sw_ptr, atomic_read (&chan->n_frags));
 
 	DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
 		 inb (card->baseaddr + 0x00),
@@ -1941,12 +2064,18 @@
 		goto out_up;
 	}
 
+	via_chan_set_buffering(card, &card->ch_in, -1);
+        rc = via_chan_buffer_init (card, &card->ch_in);
+
+	if (rc)
+		goto out_up;
+
 	rc = via_dsp_do_read (card, buffer, count, nonblock);
 
 out_up:
 	up (&card->syscall_sem);
 out:
-	DPRINTK("EXIT, returning %ld\n",(long) rc);
+	DPRINTK ("EXIT, returning %ld\n",(long) rc);
 	return rc;
 }
 
@@ -1966,40 +2095,40 @@
 	if (current->need_resched)
 		schedule ();
 
-	/* grab current channel software pointer.  In the case of
-	 * playback, this is pointing to the next buffer that
+	/* grab current channel fragment pointer.  In the case of
+	 * playback, this is pointing to the next fragment that
 	 * should receive data from userland.
 	 */
 	n = chan->sw_ptr;
 
-	/* n_bufs represents the number of buffers remaining
+	/* n_frags represents the number of fragments remaining
 	 * to be filled by userspace.  Sleep until
-	 * at least one buffer is available for our use.
+	 * at least one fragment is available for our use.
 	 */
-	tmp = atomic_read (&chan->n_bufs);
+	tmp = atomic_read (&chan->n_frags);
 	assert (tmp >= 0);
-	assert (tmp <= VIA_DMA_BUFFERS);
+	assert (tmp <= chan->frag_number);
 	while (tmp == 0) {
 		if (nonblock || !chan->is_enabled)
 			return -EAGAIN;
 
-		DPRINTK ("Sleeping on block %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
+		DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
 		interruptible_sleep_on (&chan->wait);
 
 		if (signal_pending (current))
 			return -ERESTARTSYS;
 
-		tmp = atomic_read (&chan->n_bufs);
+		tmp = atomic_read (&chan->n_frags);
 	}
 
-	/* Now that we have a buffer we can write to, fill it up
+	/* Now that we have at least one fragment we can write to, fill the buffer
 	 * as much as possible with data from userspace.
 	 */
-	while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) {
-		size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len;
+	while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+		size_t slop_left = chan->frag_size - chan->slop_len;
 
 		size = (count < slop_left) ? count : slop_left;
-		if (copy_from_user (chan->sgbuf[n].cpuaddr + chan->slop_len,
+		if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size + chan->slop_len,
 				    userbuf, size))
 			return -EFAULT;
 
@@ -2009,36 +2138,36 @@
 	}
 
 	/* If we didn't fill up the buffer with data, stop now.
-	 * Put a 'stop' marker in the DMA table too, to tell the
-	 * audio hardware to stop if it gets here.
-	 */
-	if (chan->slop_len < VIA_DMA_BUF_SIZE) {
+         * Put a 'stop' marker in the DMA table too, to tell the
+         * audio hardware to stop if it gets here.
+         */
+	if (chan->slop_len < chan->frag_size) {
 		sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP);
 		goto out;
 	}
 
 	/*
-	 * If we get to this point, we have filled a buffer with
-	 * audio data, flush the buffer to audio hardware.
-	 */
+         * If we get to this point, we have filled a buffer with
+         * audio data, flush the buffer to audio hardware.
+         */
 
 	/* Record the true size for the audio hardware to notice */
-	if (n == (VIA_DMA_BUFFERS - 1))
-		sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
-	else
-		sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
+        if (n == (chan->frag_number - 1))
+                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
+        else
+                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
 
 	/* advance channel software pointer to point to
 	 * the next buffer we will fill with data
 	 */
-	if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1))
+	if (chan->sw_ptr == (chan->frag_number - 1))
 		chan->sw_ptr = 0;
 	else
 		chan->sw_ptr++;
 
 	/* mark one less buffer as being available for userspace consumption */
-	assert (atomic_read (&chan->n_bufs) > 0);
-	atomic_dec (&chan->n_bufs);
+	assert (atomic_read (&chan->n_frags) > 0);
+	atomic_dec (&chan->n_frags);
 
 	/* we are at a block boundary, there is no fragment data */
 	chan->slop_len = 0;
@@ -2046,8 +2175,8 @@
 	/* if SGD has not yet been started, start it */
 	via_chan_maybe_start (chan);
 
-	DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n",
-		n, chan->sw_ptr, atomic_read (&chan->n_bufs));
+	DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+		n, chan->sw_ptr, atomic_read (&chan->n_frags));
 
 	DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
 		 inb (card->baseaddr + 0x00),
@@ -2093,12 +2222,18 @@
 		goto out_up;
 	}
 
+	via_chan_set_buffering(card, &card->ch_out, -1);
+	rc = via_chan_buffer_init (card, &card->ch_out);
+
+	if (rc)
+		goto out_up;
+
 	rc = via_dsp_do_write (card, buffer, count, nonblock);
 
 out_up:
 	up (&card->syscall_sem);
 out:
-	DPRINTK("EXIT, returning %ld\n",(long) rc);
+	DPRINTK ("EXIT, returning %ld\n",(long) rc);
 	return rc;
 }
 
@@ -2117,23 +2252,27 @@
 	rd = (file->f_mode & FMODE_READ);
 	wr = (file->f_mode & FMODE_WRITE);
 
-	if (wr && (atomic_read (&card->ch_out.n_bufs) == 0)) {
+	if (wr && (atomic_read (&card->ch_out.n_frags) == 0)) {
 		assert (card->ch_out.is_active);
                 poll_wait(file, &card->ch_out.wait, wait);
 	}
         if (rd) {
 		/* XXX is it ok, spec-wise, to start DMA here? */
+		if (!card->ch_in.is_active) {
+			via_chan_set_buffering(card, &card->ch_in, -1);
+			via_chan_buffer_init(card, &card->ch_in);
+		}
 		via_chan_maybe_start (&card->ch_in);
-		if (atomic_read (&card->ch_in.n_bufs) == 0)
+		if (atomic_read (&card->ch_in.n_frags) == 0)
 	                poll_wait(file, &card->ch_in.wait, wait);
 	}
 
-	if (wr && (atomic_read (&card->ch_out.n_bufs) > 0))
+	if (wr && ((atomic_read (&card->ch_out.n_frags) > 0) || !card->ch_out.is_active))
 		mask |= POLLOUT | POLLWRNORM;
-	if (rd && (atomic_read (&card->ch_in.n_bufs) > 0))
+	if (rd && (atomic_read (&card->ch_in.n_frags) > 0))
 		mask |= POLLIN | POLLRDNORM;
 
-	DPRINTK("EXIT, returning %u\n", mask);
+	DPRINTK ("EXIT, returning %u\n", mask);
 	return mask;
 }
 
@@ -2158,12 +2297,12 @@
 	if (chan->slop_len > 0)
 		via_chan_flush_frag (chan);
 
-	if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS)
+	if (atomic_read (&chan->n_frags) == chan->frag_number)
 		goto out;
 
 	via_chan_maybe_start (chan);
 
-	while (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS) {
+	while (atomic_read (&chan->n_frags) < chan->frag_number) {
 		if (nonblock) {
 			DPRINTK ("EXIT, returning -EAGAIN\n");
 			return -EAGAIN;
@@ -2178,7 +2317,7 @@
 		pci_read_config_byte (card->pdev, 0x43, &r43);
 		pci_read_config_byte (card->pdev, 0x44, &r44);
 		pci_read_config_byte (card->pdev, 0x48, &r48);
-		DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+		DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
 			r40,r41,r42,r43,r44,r48);
 
 		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
@@ -2195,7 +2334,7 @@
 			printk (KERN_ERR "sleeping but not active\n");
 #endif
 
-		DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_bufs));
+		DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags));
 		interruptible_sleep_on (&chan->wait);
 
 		if (signal_pending (current)) {
@@ -2213,7 +2352,7 @@
 		pci_read_config_byte (card->pdev, 0x43, &r43);
 		pci_read_config_byte (card->pdev, 0x44, &r44);
 		pci_read_config_byte (card->pdev, 0x48, &r48);
-		DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+		DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
 			r40,r41,r42,r43,r44,r48);
 
 		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
@@ -2225,7 +2364,7 @@
 			 inl (card->baseaddr + 0x80),
 			 inl (card->baseaddr + 0x84));
 
-		DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_bufs));
+		DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags));
 	}
 #endif
 
@@ -2252,21 +2391,23 @@
 {
 	audio_buf_info info;
 
-	info.fragstotal = VIA_DMA_BUFFERS;
-	info.fragsize = VIA_DMA_BUF_SIZE;
+	via_chan_set_buffering(card, chan, -1);
+
+	info.fragstotal = chan->frag_number;
+	info.fragsize = chan->frag_size;
 
 	/* number of full fragments we can read/write without blocking */
-	info.fragments = atomic_read (&chan->n_bufs);
+	info.fragments = atomic_read (&chan->n_frags);
 
-	if ((chan->slop_len > 0) && (info.fragments > 0))
+	if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0))
 		info.fragments--;
 
 	/* number of bytes that can be read or written immediately
 	 * without blocking.
 	 */
-	info.bytes = (info.fragments * VIA_DMA_BUF_SIZE);
-	if (chan->slop_len > 0)
-		info.bytes += VIA_DMA_BUF_SIZE - chan->slop_len;
+	info.bytes = (info.fragments * chan->frag_size);
+	if (chan->slop_len % chan->frag_size > 0)
+		info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size);
 
 	DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n",
 		info.fragstotal,
@@ -2305,8 +2446,8 @@
 
 	if (chan->is_active) {
 		unsigned long extra;
-		info.ptr = atomic_read (&chan->hw_ptr) * VIA_DMA_BUF_SIZE;
-		extra = VIA_DMA_BUF_SIZE - inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
+		info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size;
+		extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
 		info.ptr += extra;
 		info.bytes += extra;
 	} else {
@@ -2386,13 +2527,13 @@
 
 	/* OSS API version.  XXX unverified */
 	case OSS_GETVERSION:
-		DPRINTK("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
+		DPRINTK ("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
 		rc = put_user (SOUND_VERSION, (int *)arg);
 		break;
 
 	/* list of supported PCM data formats */
 	case SNDCTL_DSP_GETFMTS:
-		DPRINTK("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
+		DPRINTK ("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
                 rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg);
 		break;
 
@@ -2402,20 +2543,19 @@
 			rc = -EFAULT;
 			break;
 		}
-		DPRINTK("DSP_SETFMT, val==%d\n", val);
+		DPRINTK ("DSP_SETFMT, val==%d\n", val);
 		if (val != AFMT_QUERY) {
 			rc = 0;
 
-			if (rc == 0 && rd)
+			if (rd)
 				rc = via_chan_set_fmt (card, &card->ch_in, val);
-			if (rc == 0 && wr)
+
+			if (rc >= 0 && wr)
 				rc = via_chan_set_fmt (card, &card->ch_out, val);
 
-			if (rc <= 0) {
-				if (rc == 0)
-					rc = -EINVAL;
+			if (rc < 0)
 				break;
-			}
+
 			val = rc;
 		} else {
 			if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) ||
@@ -2424,7 +2564,7 @@
 			else
 				val = AFMT_U8;
 		}
-		DPRINTK("SETFMT EXIT, returning %d\n", val);
+		DPRINTK ("SETFMT EXIT, returning %d\n", val);
                 rc = put_user (val, (int *)arg);
 		break;
 
@@ -2434,18 +2574,19 @@
 			rc = -EFAULT;
 			break;
 		}
-		DPRINTK("DSP_CHANNELS, val==%d\n", val);
+		DPRINTK ("DSP_CHANNELS, val==%d\n", val);
 		if (val != 0) {
 			rc = 0;
-			if (rc == 0 && rd)
+
+			if (rd)
 				rc = via_chan_set_stereo (card, &card->ch_in, val);
-			if (rc == 0 && wr)
+
+			if (rc >= 0 && wr)
 				rc = via_chan_set_stereo (card, &card->ch_out, val);
-			if (rc <= 0) {
-				if (rc == 0)
-					rc = -EINVAL;
+
+			if (rc < 0)
 				break;
-			}
+
 			val = rc;
 		} else {
 			if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) ||
@@ -2454,7 +2595,7 @@
 			else
 				val = 1;
 		}
-		DPRINTK("CHANNELS EXIT, returning %d\n", val);
+		DPRINTK ("CHANNELS EXIT, returning %d\n", val);
                 rc = put_user (val, (int *)arg);
 		break;
 
@@ -2464,21 +2605,21 @@
 			rc = -EFAULT;
 			break;
 		}
-		DPRINTK("DSP_STEREO, val==%d\n", val);
+		DPRINTK ("DSP_STEREO, val==%d\n", val);
 		rc = 0;
 
-		if (rc == 0 && rd)
+		if (rd)
 			rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1);
-		if (rc == 0 && wr)
+		if (rc >= 0 && wr)
 			rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1);
 
-		if (rc <= 0) {
-			if (rc == 0)
-				rc = -EINVAL;
+		if (rc < 0)
 			break;
-		}
-		DPRINTK("STEREO EXIT, returning %d\n", val);
-                rc = 0;
+
+		val = rc - 1;
+
+		DPRINTK ("STEREO EXIT, returning %d\n", val);
+		rc = put_user(val, (int *) arg);
 		break;
 
 	/* query or set sampling rate */
@@ -2487,7 +2628,7 @@
 			rc = -EFAULT;
 			break;
 		}
-		DPRINTK("DSP_SPEED, val==%d\n", val);
+		DPRINTK ("DSP_SPEED, val==%d\n", val);
 		if (val < 0) {
 			rc = -EINVAL;
 			break;
@@ -2495,16 +2636,14 @@
 		if (val > 0) {
 			rc = 0;
 
-			if (rc == 0 && rd)
+			if (rd)
 				rc = via_chan_set_speed (card, &card->ch_in, val);
-			if (rc == 0 && wr)
+			if (rc >= 0 && wr)
 				rc = via_chan_set_speed (card, &card->ch_out, val);
 
-			if (rc <= 0) {
-				if (rc == 0)
-					rc = -EINVAL;
+			if (rc < 0)
 				break;
-			}
+
 			val = rc;
 		} else {
 			if (rd)
@@ -2514,7 +2653,7 @@
 			else
 				val = 0;
 		}
-		DPRINTK("SPEED EXIT, returning %d\n", val);
+		DPRINTK ("SPEED EXIT, returning %d\n", val);
                 rc = put_user (val, (int *)arg);
 		break;
 
@@ -2522,7 +2661,7 @@
 	case SNDCTL_DSP_SYNC:
 		DPRINTK ("DSP_SYNC\n");
 		if (wr) {
-			DPRINTK("SYNC EXIT (after calling via_dsp_drain_playback)\n");
+			DPRINTK ("SYNC EXIT (after calling via_dsp_drain_playback)\n");
 			rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
 		}
 		break;
@@ -2531,12 +2670,19 @@
         case SNDCTL_DSP_RESET:
 		DPRINTK ("DSP_RESET\n");
 		if (rd) {
-			via_chan_clear (&card->ch_in);
+			via_chan_clear (card, &card->ch_in);
 			via_chan_pcm_fmt (&card->ch_in, 1);
+			card->ch_in.frag_number = 0;
+			card->ch_in.frag_size = 0;
+			atomic_set(&card->ch_in.n_frags, 0);
 		}
+
 		if (wr) {
-			via_chan_clear (&card->ch_out);
+			via_chan_clear (card, &card->ch_out);
 			via_chan_pcm_fmt (&card->ch_out, 1);
+			card->ch_out.frag_number = 0;
+			card->ch_out.frag_size = 0;
+			atomic_set(&card->ch_out.n_frags, 0);
 		}
 
 		rc = 0;
@@ -2544,40 +2690,47 @@
 
 	/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
 	case SNDCTL_DSP_GETCAPS:
-		DPRINTK("DSP_GETCAPS\n");
+		DPRINTK ("DSP_GETCAPS\n");
 		rc = put_user(VIA_DSP_CAP, (int *)arg);
 		break;
 
-	/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
+	/* obtain buffer fragment size */
 	case SNDCTL_DSP_GETBLKSIZE:
-		DPRINTK("DSP_GETBLKSIZE\n");
-		rc = put_user(VIA_DMA_BUF_SIZE, (int *)arg);
+		DPRINTK ("DSP_GETBLKSIZE\n");
+
+		if (rd) {
+			via_chan_set_buffering(card, &card->ch_in, -1);
+			rc = put_user(card->ch_in.frag_size, (int *)arg);
+		} else if (wr) {
+			via_chan_set_buffering(card, &card->ch_out, -1);
+			rc = put_user(card->ch_out.frag_size, (int *)arg);
+		}
 		break;
 
 	/* obtain information about input buffering */
 	case SNDCTL_DSP_GETISPACE:
-		DPRINTK("DSP_GETISPACE\n");
+		DPRINTK ("DSP_GETISPACE\n");
 		if (rd)
 			rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg);
 		break;
 
 	/* obtain information about output buffering */
 	case SNDCTL_DSP_GETOSPACE:
-		DPRINTK("DSP_GETOSPACE\n");
+		DPRINTK ("DSP_GETOSPACE\n");
 		if (wr)
 			rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg);
 		break;
 
 	/* obtain information about input hardware pointer */
 	case SNDCTL_DSP_GETIPTR:
-		DPRINTK("DSP_GETIPTR\n");
+		DPRINTK ("DSP_GETIPTR\n");
 		if (rd)
 			rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg);
 		break;
 
 	/* obtain information about output hardware pointer */
 	case SNDCTL_DSP_GETOPTR:
-		DPRINTK("DSP_GETOPTR\n");
+		DPRINTK ("DSP_GETOPTR\n");
 		if (wr)
 			rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg);
 		break;
@@ -2585,25 +2738,29 @@
 	/* return number of bytes remaining to be played by DMA engine */
 	case SNDCTL_DSP_GETODELAY:
 		{
-		DPRINTK("DSP_GETODELAY\n");
+		DPRINTK ("DSP_GETODELAY\n");
 
 		chan = &card->ch_out;
 
 		if (!wr)
 			break;
 
-		val = VIA_DMA_BUFFERS - atomic_read (&chan->n_bufs);
+		if (chan->is_active) {
 
-		if (val > 0) {
-			val *= VIA_DMA_BUF_SIZE;
-			val -= VIA_DMA_BUF_SIZE -
-			       inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
-		}
-		val += chan->slop_len;
+			val = chan->frag_number - atomic_read (&chan->n_frags);
 
-		assert (val <= (VIA_DMA_BUF_SIZE * VIA_DMA_BUFFERS));
+			if (val > 0) {
+				val *= chan->frag_size;
+				val -= chan->frag_size -
+				       inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
+			}
+			val += chan->slop_len % chan->frag_size;
+		} else
+			val = 0;
 
-		DPRINTK("GETODELAY EXIT, val = %d bytes\n", val);
+		assert (val <= (chan->frag_size * chan->frag_number));
+
+		DPRINTK ("GETODELAY EXIT, val = %d bytes\n", val);
                 rc = put_user (val, (int *)arg);
 		break;
 		}
@@ -2617,7 +2774,7 @@
 			rc = -EFAULT;
 			break;
 		}
-		DPRINTK("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
+		DPRINTK ("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
 			rd, wr, card->ch_in.is_active, card->ch_out.is_active,
 			card->ch_in.is_enabled, card->ch_out.is_enabled);
 
@@ -2625,6 +2782,7 @@
 
 		if (rd)
 			rc = via_dsp_ioctl_trigger (&card->ch_in, val);
+
 		if (!rc && wr)
 			rc = via_dsp_ioctl_trigger (&card->ch_out, val);
 
@@ -2634,7 +2792,7 @@
 	 * with O_RDWR, this is mainly a no-op that always returns success.
 	 */
 	case SNDCTL_DSP_SETDUPLEX:
-		DPRINTK("DSP_SETDUPLEX\n");
+		DPRINTK ("DSP_SETDUPLEX\n");
 		if (!rd || !wr)
 			break;
 		rc = 0;
@@ -2646,7 +2804,13 @@
 			rc = -EFAULT;
 			break;
 		}
-		DPRINTK("DSP_SETFRAGMENT, val==%d\n", val);
+		DPRINTK ("DSP_SETFRAGMENT, val==%d\n", val);
+
+		if (rd)
+			rc = via_chan_set_buffering(card, &card->ch_in, val);
+
+		if (wr)
+			rc = via_chan_set_buffering(card, &card->ch_out, val);	
 
 		DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n",
 			 val & 0xFFFF,
@@ -2654,13 +2818,12 @@
 			 (val >> 16) & 0xFFFF,
 			 (val >> 16) & 0xFFFF);
 
-		/* just to shut up some programs */
 		rc = 0;
 		break;
 
 	/* inform device of an upcoming pause in input (or output). */
 	case SNDCTL_DSP_POST:
-		DPRINTK("DSP_POST\n");
+		DPRINTK ("DSP_POST\n");
 		if (wr) {
 			if (card->ch_out.slop_len > 0)
 				via_chan_flush_frag (&card->ch_out);
@@ -2678,15 +2841,14 @@
 	}
 
 	up (&card->syscall_sem);
-	DPRINTK("EXIT, returning %d\n", rc);
+	DPRINTK ("EXIT, returning %d\n", rc);
 	return rc;
 }
 
 
 static int via_dsp_open (struct inode *inode, struct file *file)
 {
-	int rc, minor = MINOR(inode->i_rdev);
-	int got_read_chan = 0;
+	int minor = MINOR(inode->i_rdev);
 	struct via_info *card;
 	struct pci_dev *pdev;
 	struct via_channel *chan;
@@ -2733,17 +2895,13 @@
 	}
 
 	file->private_data = card;
-	DPRINTK("file->f_mode == 0x%x\n", file->f_mode);
+	DPRINTK ("file->f_mode == 0x%x\n", file->f_mode);
 
 	/* handle input from analog source */
 	if (file->f_mode & FMODE_READ) {
 		chan = &card->ch_in;
 
-		rc = via_chan_init (card, chan);
-		if (rc)
-			goto err_out;
-
-		got_read_chan = 1;
+		via_chan_init (card, chan);
 
 		/* why is this forced to 16-bit stereo in all drivers? */
 		chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
@@ -2756,30 +2914,28 @@
 	if (file->f_mode & FMODE_WRITE) {
 		chan = &card->ch_out;
 
-		rc = via_chan_init (card, chan);
-		if (rc)
-			goto err_out_read_chan;
+		via_chan_init (card, chan);
 
-		if ((minor & 0xf) == SND_DEV_DSP16) {
-			chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
-			via_set_rate (&card->ac97, chan, 44100);
+		if (file->f_mode & FMODE_READ) {
+			/* if in duplex mode make the recording and playback channels
+			   have the same settings */
+			chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+			via_chan_pcm_fmt (chan, 0);
+                        via_set_rate (&card->ac97, chan, 44100);
 		} else {
-			via_set_rate (&card->ac97, chan, 8000);
+			 if ((minor & 0xf) == SND_DEV_DSP16) {
+				chan->pcm_fmt = VIA_PCM_FMT_16BIT;
+				via_chan_pcm_fmt (chan, 0);
+				via_set_rate (&card->ac97, chan, 44100);
+			} else {
+				via_chan_pcm_fmt (chan, 0);
+				via_set_rate (&card->ac97, chan, 8000);
+			}
 		}
-
-		via_chan_pcm_fmt (chan, 0);
 	}
 
 	DPRINTK ("EXIT, returning 0\n");
 	return 0;
-
-err_out_read_chan:
-	if (got_read_chan)
-		via_chan_free (card, &card->ch_in);
-err_out:
-	up (&card->open_sem);
-	DPRINTK("ERROR EXIT, returning %d\n", rc);
-	return rc;
 }
 
 
@@ -2807,15 +2963,18 @@
 			printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc);
 
 		via_chan_free (card, &card->ch_out);
+		via_chan_buffer_free(card, &card->ch_out);
 	}
 
-	if (file->f_mode & FMODE_READ)
+	if (file->f_mode & FMODE_READ) {
 		via_chan_free (card, &card->ch_in);
+		via_chan_buffer_free (card, &card->ch_in);
+	}
 
 	up (&card->syscall_sem);
 	up (&card->open_sem);
 
-	DPRINTK("EXIT, returning 0\n");
+	DPRINTK ("EXIT, returning 0\n");
 	return 0;
 }
 
@@ -2934,9 +3093,9 @@
 		tmp &= 0xF0;
 		tmp |= pdev->irq;
 		pci_write_config_byte (pdev, 0x3C, tmp);
-		DPRINTK("new 0x3c==0x%02x\n", tmp);
+		DPRINTK ("new 0x3c==0x%02x\n", tmp);
 	} else {
-		DPRINTK("IRQ reg 0x3c==0x%02x, irq==%d\n",
+		DPRINTK ("IRQ reg 0x3c==0x%02x, irq==%d\n",
 			tmp, tmp & 0x0F);
 	}
 
@@ -3036,12 +3195,12 @@
 
 static void __exit cleanup_via82cxxx_audio(void)
 {
-	DPRINTK("ENTER\n");
+	DPRINTK ("ENTER\n");
 
 	pci_unregister_driver (&via_driver);
 	via_cleanup_proc ();
 
-	DPRINTK("EXIT\n");
+	DPRINTK ("EXIT\n");
 }
 
 
@@ -3133,7 +3292,7 @@
 
 		);
 
-	DPRINTK("EXIT, returning %d\n", len);
+	DPRINTK ("EXIT, returning %d\n", len);
 	return len;
 
 #undef YN
diff -urN linux-2.4.1-pre10/drivers/sound/ymfpci.c linux/drivers/sound/ymfpci.c
--- linux-2.4.1-pre10/drivers/sound/ymfpci.c	Mon Dec 11 13:32:24 2000
+++ linux/drivers/sound/ymfpci.c	Mon Dec  3 22:26:35 2001
@@ -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);
 }
 
diff -urN linux-2.4.1-pre10/drivers/sound/ymfpci.h linux/drivers/sound/ymfpci.h
--- linux-2.4.1-pre10/drivers/sound/ymfpci.h	Mon Dec 11 13:32:24 2000
+++ linux/drivers/sound/ymfpci.h	Mon Dec  3 22:26:35 2001
@@ -21,6 +21,7 @@
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  */
+#include <linux/config.h>
 
 /*
  *  Direct registers
@@ -131,7 +132,12 @@
 #define YDSXG_AC97READCMD		0x8000
 #define YDSXG_AC97WRITECMD		0x0000
 
+#define PCIR_LEGCTRL			0x40
+#define PCIR_ELEGCTRL			0x42
 #define PCIR_DSXGCTRL			0x48
+#define PCIR_OPLADR			0x60
+#define PCIR_SBADR			0x62
+#define PCIR_MPUADR			0x64
 
 #define YDSXG_DSPLENGTH			0x0080
 #define YDSXG_CTRLLENGTH		0x3000
@@ -185,8 +191,8 @@
 } ymfpci_playback_bank_t;
 
 typedef struct stru_ymfpci_capture_bank {
-	u32 base;			/* 32-bit address */
-	u32 loop_end;			/* 32-bit offset */
+	u32 base;			/* 32-bit address (aligned at 4) */
+	u32 loop_end;			/* size in BYTES (aligned at 4) */
 	u32 start;			/* 32-bit offset */
 	u32 num_of_loops;		/* counter */
 } ymfpci_capture_bank_t;
@@ -198,8 +204,7 @@
 	u32 temp;
 } ymfpci_effect_bank_t;
 
-typedef struct stru_ymfpci_voice ymfpci_voice_t;
-typedef struct ymf_pcm ymfpci_pcm_t;
+typedef struct ymf_voice ymfpci_voice_t;
 /*
  * Throughout the code Yaroslav names YMF unit pointer "codec"
  * even though it does not correspond to any codec. Must be historic.
@@ -214,52 +219,35 @@
 	YMFPCI_MIDI
 } ymfpci_voice_type_t;
 
-struct stru_ymfpci_voice {
-	ymfpci_t *codec;
+struct ymf_voice {
+	// ymfpci_t *codec;
 	int number;
-	int use: 1,
-	    pcm: 1,
-	    synth: 1,
-	    midi: 1;
+	char use, pcm, synth, midi;	// bool
 	ymfpci_playback_bank_t *bank;
-	void (*interrupt)(ymfpci_t *codec, ymfpci_voice_t *voice);
-	ymfpci_pcm_t *ypcm;
+	struct ymf_pcm *ypcm;
 };
 
-typedef enum {
-	PLAYBACK_VOICE,
-	CAPTURE_REC,
-	CAPTURE_AC97,
-	EFFECT_DRY_LEFT,
-	EFFECT_DRY_RIGHT,
-	EFFECT_EFF1,
-	EFFECT_EFF2,
-	EFFECT_EFF3
-} ymfpci_pcm_type_t;
-
-struct ymf_pcm {
-	ymfpci_t *codec;
-	ymfpci_pcm_type_t type;
-	struct ymf_state *state;
-	ymfpci_voice_t *voices[2];	/* playback only */
-	int running;			// +
-	int spdif;
+struct ymf_capture {
+	// struct ymf_unit *unit;
+	int use;
+	ymfpci_capture_bank_t *bank;
+	struct ymf_pcm *ypcm;
 };
 
 struct ymf_unit {
 	u8 rev;				/* PCI revision */
 	void *reg_area_virt;
-	void *work_ptr;				// +
+	void *work_ptr;
 
 	unsigned int bank_size_playback;
 	unsigned int bank_size_capture;
 	unsigned int bank_size_effect;
 	unsigned int work_size;
 
-	void *bank_base_playback;		// +
-	void *bank_base_capture;		// +
-	void *bank_base_effect;			// +
-	void *work_base;			// +
+	void *bank_base_playback;
+	void *bank_base_capture;
+	void *bank_base_effect;
+	void *work_base;
 
 	u32 *ctrl_playback;
 	ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2];
@@ -269,13 +257,20 @@
 	int start_count;
 
 	u32 active_bank;
-	ymfpci_voice_t voices[64];
+	struct ymf_voice voices[64];
+	struct ymf_capture capture[5];
 
 	struct ac97_codec *ac97_codec[NR_AC97];
 	u16 ac97_features;
 
 	struct pci_dev *pci;
 
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+	/* legacy hardware resources */
+	unsigned int iosynth, iomidi;
+	struct address_info opl3_data, mpu_data;
+#endif
+
 	spinlock_t reg_lock;
 	spinlock_t voice_lock;
 
@@ -284,14 +279,11 @@
 	struct semaphore open_sem;
 
 	struct list_head ymf_devs;
-	struct ymf_state *states[1];			// *
-	/* ypcm may be the same thing as state, but not for record, effects. */
+	struct list_head states;	/* List of states for this unit */
+	/* For the moment we do not traverse list of states so it is
+	 * entirely useless. Will be used (PM) or killed. XXX */
 };
 
-/*
- * "Software" or virtual channel, an instance of opened /dev/dsp.
- */
-
 struct ymf_dmabuf {
 
 	/* OSS buffer management stuff */
@@ -312,7 +304,6 @@
 	/* redundant, but makes calculations easier */
 	unsigned fragsize;
 	unsigned dmasize;	/* Total rawbuf[] size */
-	unsigned fragsamples;
 
 	/* OSS stuff */
 	unsigned mapped:1;
@@ -329,15 +320,40 @@
 	int shift;			/* redundant, computed from the above */
 };
 
-struct ymf_state {
-	struct ymf_unit *unit;			/* backpointer */
+typedef enum {
+	PLAYBACK_VOICE,
+	CAPTURE_REC,
+	CAPTURE_AC97,
+	EFFECT_DRY_LEFT,
+	EFFECT_DRY_RIGHT,
+	EFFECT_EFF1,
+	EFFECT_EFF2,
+	EFFECT_EFF3
+} ymfpci_pcm_type_t;
 
-	/* virtual channel number */
-	int virt;				// * unused a.t.m.
+/* This is variant record, but we hate unions. Little waste on pointers []. */
+struct ymf_pcm {
+	ymfpci_pcm_type_t type;
+	struct ymf_state *state;
+
+	ymfpci_voice_t *voices[2];
+	int capture_bank_number;
+
+	struct ymf_dmabuf dmabuf;
+	int running;
+	int spdif;
+};
 
-	struct ymf_pcm ypcm;			// *
-	struct ymf_dmabuf dmabuf;		// *
-	struct ymf_pcm_format format;		// *
+/*
+ * "Software" or virtual channel, an instance of opened /dev/dsp.
+ * It may have two physical channels (pcms) for duplex operations.
+ */
+
+struct ymf_state {
+	struct list_head chain;
+	struct ymf_unit *unit;			/* backpointer */
+	struct ymf_pcm rpcm, wpcm;
+	struct ymf_pcm_format format;
 };
 
 #endif				/* __YMFPCI_H */
diff -urN linux-2.4.1-pre10/fs/nfs/flushd.c linux/fs/nfs/flushd.c
--- linux-2.4.1-pre10/fs/nfs/flushd.c	Mon Dec  3 22:26:28 2001
+++ linux/fs/nfs/flushd.c	Mon Dec  3 22:26:36 2001
@@ -34,7 +34,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
 
-#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
 
 #include <linux/nfs.h>
 #include <linux/nfs_fs.h>
@@ -53,11 +53,6 @@
 static struct rpc_wait_queue    flushd_queue = RPC_INIT_WAITQ("nfs_flushd");
 
 /*
- * Spinlock
- */
-spinlock_t nfs_flushd_lock = SPIN_LOCK_UNLOCKED;
-
-/*
  * Local function declarations.
  */
 static void	nfs_flushd(struct rpc_task *);
@@ -68,17 +63,19 @@
 {
 	struct nfs_reqlist	*cache;
 	struct rpc_task		*task;
-	int			status = 0;
+	int			status;
 
 	dprintk("NFS: writecache_init\n");
 
+	lock_kernel();
+	status = -ENOMEM;
 	/* Create the RPC task */
 	if (!(task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC)))
-		return -ENOMEM;
+		goto out_unlock;
 
-	spin_lock(&nfs_flushd_lock);
 	cache = server->rw_requests;
 
+	status = 0;
 	if (cache->task)
 		goto out_unlock;
 
@@ -93,12 +90,13 @@
 	task->tk_action   = nfs_flushd;
 	task->tk_exit   = nfs_flushd_exit;
 
-	spin_unlock(&nfs_flushd_lock);
 	rpc_execute(task);
+	unlock_kernel();
 	return 0;
  out_unlock:
-	spin_unlock(&nfs_flushd_lock);
-	rpc_release_task(task);
+	if (task)
+		rpc_release_task(task);
+	unlock_kernel();
 	return status;
 }
 
@@ -106,23 +104,24 @@
 {
 	struct nfs_reqlist      *cache;
 
+	lock_kernel();
 	cache = server->rw_requests;
 	if (!cache)
-		return;
+		goto out;
 
 	dprintk("NFS: reqlist_exit (ptr %p rpc %p)\n", cache, cache->task);
+
 	while (cache->task || cache->inodes) {
-		spin_lock(&nfs_flushd_lock);
 		if (!cache->task) {
-			spin_unlock(&nfs_flushd_lock);
 			nfs_reqlist_init(server);
 		} else {
 			cache->task->tk_status = -ENOMEM;
 			rpc_wake_up_task(cache->task);
-			spin_unlock(&nfs_flushd_lock);
 		}
 		interruptible_sleep_on_timeout(&cache->request_wait, 1 * HZ);
 	}
+ out:
+	unlock_kernel();
 }
 
 int nfs_reqlist_alloc(struct nfs_server *server)
@@ -161,7 +160,6 @@
 	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
 	struct inode		**q;
 
-	spin_lock(&nfs_flushd_lock);
 	if (NFS_FLAGS(inode) & NFS_INO_FLUSH)
 		goto out;
 	inode->u.nfs_i.hash_next = NULL;
@@ -177,7 +175,6 @@
 	NFS_FLAGS(inode) |= NFS_INO_FLUSH;
 	atomic_inc(&inode->i_count);
  out:
-	spin_unlock(&nfs_flushd_lock);
 }
 
 void inode_remove_flushd(struct inode *inode)
@@ -185,7 +182,7 @@
 	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
 	struct inode		**q;
 
-	spin_lock(&nfs_flushd_lock);
+	lock_kernel();
 	if (!(NFS_FLAGS(inode) & NFS_INO_FLUSH))
 		goto out;
 
@@ -195,12 +192,10 @@
 	if (*q) {
 		*q = inode->u.nfs_i.hash_next;
 		NFS_FLAGS(inode) &= ~NFS_INO_FLUSH;
-		spin_unlock(&nfs_flushd_lock);
 		iput(inode);
-		return;
 	}
  out:
-	spin_unlock(&nfs_flushd_lock);
+	unlock_kernel();
 }
 
 void inode_schedule_scan(struct inode *inode, unsigned long time)
@@ -209,6 +204,7 @@
 	struct rpc_task		*task;
 	unsigned long		mintimeout;
 
+	lock_kernel();
 	if (time_after(NFS_NEXTSCAN(inode), time))
 		NFS_NEXTSCAN(inode) = time;
 	mintimeout = jiffies + 1 * HZ;
@@ -216,16 +212,14 @@
 		mintimeout = NFS_NEXTSCAN(inode);
 	inode_append_flushd(inode);
 
-	spin_lock(&nfs_flushd_lock);
 	task = cache->task;
 	if (!task) {
-		spin_unlock(&nfs_flushd_lock);
 		nfs_reqlist_init(NFS_SERVER(inode));
 	} else {
 		if (time_after(cache->runat, mintimeout))
 			rpc_wake_up_task(task);
-		spin_unlock(&nfs_flushd_lock);
 	}
+	unlock_kernel();
 }
 
 
@@ -242,10 +236,8 @@
 	server = (struct nfs_server *) task->tk_calldata;
         cache = server->rw_requests;
 
-	spin_lock(&nfs_flushd_lock);
 	next = cache->inodes;
 	cache->inodes = NULL;
-	spin_unlock(&nfs_flushd_lock);
 
 	while ((inode = next) != NULL) {
 		next = next->u.nfs_i.hash_next;
@@ -282,13 +274,11 @@
 	task->tk_timeout = delay;
 	cache->runat = jiffies + task->tk_timeout;
 
-	spin_lock(&nfs_flushd_lock);
 	if (!atomic_read(&cache->nr_requests) && !cache->inodes) {
 		cache->task = NULL;
 		task->tk_action = NULL;
 	} else
 		rpc_sleep_on(&flushd_queue, task, NULL, NULL);
-	spin_unlock(&nfs_flushd_lock);
 }
 
 static void
@@ -299,10 +289,8 @@
 	server = (struct nfs_server *) task->tk_calldata;
 	cache = server->rw_requests;
 
-	spin_lock(&nfs_flushd_lock);
 	if (cache->task == task)
 		cache->task = NULL;
-	spin_unlock(&nfs_flushd_lock);
 	wake_up(&cache->request_wait);
 }
 
diff -urN linux-2.4.1-pre10/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h
--- linux-2.4.1-pre10/include/asm-alpha/unistd.h	Fri Aug 11 14:29:03 2000
+++ linux/include/asm-alpha/unistd.h	Mon Dec  3 22:26:36 2001
@@ -572,7 +572,6 @@
 	return sys_sync();
 }
 
-extern long sys_wait4(int, int *, int, struct rusage *);
 static inline pid_t waitpid(int pid, int * wait_stat, int flags)
 {
 	return sys_wait4(pid, wait_stat, flags, NULL);
diff -urN linux-2.4.1-pre10/mm/memory.c linux/mm/memory.c
--- linux-2.4.1-pre10/mm/memory.c	Mon Dec  3 22:26:28 2001
+++ linux/mm/memory.c	Mon Dec  3 22:26:36 2001
@@ -945,7 +945,6 @@
 	if (inode->i_size < offset)
 		goto do_expand;
 	inode->i_size = offset;
-	truncate_inode_pages(mapping, offset);
 	spin_lock(&mapping->i_shared_lock);
 	if (!mapping->i_mmap && !mapping->i_mmap_shared)
 		goto out_unlock;
@@ -960,8 +959,7 @@
 
 out_unlock:
 	spin_unlock(&mapping->i_shared_lock);
-	/* this should go into ->truncate */
-	inode->i_size = offset;
+	truncate_inode_pages(mapping, offset);
 	if (inode->i_op && inode->i_op->truncate)
 		inode->i_op->truncate(inode);
 	return;
diff -urN linux-2.4.1-pre10/mm/shmem.c linux/mm/shmem.c
--- linux-2.4.1-pre10/mm/shmem.c	Mon Dec  3 22:26:28 2001
+++ linux/mm/shmem.c	Mon Dec  3 22:26:36 2001
@@ -217,8 +217,11 @@
 
 	info = &page->mapping->host->u.shmem_i;
 	swap = __get_swap_page(2);
-	if (!swap.val)
-		return 1;
+	if (!swap.val) {
+		set_page_dirty(page);
+		UnlockPage(page);
+		return -ENOMEM;
+	}
 
 	spin_lock(&info->lock);
 	shmem_recalc_inode(page->mapping->host);
diff -urN linux-2.4.1-pre10/net/sunrpc/auth.c linux/net/sunrpc/auth.c
--- linux-2.4.1-pre10/net/sunrpc/auth.c	Fri Oct 13 12:11:26 2000
+++ linux/net/sunrpc/auth.c	Mon Dec  3 22:26:36 2001
@@ -267,22 +267,26 @@
 	dprintk("RPC: %4d holding %s cred %p\n",
 		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	if (task->tk_msg.rpc_cred) {
+		spin_lock(&rpc_credcache_lock);
 		task->tk_msg.rpc_cred->cr_count++;
 		task->tk_msg.rpc_cred->cr_expire = jiffies + task->tk_auth->au_expire;
+		spin_unlock(&rpc_credcache_lock);
 	}
 }
 
 void
 rpcauth_releasecred(struct rpc_auth *auth, struct rpc_cred *cred)
 {
+	spin_lock(&rpc_credcache_lock);
 	if (cred != NULL && cred->cr_count > 0) {
-		cred->cr_count--;
-		if (cred->cr_flags & RPCAUTH_CRED_DEAD) {
+		if (!--cred->cr_count && (cred->cr_flags & RPCAUTH_CRED_DEAD)) {
+			spin_unlock(&rpc_credcache_lock);
 			rpcauth_remove_credcache(auth, cred);
-			if (!cred->cr_count)
-				rpcauth_crdestroy(auth, cred);
+			rpcauth_crdestroy(auth, cred);
+			return;
 		}
 	}
+	spin_unlock(&rpc_credcache_lock);
 }
 
 void
@@ -335,13 +339,19 @@
 {
 	dprintk("RPC: %4d invalidating %s cred %p\n",
 		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
+	spin_lock(&rpc_credcache_lock);
 	if (task->tk_msg.rpc_cred)
 		task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
+	spin_unlock(&rpc_credcache_lock);
 }
 
 int
 rpcauth_uptodatecred(struct rpc_task *task)
 {
-	return !(task->tk_msg.rpc_cred) ||
+	int retval;
+	spin_lock(&rpc_credcache_lock);
+	retval = !(task->tk_msg.rpc_cred) ||
 		(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
+	spin_unlock(&rpc_credcache_lock);
+	return retval;
 }
diff -urN linux-2.4.1-pre10/net/unix/sysctl_net_unix.c linux/net/unix/sysctl_net_unix.c
--- linux-2.4.1-pre10/net/unix/sysctl_net_unix.c	Mon Dec  3 22:26:28 2001
+++ linux/net/unix/sysctl_net_unix.c	Mon Dec  3 22:26:36 2001
@@ -11,7 +11,6 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
-#include <linux/config.h>
 
 extern int sysctl_unix_max_dgram_qlen;
 
diff -urN linux-2.4.1-pre10/net/x25/x25_facilities.c linux/net/x25/x25_facilities.c
--- linux-2.4.1-pre10/net/x25/x25_facilities.c	Mon Dec  3 22:26:28 2001
+++ linux/net/x25/x25_facilities.c	Mon Dec  3 22:26:36 2001
@@ -18,7 +18,6 @@
  *					  negotiation.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -urN linux-2.4.1-pre10/net/x25/x25_in.c linux/net/x25/x25_in.c
--- linux-2.4.1-pre10/net/x25/x25_in.c	Mon Dec  3 22:26:28 2001
+++ linux/net/x25/x25_in.c	Mon Dec  3 22:26:36 2001
@@ -22,7 +22,6 @@
  *					  i-frames.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -urN linux-2.4.1-pre10/net/x25/x25_link.c linux/net/x25/x25_link.c
--- linux-2.4.1-pre10/net/x25/x25_link.c	Mon Dec  3 22:26:28 2001
+++ linux/net/x25/x25_link.c	Mon Dec  3 22:26:36 2001
@@ -20,7 +20,6 @@
  *	2000-09-04	Henner Eisen	  dev_hold() / dev_put() for x25_neigh.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -urN linux-2.4.1-pre10/net/x25/x25_out.c linux/net/x25/x25_out.c
--- linux-2.4.1-pre10/net/x25/x25_out.c	Mon Dec  3 22:26:28 2001
+++ linux/net/x25/x25_out.c	Mon Dec  3 22:26:36 2001
@@ -21,7 +21,6 @@
  *					needed cleaned seq-number fields.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -urN linux-2.4.1-pre10/net/x25/x25_subr.c linux/net/x25/x25_subr.c
--- linux-2.4.1-pre10/net/x25/x25_subr.c	Mon Dec  3 22:26:28 2001
+++ linux/net/x25/x25_subr.c	Mon Dec  3 22:26:36 2001
@@ -19,7 +19,6 @@
  *					  negotiation.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -urN linux-2.4.1-pre10/net/x25/x25_timer.c linux/net/x25/x25_timer.c
--- linux-2.4.1-pre10/net/x25/x25_timer.c	Mon Dec  3 22:26:28 2001
+++ linux/net/x25/x25_timer.c	Mon Dec  3 22:26:36 2001
@@ -18,7 +18,6 @@
  *					Centralised disconnection processing.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
