]> git.neil.brown.name Git - history.git/commitdiff
v2.4.0.10 -> v2.4.0.11
authorLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 01:52:31 +0000 (17:52 -0800)
committerLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 01:52:31 +0000 (17:52 -0800)
  - Trond Myklebust: NFS/RPC client SMP fixes
  - rth: alpha pyxis and cabriolet fixes
  - remove broken sys_wait4() declarations
  - disable radeon debugging code
  - VIA IDE driver should not enable autodma unless asked for
  - Andrey Savochkin: eepro100 update. Should fix the resource timing problems.
  - Jeff Garzik: via82cxxx_audio update
  - YMF7xx PCI audio update: get rid of old broken driver, make new
  driver handle legacy control too.
  - fix missed wakeup on block device request list
  - hpt366 controller doesn't play nice with some IBM harddisks
  - remove inode pages from the page cache only after having removed them
  from the page tables.
  - shared memory out-of-swap writepage() fixup (no more magic return)

48 files changed:
Makefile
arch/alpha/kernel/Makefile
arch/alpha/kernel/osf_sys.c
arch/alpha/kernel/signal.c
arch/alpha/kernel/sys_cabriolet.c
arch/arm/kernel/signal.c
arch/ia64/ia32/sys_ia32.c
arch/m68k/kernel/signal.c
arch/mips/kernel/irixsig.c
arch/mips/kernel/signal.c
arch/mips64/kernel/linux32.c
arch/mips64/kernel/signal.c
arch/mips64/kernel/signal32.c
arch/ppc/kernel/open_pic.h
arch/ppc/kernel/pci.c
arch/ppc/kernel/prep_pci.c
arch/ppc/kernel/signal.c
arch/s390/kernel/signal.c
arch/sh/kernel/signal.c
arch/sparc/kernel/signal.c
arch/sparc/kernel/sys_sunos.c
arch/sparc64/kernel/signal.c
arch/sparc64/kernel/signal32.c
arch/sparc64/kernel/sys_sparc32.c
drivers/acpi/acpi_ksyms.c
drivers/acpi/include/aclinux.h
drivers/block/ll_rw_blk.c
drivers/char/drm/radeon_state.c
drivers/ide/hpt366.c
drivers/ide/via82cxxx.c
drivers/net/eepro100.c
drivers/sound/Config.in
drivers/sound/Makefile
drivers/sound/via82cxxx_audio.c
drivers/sound/ymfpci.c
drivers/sound/ymfpci.h
fs/nfs/flushd.c
include/asm-alpha/unistd.h
mm/memory.c
mm/shmem.c
net/sunrpc/auth.c
net/unix/sysctl_net_unix.c
net/x25/x25_facilities.c
net/x25/x25_in.c
net/x25/x25_link.c
net/x25/x25_out.c
net/x25/x25_subr.c
net/x25/x25_timer.c

index 7894f6c528fe0e9ae5f5ffb7595b1885a0f1109a..5c35d263b89935db06871b1f436c912c840931c4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 1
-EXTRAVERSION =-pre10
+EXTRAVERSION =-pre11
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
index 85cc7ed483094b77ae8ab78b91c274cb4fa4cc91..abc04cca20f25ad9a70369981162150669bb0028 100644 (file)
@@ -23,7 +23,7 @@ obj-y    := entry.o traps.o process.o osf_sys.o irq.o irq_alpha.o \
 # 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 @@ obj-y          += core_apecs.o core_cia.o core_irongate.o core_lca.o core_mcpcia.o \
            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
 
@@ -94,6 +94,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
 
 all: kernel.o head.o
index 4d574b824690ed201fbf2caf7aeee16a5128c207..cd28b07fc1958c4f482813f7e503510e2e788ff6 100644 (file)
@@ -904,7 +904,6 @@ extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz);
 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
index 73df1e93051699ec6ea5f5eb1776d02476eb09c8..e3d75f7a1a4fd4d6a48eea5643ddb6da95685c6c 100644 (file)
@@ -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);
index 624c6732c2b35d2676f2ca5f4205633c9cf1bcd9..58a8f46589d357e8d66fd6283666238bad2d6292 100644 (file)
@@ -42,7 +42,7 @@ static inline void
 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
index 1af2abd84b07fa020524c75d4439b5da40ad507b..1b8f88815893fb704c8693dae0f0c0667e66c4fc 100644 (file)
@@ -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)
index f7b857b4c58f7de88071e6c139449c81a012a44c..25007aa43f92c0758ac5669bf4fb7bd0685aa5f4 100644 (file)
@@ -1957,9 +1957,6 @@ put_rusage (struct rusage32 *ru, struct rusage *r)
        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)
index 9d754f9b8e2be86354257ba7da8c62b346fb5f7a..e9c41780c84ade21aaaf9de79fe331c14d123eee 100644 (file)
@@ -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] = {
index 6f6556da484f1d62330581792b89a2b2db380a74..66c37fefc81e3c4ab7e5075eddd9be2f41b5bd55 100644 (file)
@@ -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
index ad66fcb6c9e03c01798118820896a90258d429a0..b2d2a6a70c1e334e7761984b955ebdff76034ff0 100644 (file)
@@ -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);
index 2e64aa465558d5057c04e95ddfd4d7d3cf55575e..f1a6084e5240803f747b9c5bdee7160958d12277 100644 (file)
@@ -585,9 +585,6 @@ put_rusage (struct rusage32 *ru, struct rusage *r)
        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)
index 15b2fc60b8754fadbfdc659fbf3bd2eee422528a..f30df53a33e10cacc20c48b5d0d4e8b4892ccbc0 100644 (file)
@@ -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);
index 548f271b5255ae7930cc9397a81a946e8c5f979d..6dad16160c024e3951399756a4357ce12a09164d 100644 (file)
@@ -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);
index ac99cd1e04f9e79ec11bc43c712a61e60eeec155..cf924ba3a5d501c73b1b125ba01abf243b93f082 100644 (file)
@@ -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 */
index 30a5e70058eead57b1802c2da2f4e340b9ebc78d..1e503fae4b8d6591ae5ff20c7d42a0b484181e70 100644 (file)
@@ -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>
index 633521c4a86dd7e8dddf5b1964702b5b20c48400..2f556d83dc99fd951cbd12ee2d8335379b00eb68 100644 (file)
@@ -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>
index dd3d1ae1b557006b6410f9dd43aac6ef67314666..fe16fae25f5d62f52827db659f7b16d5b5b57d70 100644 (file)
@@ -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)
 {
index a4df59c65c0cddafbfa646721bc32f7270e22ff9..7809a4a2bb93ac1ecd688ff50807406170266a27 100644 (file)
@@ -52,8 +52,6 @@ typedef struct
        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));
 
 /*
index fe2f1b31958fe198437f8695e737e8a1889db457..1e3cb5277c0440faef7423f82b81847624d04ab1 100644 (file)
@@ -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)
index 6ac8c4b00fa0c654df9bec72c544ac003dae78fb..957ddc17ca30fb2c880e08e9078516b6906f9881 100644 (file)
@@ -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
@@ -28,9 +28,6 @@
 
 #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);
 extern void fpload(unsigned long *fpregs, unsigned long *fsr);
index ac252f55a73552602d7384371906d7e9c7414930..24ae3829d313bbed4244f648b0a9b0db63acf9a9 100644 (file)
@@ -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 @@ asmlinkage int sunos_setpgrp(pid_t pid, pid_t pgid)
 }
 
 /* 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;
index c2a7833fbdbc258ce84fa507674e367d5d669eaf..23d0774b4b4700e6b0ce3307e7fab9ec0f7bc43f 100644 (file)
@@ -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
@@ -31,9 +31,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,
                         unsigned long orig_o0, int ret_from_syscall);
 
index 6d06328dd012f0540fb55be641f9bcb51733f149..0886d9d39499b4831db67d6dd7a6ab3f7dfc1331 100644 (file)
@@ -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
@@ -29,9 +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_signal32(sigset_t *oldset, struct pt_regs *regs,
                         unsigned long orig_o0, int ret_from_syscall);
 
index 775c986c850aa1872e86831d13e136a6084a1ca1..184b4169dffb11d646c99e4556606c83ba178ebd 100644 (file)
@@ -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)
@@ -1794,9 +1794,6 @@ static int put_rusage (struct rusage32 *ru, struct rusage *r)
        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)
 {
        if (!ru)
index cb4c5a4b92a5330985b2925080046f7748fba6d2..13f4fe0e78fc18dcf50303e9671dc9350f662ea8 100644 (file)
@@ -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>
index 77a634234568c5b998ed67bee79108e852fb6ab2..0cf0e2845519bb1f40fc667888e8d4c94a2cebb6 100644 (file)
@@ -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>
index 98019ba5d264b50e9bfb1c8ac965e219aae9bf6b..7b7a6f2066b68043adbc5e657111c4df4ccf1cec 100644 (file)
@@ -630,15 +630,8 @@ void inline blkdev_release_request(struct request *req)
                    && 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]);
 
index 7bfefb2cada5ceb8cdfac1137328779ea9c1c17c..1a4da800574e46fca291a52f8b4ead865e3b3c81 100644 (file)
@@ -778,7 +778,7 @@ static void radeon_cp_dispatch_vertex( drm_device_t *dev,
 
        radeon_update_ring_snapshot( dev_priv );
 
-       if ( 1 )
+       if ( 0 )
                radeon_print_dirty( "dispatch_vertex", sarea_priv->dirty );
 
        if ( buf->used ) {
index 62e2437e58405b60102f4e45c9741678e94767b6..38cde0b40441a60f009e9e316e3b89a9b4c0f972 100644 (file)
@@ -55,6 +55,15 @@ const char *bad_ata100_5[] = {
 };
 
 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
 };
index 3ea1e261ece4224c784f015a1880c0d7a74b4231..356e7a4ccbd1d3bdc979f92a351e54eb561c1f49 100644 (file)
@@ -602,7 +602,9 @@ void __init ide_init_via82cxxx(ide_hwif_t *hwif)
 #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 */
 }
index 298a317e2297310844bb9a548fa5af28f0ff4e21..2970fac9f0382adf548980e008d2285584a790d1 100644 (file)
@@ -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 @@ static int speedo_found1(struct pci_dev *pdev,
           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 @@ static int speedo_found1(struct pci_dev *pdev,
 #endif  /* kernel_bloat */
 
        outl(PortReset, ioaddr + SCBPort);
+       inl(ioaddr + SCBPort);
        udelay(10);
 
        /* Return the chip to its original power state. */
@@ -801,7 +803,7 @@ static int speedo_found1(struct pci_dev *pdev,
        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 @@ static void speedo_resume(struct net_device *dev)
        /* 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 @@ static void speedo_resume(struct net_device *dev)
        /* 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 @@ static void speedo_resume(struct net_device *dev)
 
        /* 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 @@ static void speedo_tx_timeout(struct net_device *dev)
                /* 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);
index 9fd35efcbb4886c0732fc16eb073d6e6eeeb4104..10235dd2e516e8af8b0851b0e505f1c825bc72cc 100644 (file)
@@ -142,9 +142,9 @@ if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
    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
   
index e7ee317e6743d2146d4d7a17bf291b6344346137..824f08ec87f6a68487db975c381bf93058178bb6 100644 (file)
@@ -47,8 +47,10 @@ obj-$(CONFIG_SOUND_ACI_MIXER)        += aci.o
 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
index f609d1e2b794375d1ae801a3cc0d7ec6bab37511..fbe02bffd88f766c4203a57c219cc1256e3ba9ce 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 
-#define VIA_VERSION    "1.1.14"
+#define VIA_VERSION    "1.1.14a"
 
 
 #include <linux/config.h>
 #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
 #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
 #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 @@ enum via_channel_states {
 };
 
 
-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 @@ struct via_channel {
        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 unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wa
 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 @@ static void via_chan_init_defaults (struct via_info *card, struct via_channel *c
        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 @@ static void via_chan_init_defaults (struct via_info *card, struct via_channel *c
  *     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 @@ static int via_chan_init (struct via_info *card, struct via_channel *chan)
        }
 
        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 @@ static int via_chan_init (struct via_info *card, struct via_channel *chan)
        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 @@ static int via_chan_init (struct via_info *card, struct via_channel *chan)
 
 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 @@ err_out_nomem:
 
 static void via_chan_free (struct via_info *card, struct via_channel *chan)
 {
-       int i;
-
        DPRINTK ("ENTER\n");
 
        synchronize_irq();
@@ -710,23 +758,33 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan)
 
        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 @@ static void via_chan_pcm_fmt (struct via_channel *chan, int reset)
        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 @@ static void via_chan_pcm_fmt (struct via_channel *chan, int reset)
  *     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 @@ static void via_chan_clear (struct via_channel *chan)
        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 @@ static int via_chan_set_speed (struct via_info *card,
 {
        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 @@ static int via_chan_set_fmt (struct via_info *card,
                 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 @@ static int via_chan_set_stereo (struct via_info *card,
 {
        DPRINTK ("ENTER, channels = %d\n", val);
 
-       via_chan_clear (chan);
+       via_chan_clear (card, chan);
 
        switch (val) {
 
@@ -934,6 +989,78 @@ static int via_chan_set_stereo (struct via_info *card,
        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 @@ static void via_chan_dump_bufs (struct via_channel *chan)
 {
        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 @@ static void via_chan_flush_frag (struct via_channel *chan)
 
        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 @@ static inline void via_chan_maybe_start (struct via_channel *chan)
        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 @@ static loff_t via_llseek(struct file *file, loff_t offset, int origin)
 {
        DPRINTK ("ENTER\n");
 
-       DPRINTK("EXIT, returning -ESPIPE\n");
+       DPRINTK ("EXIT, returning -ESPIPE\n");
        return -ESPIPE;
 }
 
@@ -1245,7 +1372,7 @@ static int __init via_ac97_reset (struct via_info *card)
                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 @@ static int __init via_ac97_init (struct via_info *card)
        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 @@ static int __init via_ac97_init (struct via_info *card)
 
 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 @@ static void via_intr_channel (struct via_channel *chan)
 
        /* 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 @@ static void via_intr_channel (struct via_channel *chan)
        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 @@ static struct file_operations via_dsp_fops = {
        poll:           via_dsp_poll,
        llseek:         via_llseek,
        ioctl:          via_dsp_ioctl,
-#ifdef VIA_SUPPORT_MMAP
        mmap:           via_dsp_mmap,
-#endif
 };
 
 
@@ -1668,7 +1793,6 @@ static void via_dsp_cleanup (struct via_info *card)
 }
 
 
-#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 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma,
                 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 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma,
 
 #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 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma,
 
        /* 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 @@ static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma)
                 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 @@ static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma)
        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 @@ handle_one_block:
         */
        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 @@ handle_one_block:
                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 @@ handle_one_block:
        /* 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 @@ handle_one_block:
        /* 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 @@ static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_
                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 @@ handle_one_block:
        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 @@ handle_one_block:
        }
 
        /* 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 @@ handle_one_block:
        /* 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 @@ static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count
                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 @@ static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wa
        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 @@ static int via_dsp_drain_playback (struct via_info *card,
        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 @@ static int via_dsp_drain_playback (struct via_info *card,
                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 @@ static int via_dsp_drain_playback (struct via_info *card,
                        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 @@ static int via_dsp_drain_playback (struct via_info *card,
                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 @@ static int via_dsp_drain_playback (struct via_info *card,
                         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 @@ static int via_dsp_ioctl_space (struct via_info *card,
 {
        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 @@ static int via_dsp_ioctl_ptr (struct via_info *card,
 
        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
 
        /* 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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
         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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
 
        /* 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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
        /* 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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
 
                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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
         * 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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                        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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
                         (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 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file,
        }
 
        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 @@ match:
        }
 
        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 @@ match:
        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 @@ static int via_dsp_release(struct inode *inode, struct file *file)
                        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 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id
                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 int __init init_via82cxxx_audio(void)
 
 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 @@ static int via_info_read_proc (char *page, char **start, off_t off,
 
                );
 
-       DPRINTK("EXIT, returning %d\n", len);
+       DPRINTK ("EXIT, returning %d\n", len);
        return len;
 
 #undef YN
index a8cdc58f2fd37ab6de1af7c342a7bf37698fca0f..9670951f866643badce5d0589452614af3be432a 100644 (file)
  *
  * 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>
 #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 @@ MODULE_DEVICE_TABLE(pci, ymf_id_tbl);
 /*
  * 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 @@ static void ymf_pcm_update_shift(struct ymf_pcm_format *f)
                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 @@ static int alloc_dmabuf(struct ymf_state *state)
 }
 
 /* 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 @@ static void dealloc_dmabuf(struct ymf_state *state)
        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 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
        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 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
 
        /* 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 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
                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 @@ static int prog_dmabuf(struct ymf_state *state, unsigned rec)
         *      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 int prog_dmabuf(struct ymf_state *state, unsigned rec)
 
 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_start_dac(struct ymf_state *state)
 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 @@ static void ymf_wait_dac(struct ymf_state *state)
        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 @@ static void ymf_wait_dac(struct ymf_state *state)
         */
 }
 
+/* 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 @@ static void ymfpci_hw_stop(ymfpci_t *codec)
  *  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 @@ static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfp
                        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 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
                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 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
                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 @@ static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
        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 @@ static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd)
        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 @@ static int ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
                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 @@ static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo,
 }
 
 /*
- * 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 @@ static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state)
                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)
+static int ymf_capture_prepare(struct ymf_state *state)
 {
-       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_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 @@ void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                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 @@ void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        }
 }
 
-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 @@ static int ymf_state_alloc(ymfpci_t *unit, int nvirt)
        }
        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 @@ static int ymfpci_capture_open(void *private_data,
 #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 @@ static loff_t ymf_llseek(struct file *file, loff_t offset, int origin)
        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)
+/*
+ * 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;
+       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 @@ static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *p
                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 @@ static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *p
                        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 @@ static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *p
                           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 @@ static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
                }
                if (dmabuf->count == 0) {
                        swptr = dmabuf->hwptr;
-                       if (state->ypcm.running) {
+                       if (state->wpcm.running) {
                                /*
                                 * Add uncertainty reserve.
                                 */
@@ -1410,8 +1389,8 @@ static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
                         * 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 @@ static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
                 */
                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 ssize_t ymf_write(struct file *file, const char *buffer, size_t count, lo
 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 unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait)
 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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
     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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
        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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
         * 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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                 * 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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                        return -EINVAL;
                dmabuf->subdivision = val;
                return 0;
+#endif
 
        case SNDCTL_DSP_SETFRAGMENT:
                if (get_user(val, (int *)arg))
@@ -1720,6 +1721,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
        /* 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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
        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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                            (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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
                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 @@ static int ymf_ioctl(struct inode *inode, struct file *file,
        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 @@ static int ymf_open(struct inode *inode, struct file *file)
                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 @@ static int ymf_open(struct inode *inode, struct file *file)
            (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 @@ static int ymf_release(struct inode *inode, struct file *file)
        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 @@ static int ymf_release(struct inode *inode, struct file *file)
         * Deallocate when unloading the driver and we can wait.
         */
        ymf_wait_dac(state);
-       dealloc_dmabuf(state);
-       ymf_pcm_free_substream(&state->ypcm);
-
-       codec->states[state->virt] = NULL;
+       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);
+
+       list_del(&state->chain);
+       file->private_data = NULL;      /* Can you tell I programmed Solaris */
        kfree(state);
 
        up(&codec->open_sem);
@@ -2045,6 +2061,83 @@ static /*const*/ struct file_operations ymf_mixer_fops = {
  *  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 @@ static int ymfpci_memalloc(ymfpci_t *codec)
        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 @@ static void ymfpci_memfree(ymfpci_t *codec)
        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 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
 
        /* 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 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
                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 @@ static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
        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 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
        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 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
        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 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
        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 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
        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 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
        ymfpci_writel(codec, YDSXGR_STATUS, ~0);
  out_unmap:
        iounmap(codec->reg_area_virt);
+ out_free:
        kfree(codec);
        return -ENODEV;
 }
@@ -2358,6 +2503,11 @@ static void __devexit ymf_remove_one(struct pci_dev *pcidev)
        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);
 }
 
index 6e1a8d5f396730001559b85752534da53b463ac8..bd2f4c2a309cade3a35e2b46e9811445d938916f 100644 (file)
@@ -21,6 +21,7 @@
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  */
+#include <linux/config.h>
 
 /*
  *  Direct registers
 #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 @@ typedef struct stru_ymfpci_playback_bank {
 } 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 @@ typedef struct stru_ymfpci_effect_bank {
        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 @@ typedef enum {
        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 @@ struct ymf_unit {
        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 ymf_unit {
        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 @@ struct ymf_dmabuf {
        /* redundant, but makes calculations easier */
        unsigned fragsize;
        unsigned dmasize;       /* Total rawbuf[] size */
-       unsigned fragsamples;
 
        /* OSS stuff */
        unsigned mapped:1;
@@ -329,15 +320,40 @@ struct ymf_pcm_format {
        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 */
index c03465c02846232438b10554180c78b1b3725f0b..502b490b8d7855e2bf109646ae440ddc0ef12c4a 100644 (file)
@@ -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>
  */
 static struct rpc_wait_queue    flushd_queue = RPC_INIT_WAITQ("nfs_flushd");
 
-/*
- * Spinlock
- */
-spinlock_t nfs_flushd_lock = SPIN_LOCK_UNLOCKED;
-
 /*
  * Local function declarations.
  */
@@ -68,17 +63,19 @@ int nfs_reqlist_init(struct nfs_server *server)
 {
        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 @@ int nfs_reqlist_init(struct nfs_server *server)
        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 @@ void nfs_reqlist_exit(struct nfs_server *server)
 {
        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 @@ static void inode_append_flushd(struct inode *inode)
        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 @@ static void inode_append_flushd(struct inode *inode)
        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 @@ void inode_remove_flushd(struct inode *inode)
        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 @@ void inode_remove_flushd(struct inode *inode)
        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 @@ void inode_schedule_scan(struct inode *inode, unsigned long time)
        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 @@ void inode_schedule_scan(struct inode *inode, unsigned long time)
                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 @@ nfs_flushd(struct rpc_task *task)
        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 @@ nfs_flushd(struct rpc_task *task)
        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 @@ nfs_flushd_exit(struct rpc_task *task)
        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);
 }
 
index 4a5b986076f07c64711132afd1df29c4299e7773..265c1b2dcf4111f18e7d5879f1ff1a89628b2027 100644 (file)
@@ -572,7 +572,6 @@ static inline long sync(void)
        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);
index 22d061d1a72fd69d4e380f7a125e60da19781c80..7fc8de5eb62445a41d42e397a21f6942231b214d 100644 (file)
@@ -945,7 +945,6 @@ void vmtruncate(struct inode * inode, loff_t offset)
        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 @@ void vmtruncate(struct inode * inode, loff_t offset)
 
 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;
index 6f8681d449828280f50f6fafb3f856e16dca9d50..00426ca27272e9ef11f0600d67334c722e390c3a 100644 (file)
@@ -217,8 +217,11 @@ static int shmem_writepage(struct page * page)
 
        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);
index a295ecaf7ce33ee75330e15f47092f47837051a7..a6af993c882d751820a50fbde463f247f1d50758 100644 (file)
@@ -267,22 +267,26 @@ rpcauth_holdcred(struct rpc_task *task)
        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 @@ rpcauth_invalcred(struct rpc_task *task)
 {
        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;
 }
index 928b3f857a4db4502e24a4c965daec0d5078081b..eceea5c957bfebd0044e3fe100b3492a553ddb0e 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
-#include <linux/config.h>
 
 extern int sysctl_unix_max_dgram_qlen;
 
index cdd3f3e53fa33306a9ffd74d3834724e1defd53f..adf757e56d48e3e7e43d099c7be6b799af599b45 100644 (file)
@@ -18,7 +18,6 @@
  *                                       negotiation.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
index 34e6c4190b35d86088c33e515e894dd4383c7ebc..73a567e89a50c86a5bf7daf72489db70e28e3482 100644 (file)
@@ -22,7 +22,6 @@
  *                                       i-frames.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
index 1401c90dee896386312e99dec2109b2e8c5ca02c..841499325f7e1072e98c7559b580ee2b2452e85d 100644 (file)
@@ -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>
index e7a429bf6b739d4df98bcd1aa716b2bf492134b1..2521078a6c9e01933c87ae71d0e8068569862a41 100644 (file)
@@ -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>
index d81f00ba85e07439bdb92c052cfef72a353d00be..635c872e557debd288ce62511529af1499bb90fe 100644 (file)
@@ -19,7 +19,6 @@
  *                                       negotiation.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
index c30ccb626fe6cba7e9041c24e9df838b97c90826..cf9b42606c2e8a2b4059871f54cfb184fc40d358 100644 (file)
@@ -18,7 +18,6 @@
  *                                     Centralised disconnection processing.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>