## Automatically generated incremental diff ## From: linux-2.5.75-bk1 ## To: linux-2.5.75-bk2 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.5.75-bk1/Documentation/filesystems/cifs.txt linux-2.5.75-bk2/Documentation/filesystems/cifs.txt --- linux-2.5.75-bk1/Documentation/filesystems/cifs.txt 2003-07-10 13:05:39.000000000 -0700 +++ linux-2.5.75-bk2/Documentation/filesystems/cifs.txt 2003-07-12 04:37:47.000000000 -0700 @@ -1,33 +1,51 @@ -This module, cifs, is a filesystem that implements the SMB/CIFS -protocol, which is the protocol used by Windows based operating systems -(including Windows 2000 and its successors) as well as Samba, OS/2 and -many others operating systems and network file server appliances. The -Cifs VFS filesystem module is designed to work well with servers that -implement the newer versions (dialects) of the SMB/CIFS protocol such as -Samba, the program written by Andrew Tridgell that turns any Unix host -into a file server for DOS or Windows clients, as well as Windows NT, -Windows 2000 and its successors. It is not designed to handle older smb -servers well, those that implement older versions of the dialect (such -as OS/2 or Windows 95), for this purpose use smbfs. - -This module can support mounting without a mount helper program. The -mount syntax is: - mount //server_ip/share_name /mnt -o user=username,password=your_password - -where "username", "your_password" and "server_ip" and "share_name" -should be replaced with specific values (supplied by the user) e.g. - mount //9.53.216.16/public /mnt -o user=jsmith,password=openup - -This cifs implementation is designed to handle network caching (safely) -as well as to implement locking, large file (64 bit access), distributed -file system ("dfs") and other advanced protocol features. It also -implements the SNIA standard for Unix extensions to CIFS (when -communicating with servers such as Samba 2.2.3 or later which support it). - -For more information contact sfrench@us.ibm.com - -Cifs is an SMB client (or gateway). For more info on the SMB/CIFS -protocol and Samba, including documentation, please go to -http://www.samba.org/ and then on to your nearest mirror. For more -information about the cifs vfs, go to the project page at: - http://us1.samba.org/samba/Linux_CIFS_client.html + This is the client VFS module for the Common Internet File System + (CIFS) protocol which is the successor to the Server Message Block + (SMB) protocol, the native file sharing mechanism for most early + PC operating systems. CIFS is fully supported by current network + file servers such as Windows 2000, Windows 2003 (including + Windows XP) as well by Samba (which provides excellent CIFS + server support for Linux and many other operating systems), so + this network filesystem client can mount to a wide variety of + servers. The smbfs module should be used instead of this cifs module + for mounting to older SMB servers such as OS/2. The smbfs and cifs + modules can coexist and do not conflict. The CIFS VFS filesystem + module is designed to work well with servers that implement the + newer versions (dialects) of the SMB/CIFS protocol such as Samba, + the program written by Andrew Tridgell that turns any Unix host + into a SMB/CIFS file server. + + The intent of this module is to provide the most advanced network + file system function for CIFS compliant servers, including better + POSIX compliance, secure per-user session establishment, high + performance safe distributed caching (oplock), optional packet + signing, large files, Unicode support and other internationalization + improvements. Since both Samba server and this filesystem client support + the CIFS Unix extensions, the combination can provide a reasonable + alternative to NFSv4 for fileserving in some Linux to Linux environments, + not just in Linux to Windows environments. + + This filesystem has an optional mount utility (mount.cifs) that can + be obtained from the project page and installed in the path in the same + directory with the other mount helpers (such as mount.smbfs). + Mounting using the cifs filesystem without installing the mount helper + requires specifying the server's ip address. + + For Linux 2.4: + mount //anything/here /mnt_target -o + user=username,pass=password,unc=//ip_address_of_server/sharename + + For Linux 2.5: + mount //ip_address_of_server/sharename /mnt_target -o user=username, pass=password + + + For more information on the module see the project page at + + http://us1.samba.org/samba/Linux_CIFS_client.html + + For more information on CIFS see: + + http://www.snia.org/tech_activities/CIFS + + or the Samba site: + + http://www.samba.org diff -urN linux-2.5.75-bk1/Makefile linux-2.5.75-bk2/Makefile --- linux-2.5.75-bk1/Makefile 2003-07-12 04:37:41.000000000 -0700 +++ linux-2.5.75-bk2/Makefile 2003-07-12 04:37:47.000000000 -0700 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 75 -EXTRAVERSION = -bk1 +EXTRAVERSION = -bk2 # *DOCUMENTATION* # To see a list of typical targets execute "make help" diff -urN linux-2.5.75-bk1/arch/i386/kernel/microcode.c linux-2.5.75-bk2/arch/i386/kernel/microcode.c --- linux-2.5.75-bk1/arch/i386/kernel/microcode.c 2003-07-10 13:06:04.000000000 -0700 +++ linux-2.5.75-bk2/arch/i386/kernel/microcode.c 2003-07-12 04:37:47.000000000 -0700 @@ -380,7 +380,6 @@ return -ENODATA; default: - printk(KERN_ERR "microcode: unknown ioctl cmd=%d\n", cmd); return -EINVAL; } return -EINVAL; diff -urN linux-2.5.75-bk1/drivers/block/floppy98.c linux-2.5.75-bk2/drivers/block/floppy98.c --- linux-2.5.75-bk1/drivers/block/floppy98.c 2003-07-10 13:08:10.000000000 -0700 +++ linux-2.5.75-bk2/drivers/block/floppy98.c 2003-07-12 04:37:47.000000000 -0700 @@ -4064,21 +4064,32 @@ static char *table[] = {"", #if 0 -"d360", + "d360", #else -"h1232", + "h1232", #endif -"h1200", "u360", "u720", "h360", "h720", -"u1440", "u2880", "CompaQ", "h1440", "u1680", "h410", -"u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743", -"h880", "u1040", "u1120", "h1600", "u1760", "u1920", -"u3200", "u3520", "u3840", "u1840", "u800", "u1600", + "h1200", "u360", "u720", "h360", "h720", + "u1440", "u2880", "CompaQ", "h1440", "u1680", "h410", + "u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743", + "h880", "u1040", "u1120", "h1600", "u1760", "u1920", + "u3200", "u3520", "u3840", "u1840", "u800", "u1600", NULL }; -static int t360[] = {1,0}, t1200[] = {2,5,6,10,12,14,16,18,20,23,0}, -t3in[] = {8,9,26,27,28, 7,11,15,19,24,25,29,31, 3,4,13,17,21,22,30,0}; -static int *table_sup[] = -{NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in}; + +static int t360[] = { + 1,0 +}; +static int t1200[] = { + 2,5,6,10,12,14,16,18,20,23,0 +}; +static int t3in[] = { + 8, 9,26,27,28, 7,11,15,19,24,25, + 29,31, 3, 4,13,17,21,22,30, 0 +}; + +static int *table_sup[] = { + NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in +}; static void __init register_devfs_entries (int drive) { @@ -4402,7 +4413,7 @@ return 0; out1: - del_timer(&fd_timeout); + del_timer_sync(&fd_timeout); out2: blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); unregister_blkdev(FLOPPY_MAJOR,"fd"); @@ -4430,11 +4441,9 @@ return 0; } spin_unlock_irqrestore(&floppy_usage_lock, flags); - MOD_INC_USE_COUNT; if (fd_request_irq()) { DPRINT("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); - MOD_DEC_USE_COUNT; spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4444,7 +4453,6 @@ DPRINT("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA); fd_free_irq(); - MOD_DEC_USE_COUNT; spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4521,7 +4529,6 @@ release_region(0x04be, 1); } } - MOD_DEC_USE_COUNT; spin_lock_irqsave(&floppy_usage_lock, flags); usage_count--; spin_unlock_irqrestore(&floppy_usage_lock, flags); @@ -4587,7 +4594,6 @@ } } fdc = old_fdc; - MOD_DEC_USE_COUNT; } diff -urN linux-2.5.75-bk1/drivers/char/dtlk.c linux-2.5.75-bk2/drivers/char/dtlk.c --- linux-2.5.75-bk1/drivers/char/dtlk.c 2003-07-10 13:08:25.000000000 -0700 +++ linux-2.5.75-bk2/drivers/char/dtlk.c 2003-07-12 04:37:47.000000000 -0700 @@ -55,7 +55,7 @@ #include #include /* for verify_area */ #include /* for -EBUSY */ -#include /* for check_region, request_region */ +#include /* for request_region */ #include /* for loops_per_jiffy */ #include /* for inb_p, outb_p, inb, outb, etc. */ #include /* for get_user, etc. */ diff -urN linux-2.5.75-bk1/drivers/char/genrtc.c linux-2.5.75-bk2/drivers/char/genrtc.c --- linux-2.5.75-bk1/drivers/char/genrtc.c 2003-07-10 13:14:22.000000000 -0700 +++ linux-2.5.75-bk2/drivers/char/genrtc.c 2003-07-12 04:37:47.000000000 -0700 @@ -355,8 +355,6 @@ if (gen_rtc_status & RTC_IS_OPEN) return -EBUSY; - MOD_INC_USE_COUNT; - gen_rtc_status |= RTC_IS_OPEN; gen_rtc_irq_data = 0; irq_active = 0; @@ -374,8 +372,6 @@ gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE); gen_rtc_status &= ~RTC_IS_OPEN; - MOD_DEC_USE_COUNT; - return 0; } diff -urN linux-2.5.75-bk1/drivers/isdn/hisax/isurf.c linux-2.5.75-bk2/drivers/isdn/hisax/isurf.c --- linux-2.5.75-bk1/drivers/isdn/hisax/isurf.c 2003-07-10 13:10:25.000000000 -0700 +++ linux-2.5.75-bk2/drivers/isdn/hisax/isurf.c 2003-07-12 04:37:47.000000000 -0700 @@ -237,7 +237,7 @@ struct pnp_card *pb; struct pnp_dev *pd; - cs->subtyp = 0; + card->cs->subtyp = 0; if ((pb = pnp_find_card( ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0010), pnp_surf))) { diff -urN linux-2.5.75-bk1/drivers/net/pcmcia/axnet_cs.c linux-2.5.75-bk2/drivers/net/pcmcia/axnet_cs.c --- linux-2.5.75-bk1/drivers/net/pcmcia/axnet_cs.c 2003-07-10 13:10:53.000000000 -0700 +++ linux-2.5.75-bk2/drivers/net/pcmcia/axnet_cs.c 2003-07-12 04:37:47.000000000 -0700 @@ -258,7 +258,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { axnet_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -706,7 +706,7 @@ link->open--; netif_stop_queue(dev); - del_timer(&info->watchdog); + del_timer_sync(&info->watchdog); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); diff -urN linux-2.5.75-bk1/drivers/net/pcmcia/ibmtr_cs.c linux-2.5.75-bk2/drivers/net/pcmcia/ibmtr_cs.c --- linux-2.5.75-bk1/drivers/net/pcmcia/ibmtr_cs.c 2003-07-10 13:10:16.000000000 -0700 +++ linux-2.5.75-bk2/drivers/net/pcmcia/ibmtr_cs.c 2003-07-12 04:37:47.000000000 -0700 @@ -293,9 +293,9 @@ dev = info->dev; { struct tok_info *ti = (struct tok_info *)dev->priv; - del_timer(&(ti->tr_timer)); + del_timer_sync(&(ti->tr_timer)); } - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { ibmtr_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { diff -urN linux-2.5.75-bk1/drivers/net/pcmcia/nmclan_cs.c linux-2.5.75-bk2/drivers/net/pcmcia/nmclan_cs.c --- linux-2.5.75-bk1/drivers/net/pcmcia/nmclan_cs.c 2003-07-10 13:04:57.000000000 -0700 +++ linux-2.5.75-bk2/drivers/net/pcmcia/nmclan_cs.c 2003-07-12 04:37:47.000000000 -0700 @@ -8,6 +8,7 @@ Written by Roger C. Pao Copyright 1995 Roger C. Pao + Linux 2.5 cleanups Copyright Red Hat 2003 This software may be used and distributed according to the terms of the GNU General Public License. @@ -68,6 +69,10 @@ History ------------------------------------------------------------------------------- Log: nmclan_cs.c,v + * 2.5.75-ac1 2003/07/11 Alan Cox + * Fixed hang on card eject as we probe it + * Cleaned up to use new style locking. + * * Revision 0.16 1995/07/01 06:42:17 rpao * Bug fix: nmclan_reset() called CardServices incorrectly. * @@ -369,6 +374,8 @@ char tx_free_frames; /* Number of free transmit frame buffers */ char tx_irq_disabled; /* MACE TX interrupt disabled */ + + spinlock_t bank_lock; /* Must be held if you step off bank 0 */ } mace_private; /* ---------------------------------------------------------------------------- @@ -482,6 +489,7 @@ link = &lp->link; link->priv = dev; + spin_lock_init(&lp->bank_lock); init_timer(&link->release); link->release.function = &nmclan_release; link->release.data = (u_long)link; @@ -561,7 +569,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { nmclan_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -588,7 +596,7 @@ assuming that during normal operation, the MACE is always in bank 0. ---------------------------------------------------------------------------- */ -static int mace_read(ioaddr_t ioaddr, int reg) +static int mace_read(mace_private *lp, ioaddr_t ioaddr, int reg) { int data = 0xFF; unsigned long flags; @@ -598,12 +606,11 @@ data = inb(ioaddr + AM2150_MACE_BASE + reg); break; case 1: /* register 16-31 */ - save_flags(flags); - cli(); + spin_lock_irqsave(&lp->bank_lock, flags); MACEBANK(1); data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); MACEBANK(0); - restore_flags(flags); + spin_unlock_irqrestore(&lp->bank_lock, flags); break; } return (data & 0xFF); @@ -616,7 +623,7 @@ are assuming that during normal operation, the MACE is always in bank 0. ---------------------------------------------------------------------------- */ -static void mace_write(ioaddr_t ioaddr, int reg, int data) +static void mace_write(mace_private *lp, ioaddr_t ioaddr, int reg, int data) { unsigned long flags; @@ -625,12 +632,11 @@ outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); break; case 1: /* register 16-31 */ - save_flags(flags); - cli(); + spin_lock_irqsave(&lp->bank_lock, flags); MACEBANK(1); outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); MACEBANK(0); - restore_flags(flags); + spin_unlock_irqrestore(&lp->bank_lock, flags); break; } } /* mace_write */ @@ -639,22 +645,29 @@ mace_init Resets the MACE chip. ---------------------------------------------------------------------------- */ -static void mace_init(ioaddr_t ioaddr, char *enet_addr) +static int mace_init(mace_private *lp, ioaddr_t ioaddr, char *enet_addr) { int i; + int ct = 0; /* MACE Software reset */ - mace_write(ioaddr, MACE_BIUCC, 1); - while (mace_read(ioaddr, MACE_BIUCC) & 0x01) { + mace_write(lp, ioaddr, MACE_BIUCC, 1); + while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) { /* Wait for reset bit to be cleared automatically after <= 200ns */; + if(++ct > 500) + { + printk(KERN_ERR "mace: reset failed, card removed ?\n"); + return -1; + } + udelay(1); } - mace_write(ioaddr, MACE_BIUCC, 0); + mace_write(lp, ioaddr, MACE_BIUCC, 0); /* The Am2150 requires that the MACE FIFOs operate in burst mode. */ - mace_write(ioaddr, MACE_FIFOCC, 0x0F); + mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F); - mace_write(ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ - mace_write(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ + mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ + mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ /* * Bit 2-1 PORTSEL[1-0] Port Select. @@ -670,31 +683,39 @@ */ switch (if_port) { case 1: - mace_write(ioaddr, MACE_PLSCC, 0x02); + mace_write(lp, ioaddr, MACE_PLSCC, 0x02); break; case 2: - mace_write(ioaddr, MACE_PLSCC, 0x00); + mace_write(lp, ioaddr, MACE_PLSCC, 0x00); break; default: - mace_write(ioaddr, MACE_PHYCC, /* ASEL */ 4); + mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4); /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, and the MACE device will automatically select the operating media interface port. */ break; } - mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); + mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); /* Poll ADDRCHG bit */ - while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) - ; + ct = 0; + while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + { + if(++ ct > 500) + { + printk(KERN_ERR "mace: ADDRCHG timeout, card removed ?\n"); + return -1; + } + } /* Set PADR register */ for (i = 0; i < ETHER_ADDR_LEN; i++) - mace_write(ioaddr, MACE_PADR, enet_addr[i]); + mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]); /* MAC Configuration Control Register should be written last */ /* Let set_multicast_list set this. */ - /* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ - mace_write(ioaddr, MACE_MACCC, 0x00); + /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ + mace_write(lp, ioaddr, MACE_MACCC, 0x00); + return 0; } /* mace_init */ /* ---------------------------------------------------------------------------- @@ -759,8 +780,8 @@ { char sig[2]; - sig[0] = mace_read(ioaddr, MACE_CHIPIDL); - sig[1] = mace_read(ioaddr, MACE_CHIPIDH); + sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL); + sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH); if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) { DEBUG(0, "nmclan_cs configured: mace id=%x %x\n", sig[0], sig[1]); @@ -772,7 +793,8 @@ } } - mace_init(ioaddr, dev->dev_addr); + if(mace_init(lp, ioaddr, dev->dev_addr) == -1) + goto failed; /* The if_port symbol can be set when the module is loaded */ if (if_port <= 2) @@ -923,8 +945,8 @@ lp->tx_free_frames=AM2150_MAX_TX_FRAMES; /* Reinitialize the MACE chip for operation. */ - mace_init(dev->base_addr, dev->dev_addr); - mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); + mace_init(lp, dev->base_addr, dev->dev_addr); + mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); /* Restore the multicast list and enable TX and RX. */ restore_multicast_list(dev); @@ -1456,9 +1478,9 @@ { mace_private *lp = (mace_private *)dev->priv; - lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC); - lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC); - lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC); + lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC); + lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC); + lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC); /* At this point, mace_stats is fully updated for this call. We may now update the linux_stats. */ @@ -1608,30 +1630,30 @@ DEBUG(1, "Attempt to restore multicast list detected.\n"); - mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); + mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); /* Poll ADDRCHG bit */ - while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) ; /* Set LADRF register */ for (i = 0; i < MACE_LADRF_LEN; i++) - mace_write(ioaddr, MACE_LADRF, ladrf[i]); + mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]); - mace_write(ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } else if (num_addrs < 0) { /* Promiscuous mode: receive all packets */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV ); } else { /* Normal mode */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } } /* restore_multicast_list */ @@ -1691,20 +1713,21 @@ static void restore_multicast_list(struct net_device *dev) { ioaddr_t ioaddr = dev->base_addr; + mace_private *lp = (mace_private *)dev->priv; DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name, ((mace_private *)(dev->priv))->multicast_num_addrs); if (dev->flags & IFF_PROMISC) { /* Promiscuous mode: receive all packets */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, + mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV ); } else { /* Normal mode */ - mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } } /* restore_multicast_list */ diff -urN linux-2.5.75-bk1/drivers/net/pcmcia/pcnet_cs.c linux-2.5.75-bk2/drivers/net/pcmcia/pcnet_cs.c --- linux-2.5.75-bk1/drivers/net/pcmcia/pcnet_cs.c 2003-07-10 13:13:34.000000000 -0700 +++ linux-2.5.75-bk2/drivers/net/pcmcia/pcnet_cs.c 2003-07-12 04:37:47.000000000 -0700 @@ -357,7 +357,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { pcnet_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -1052,7 +1052,7 @@ link->open--; netif_stop_queue(dev); - del_timer(&info->watchdog); + del_timer_sync(&info->watchdog); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); diff -urN linux-2.5.75-bk1/drivers/net/pcmcia/smc91c92_cs.c linux-2.5.75-bk2/drivers/net/pcmcia/smc91c92_cs.c --- linux-2.5.75-bk1/drivers/net/pcmcia/smc91c92_cs.c 2003-07-12 04:37:42.000000000 -0700 +++ linux-2.5.75-bk2/drivers/net/pcmcia/smc91c92_cs.c 2003-07-12 04:37:47.000000000 -0700 @@ -433,7 +433,7 @@ if (*linkp == NULL) return; - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { smc91c92_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -1330,7 +1330,7 @@ outw(CTL_POWERDOWN, ioaddr + CONTROL ); link->open--; - del_timer(&smc->media); + del_timer_sync(&smc->media); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); diff -urN linux-2.5.75-bk1/drivers/net/pcmcia/xirc2ps_cs.c linux-2.5.75-bk2/drivers/net/pcmcia/xirc2ps_cs.c --- linux-2.5.75-bk1/drivers/net/pcmcia/xirc2ps_cs.c 2003-07-10 13:08:16.000000000 -0700 +++ linux-2.5.75-bk2/drivers/net/pcmcia/xirc2ps_cs.c 2003-07-12 04:37:47.000000000 -0700 @@ -689,7 +689,7 @@ * the release() function is called, that will trigger a proper * detach(). */ - del_timer(&link->release); + del_timer_sync(&link->release); if (link->state & DEV_CONFIG) { xirc2ps_release((unsigned long)link); if (link->state & DEV_STALE_CONFIG) { diff -urN linux-2.5.75-bk1/drivers/scsi/g_NCR5380.c linux-2.5.75-bk2/drivers/scsi/g_NCR5380.c --- linux-2.5.75-bk1/drivers/scsi/g_NCR5380.c 2003-07-10 13:05:35.000000000 -0700 +++ linux-2.5.75-bk2/drivers/scsi/g_NCR5380.c 2003-07-12 04:37:47.000000000 -0700 @@ -386,14 +386,21 @@ if (overrides[current_override].NCR5380_map_name != PORT_AUTO) for (i = 0; ports[i]; i++) { + if (!request_region(ports[i], 16, "ncr53c80")) + continue; if (overrides[current_override].NCR5380_map_name == ports[i]) break; + release_region(ports[i], 16); } else for (i = 0; ports[i]; i++) { - if ((!check_region(ports[i], 16)) && (inb(ports[i]) == 0xff)) + if (!request_region(ports[i], 16, "ncr53c80")) + continue; + if (inb(ports[i]) == 0xff) break; + release_region(ports[i], 16); } if (ports[i]) { + /* At this point we have our region reserved */ outb(0x59, 0x779); outb(0xb9, 0x379); outb(0xc5, 0x379); @@ -408,12 +415,15 @@ } else continue; } - - request_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380"); + else + { + /* Not a 53C400A style setup - just grab */ + if(!(request_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380"))) + continue; + } #else - if (check_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size)) + if(!request_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380")) continue; - request_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380"); #endif instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata)); if (instance == NULL) { diff -urN linux-2.5.75-bk1/drivers/video/sis/init301.c linux-2.5.75-bk2/drivers/video/sis/init301.c --- linux-2.5.75-bk1/drivers/video/sis/init301.c 2003-07-10 13:04:46.000000000 -0700 +++ linux-2.5.75-bk2/drivers/video/sis/init301.c 2003-07-12 04:37:47.000000000 -0700 @@ -5282,7 +5282,7 @@ #ifdef SIS315H /* 310/325 series */ if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { - temp = temp = SiS_GetCH701x(SiS_Pr,0x61); + temp = SiS_GetCH701x(SiS_Pr,0x61); if(temp < 1) { SiS_SetCH701x(SiS_Pr,0xac76); SiS_SetCH701x(SiS_Pr,0x0066); diff -urN linux-2.5.75-bk1/fs/cifs/CHANGES linux-2.5.75-bk2/fs/cifs/CHANGES --- linux-2.5.75-bk1/fs/cifs/CHANGES 2003-07-10 13:14:10.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/CHANGES 2003-07-12 04:37:47.000000000 -0700 @@ -1,3 +1,9 @@ +Version 0.82 +------------ +Add support for mknod of block or character devices. Fix oplock +code (distributed caching) to properly send response to oplock +break from server. + Version 0.81 ------------ Finish up CIFS packet digital signing for the default diff -urN linux-2.5.75-bk1/fs/cifs/README linux-2.5.75-bk2/fs/cifs/README --- linux-2.5.75-bk1/fs/cifs/README 2003-07-10 13:14:50.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/README 2003-07-12 04:37:47.000000000 -0700 @@ -76,8 +76,9 @@ case sensitive = yes delete readonly = yes Some administrators also change the "map archive" and the "create mask" parameters -from their defaults. For more information on these see the manual pages -("man smb.conf") on the Samba server system. Note that the cifs vfs, unlike the +from their default values. Creating special devices (mknod) remotely may require +specifying a mkdev function to Samba. For more information on these see the manual +pages ("man smb.conf") on the Samba server system. Note that the cifs vfs, unlike the smbfs vfs, does not read the smb.conf on the client system (the few optional settings are passed in on mount via -o parameters instead). Note that Samba 2.2.7 or later includes a fix that allows the CIFS VFS to delete open files (required for strict diff -urN linux-2.5.75-bk1/fs/cifs/cifs_debug.c linux-2.5.75-bk2/fs/cifs/cifs_debug.c --- linux-2.5.75-bk1/fs/cifs/cifs_debug.c 2003-07-10 13:09:00.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/cifs_debug.c 2003-07-12 04:37:47.000000000 -0700 @@ -89,8 +89,8 @@ ses->serverOS, ses->serverNOS, ses->capabilities); buf += length; if(ses->server) - buf += sprintf(buf, "\tLocal Users To Same Server: %d ", - atomic_read(&ses->server->socketUseCount)); + buf += sprintf(buf, "\tLocal Users To Same Server: %d SecMode: 0x%x", + atomic_read(&ses->server->socketUseCount),ses->server->secMode); } read_unlock(&GlobalSMBSeslock); sprintf(buf, "\n"); @@ -590,6 +590,8 @@ sign_CIFS_PDUs = 0; else if (c == '1' || c == 'y' || c == 'Y') sign_CIFS_PDUs = 1; + else if (c == '2') + sign_CIFS_PDUs = 2; return count; } diff -urN linux-2.5.75-bk1/fs/cifs/cifsencrypt.c linux-2.5.75-bk2/fs/cifs/cifsencrypt.c --- linux-2.5.75-bk1/fs/cifs/cifsencrypt.c 2003-07-10 13:11:36.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/cifsencrypt.c 2003-07-12 04:37:47.000000000 -0700 @@ -24,6 +24,7 @@ #include "cifsglob.h" #include "cifs_debug.h" #include "md5.h" +#include "cifs_unicode.h" /* Calculate and return the CIFS signature based on the mac key and the smb pdu */ /* the 16 byte signature must be allocated by the caller */ @@ -133,8 +134,68 @@ if ((key == NULL) || (rn == NULL) || (password == NULL)) return -EINVAL; - E_md4hash(password, temp_key); /* BB may have to do another md4 of it */ + E_md4hash(password, temp_key); mdfour(key,temp_key,16); memcpy(key+16,rn, CIFS_SESSION_KEY_SIZE); return 0; -} +} + +int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, struct nls_table * nls_info) +{ + char temp_hash[16]; + struct HMACMD5Context ctx; + char * ucase_buf; + wchar_t * unicode_buf; + unsigned int i,user_name_len,dom_name_len; + + if(ses) + return -EINVAL; + + E_md4hash(ses->password_with_pad, temp_hash); + + hmac_md5_init_limK_to_64(temp_hash, 16, &ctx); + user_name_len = strlen(ses->userName); + if(user_name_len > MAX_USERNAME_SIZE) + return -EINVAL; + dom_name_len = strlen(ses->domainName); + if(dom_name_len > MAX_USERNAME_SIZE) + return -EINVAL; + + + ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL); + unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL); + + for(i=0;icharset2upper[(int)ses->userName[i]]; + ucase_buf[i] = 0; + user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); + unicode_buf[user_name_len] = 0; + user_name_len++; + + for(i=0;icharset2upper[(int)ses->domainName[i]]; + ucase_buf[i] = 0; + dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); + + unicode_buf[user_name_len + dom_name_len] = 0; + hmac_md5_update((const unsigned char *) unicode_buf, + (user_name_len+dom_name_len)*2,&ctx); + + hmac_md5_final(ses->mac_signing_key,&ctx); + kfree(ucase_buf); + kfree(unicode_buf); + return 0; +} +void CalcNTLMv2_response(const struct cifsSesInfo * ses,char * v2_session_response) +{ + struct HMACMD5Context context; + memcpy(v2_session_response + 8, ses->server->cryptKey,8); + /* gen_blob(v2_session_response + 16); */ + hmac_md5_init_limK_to_64(ses->mac_signing_key, 16, &context); + + hmac_md5_update(ses->server->cryptKey,8,&context); +/* hmac_md5_update(v2_session_response+16)client thing,8,&context); */ /* BB fix */ + + + hmac_md5_final(v2_session_response,&context); +} diff -urN linux-2.5.75-bk1/fs/cifs/cifsfs.c linux-2.5.75-bk2/fs/cifs/cifsfs.c --- linux-2.5.75-bk1/fs/cifs/cifsfs.c 2003-07-10 13:10:50.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/cifsfs.c 2003-07-12 04:37:47.000000000 -0700 @@ -301,6 +301,7 @@ /* revalidate:cifs_revalidate, */ .setattr = cifs_setattr, .symlink = cifs_symlink, + .mknod = cifs_mknod, }; struct inode_operations cifs_file_inode_ops = { @@ -465,7 +466,14 @@ CIFS_I(pfile->f_dentry->d_inode)->write_behind_rc = rc; cFYI(1,("Oplock flush file %p rc %d",pfile,rc)); - /* send oplock break */ + if(pfile->private_data) { + rc = CIFSSMBLock(0, pTcon, + ((struct cifsFileInfo *) pfile->private_data)->netfid, + 0 /* len */ , 0 /* offset */, 0, + 0, LOCKING_ANDX_OPLOCK_RELEASE, + 0 /* wait flag */); + cFYI(1,("Oplock release rc = %d ",rc)); + } write_lock(&GlobalMid_Lock); } else break; diff -urN linux-2.5.75-bk1/fs/cifs/cifsfs.h linux-2.5.75-bk2/fs/cifs/cifsfs.h --- linux-2.5.75-bk1/fs/cifs/cifsfs.h 2003-07-10 13:05:38.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/cifsfs.h 2003-07-12 04:37:47.000000000 -0700 @@ -50,6 +50,7 @@ extern struct dentry *cifs_lookup(struct inode *, struct dentry *, struct nameidata *); extern int cifs_unlink(struct inode *, struct dentry *); extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); +extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t); extern int cifs_mkdir(struct inode *, struct dentry *, int); extern int cifs_rmdir(struct inode *, struct dentry *); extern int cifs_rename(struct inode *, struct dentry *, struct inode *, diff -urN linux-2.5.75-bk1/fs/cifs/cifsproto.h linux-2.5.75-bk2/fs/cifs/cifsproto.h --- linux-2.5.75-bk1/fs/cifs/cifsproto.h 2003-07-10 13:04:37.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/cifsproto.h 2003-07-12 04:37:47.000000000 -0700 @@ -88,8 +88,7 @@ const struct nls_table *); extern int CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, char *ntlm_session_key, - char *lanman_session_key,int ntlmv2_flag, - const struct nls_table *); + int ntlmv2_flag, const struct nls_table *); extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, const char *tree, struct cifsTconInfo *tcon, @@ -156,7 +155,7 @@ __u64 size, __u16 fileHandle,__u32 opener_pid, int AllocSizeFlag); extern int CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *pTcon, char *full_path, __u64 mode, __u64 uid, - __u64 gid, const struct nls_table *nls_codepage); + __u64 gid, dev_t dev, const struct nls_table *nls_codepage); extern int CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon, const char *newName, @@ -229,8 +228,8 @@ extern int cifs_verify_signature(const struct smb_hdr *, const char * mac_key, __u32 expected_sequence_number); extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass); - -/* BB routines below not implemented yet BB */ +extern void CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, struct nls_table *); +extern void CalcNTLMv2_response(const struct cifsSesInfo *,char * ); extern int CIFSBuildServerList(int xid, char *serverBufferList, int recordlength, int *entries, diff -urN linux-2.5.75-bk1/fs/cifs/cifssmb.c linux-2.5.75-bk2/fs/cifs/cifssmb.c --- linux-2.5.75-bk1/fs/cifs/cifssmb.c 2003-07-10 13:06:03.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/cifssmb.c 2003-07-12 04:37:47.000000000 -0700 @@ -618,6 +618,7 @@ LOCK_REQ *pSMB = NULL; LOCK_RSP *pSMBr = NULL; int bytes_returned; + int timeout = 0; cFYI(1, ("In CIFSSMBLock")); @@ -626,6 +627,9 @@ if (rc) return rc; + if(lockType == LOCKING_ANDX_OPLOCK_RELEASE) + timeout = -1; /* no response expected */ + pSMB->NumberOfLocks = cpu_to_le32(numLock); pSMB->NumberOfUnlocks = cpu_to_le32(numUnlock); pSMB->LockType = lockType; @@ -640,7 +644,7 @@ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + (struct smb_hdr *) pSMBr, &bytes_returned, timeout); if (rc) { cERROR(1, ("Send error in Lock = %d", rc)); @@ -2299,7 +2303,7 @@ int CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon, char *fileName, __u64 mode, __u64 uid, __u64 gid, - const struct nls_table *nls_codepage) + dev_t device, const struct nls_table *nls_codepage) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; @@ -2358,6 +2362,9 @@ pSMB->hdr.smb_buf_length += pSMB->ByteCount; data_offset->Uid = cpu_to_le64(uid); data_offset->Gid = cpu_to_le64(gid); + /* better to leave device as zero when it is */ + data_offset->DevMajor = cpu_to_le64(MAJOR(device)); + data_offset->DevMinor = cpu_to_le64(MINOR(device)); data_offset->Permissions = cpu_to_le64(mode); pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, diff -urN linux-2.5.75-bk1/fs/cifs/connect.c linux-2.5.75-bk2/fs/cifs/connect.c --- linux-2.5.75-bk1/fs/cifs/connect.c 2003-07-10 13:14:19.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/connect.c 2003-07-12 04:37:47.000000000 -0700 @@ -634,7 +634,6 @@ int setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, struct nls_table * nls_info) { int rc = 0; - char session_key[CIFS_SESSION_KEY_SIZE]; char ntlm_session_key[CIFS_SESSION_KEY_SIZE]; int ntlmv2_flag = FALSE; @@ -666,35 +665,44 @@ nls_info); if (!rc) { if(ntlmv2_flag) { - cFYI(1,("Able to use the more secure NTLM version 2 password hash")); - /* SMBNTv2encrypt( ...); */ /* BB fix this up - - and note that Samba client equivalent looks wrong */ - } else - SMBNTencrypt(pSesInfo->password_with_pad, - pSesInfo->server->cryptKey,ntlm_session_key); + char * v2_response; + cFYI(1,("Can use more secure NTLM version 2 password hash")); + CalcNTLMv2_partial_mac_key(pSesInfo, + nls_info); + v2_response = kmalloc(16 + 64 /* blob */, GFP_KERNEL); + if(v2_response) { + CalcNTLMv2_response(pSesInfo,v2_response); +/* cifs_calculate_ntlmv2_mac_key(pSesInfo->mac_signing_key, response, ntlm_session_key, */ + kfree(v2_response); + /* BB Put dummy sig in SessSetup PDU? */ + } else + rc = -ENOMEM; - /* BB add call to save MAC key here BB */ + } else { + SMBNTencrypt(pSesInfo->password_with_pad, + pSesInfo->server->cryptKey, + ntlm_session_key); - /* for better security the weaker lanman hash not sent - in AuthSessSetup so why bother calculating it */ - /* toUpper(nls_info, - password_with_pad); - SMBencrypt(password_with_pad, - pSesInfo->server->cryptKey, session_key); */ + cifs_calculate_mac_key(pSesInfo->mac_signing_key, + ntlm_session_key, + pSesInfo->password_with_pad); + } + /* for better security the weaker lanman hash not sent + in AuthSessSetup so we no longer calculate it */ rc = CIFSNTLMSSPAuthSessSetup(xid, - pSesInfo, - ntlm_session_key, - session_key, - ntlmv2_flag, - nls_info); + pSesInfo, + ntlm_session_key, + ntlmv2_flag, + nls_info); } } else { /* old style NTLM 0.12 session setup */ SMBNTencrypt(pSesInfo->password_with_pad, pSesInfo->server->cryptKey, ntlm_session_key); - cifs_calculate_mac_key(pSesInfo->mac_signing_key, ntlm_session_key, pSesInfo->password_with_pad); + cifs_calculate_mac_key(pSesInfo->mac_signing_key, + ntlm_session_key, pSesInfo->password_with_pad); rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info); } @@ -1297,7 +1305,7 @@ int bytes_returned = 0; int len; - cFYI(1, ("In v2 sesssetup ")); + cFYI(1, ("In spnego sesssetup ")); smb_buffer = buf_get(); if (smb_buffer == 0) { @@ -1605,7 +1613,9 @@ SecurityBlob->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | 0x80000000 | - NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_128; + /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128; + if(sign_CIFS_PDUs) + SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN; if(ntlmv2_support) SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; /* setup pointers to domain name and workstation name */ @@ -1725,6 +1735,17 @@ CIFS_CRYPTO_KEY_SIZE); if(SecurityBlob2->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLMV2) *pNTLMv2_flag = TRUE; + + if((SecurityBlob2->NegotiateFlags & + NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + || (sign_CIFS_PDUs > 1)) + ses->server->secMode |= + SECMODE_SIGN_REQUIRED; + if ((SecurityBlob2->NegotiateFlags & + NTLMSSP_NEGOTIATE_SIGN) && (sign_CIFS_PDUs)) + ses->server->secMode |= + SECMODE_SIGN_ENABLED; + if (smb_buffer->Flags2 &= SMBFLG2_UNICODE) { if ((long) (bcc_ptr) % 2) { remaining_words = @@ -1868,7 +1889,7 @@ int CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, - char *ntlm_session_key, char *lanman_session_key, int ntlmv2_flag, + char *ntlm_session_key, int ntlmv2_flag, const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; @@ -1934,7 +1955,9 @@ SecurityBlob->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO | - 0x80000000 | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_128; + 0x80000000 | NTLMSSP_NEGOTIATE_128; + if(sign_CIFS_PDUs) + SecurityBlob->NegotiateFlags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN; if(ntlmv2_flag) SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; diff -urN linux-2.5.75-bk1/fs/cifs/dir.c linux-2.5.75-bk2/fs/cifs/dir.c --- linux-2.5.75-bk1/fs/cifs/dir.c 2003-07-10 13:12:08.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/dir.c 2003-07-12 04:37:47.000000000 -0700 @@ -23,6 +23,7 @@ #include #include #include +#include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" @@ -125,6 +126,7 @@ int rc = -ENOENT; int xid; int oplock = REQ_OPLOCK; + int desiredAccess = GENERIC_ALL; __u16 fileHandle; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; @@ -138,10 +140,23 @@ full_path = build_path_from_dentry(direntry); + + if(nd) { + cFYI(1,("In create nd flags = 0x%x for %s",nd->flags,full_path)); + cFYI(1,("Intent flags: 0x%x", nd->intent.open.flags)); + if ((nd->intent.open.flags & O_ACCMODE) == O_RDONLY) + desiredAccess = GENERIC_READ; + else if ((nd->intent.open.flags & O_ACCMODE) == O_WRONLY) + desiredAccess = GENERIC_WRITE; + else if ((nd->intent.open.flags & O_ACCMODE) == O_RDWR) + desiredAccess = GENERIC_ALL; + } + + /* BB add processing for setting the equivalent of mode - e.g. via CreateX with ACLs */ - rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OVERWRITE_IF, GENERIC_ALL - /* 0x20197 was used previously */ , CREATE_NOT_DIR, + rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OVERWRITE_IF, + desiredAccess, CREATE_NOT_DIR, &fileHandle, &oplock, cifs_sb->local_nls); if (rc) { cFYI(1, ("cifs_create returned 0x%x ", rc)); @@ -178,6 +193,43 @@ return rc; } +int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t device_number) +{ + int rc = -EPERM; + int xid; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + char *full_path = NULL; + struct inode * newinode = NULL; + + xid = GetXid(); + + cifs_sb = CIFS_SB(inode->i_sb); + pTcon = cifs_sb->tcon; + + full_path = build_path_from_dentry(direntry); + + if (pTcon->ses->capabilities & CAP_UNIX) { + rc = CIFSSMBUnixSetPerms(xid, pTcon, + full_path, mode, current->euid, current->egid, + device_number, cifs_sb->local_nls); + if(!rc) { + rc = cifs_get_inode_info_unix(&newinode, full_path, + inode->i_sb); + direntry->d_op = &cifs_dentry_ops; + if(rc == 0) + d_instantiate(direntry, newinode); + } + } + + if (full_path) + kfree(full_path); + FreeXid(xid); + + return rc; +} + + struct dentry * cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct nameidata *nd) { @@ -208,6 +260,13 @@ } cFYI(1, (" Full path: %s inode = 0x%p", full_path, direntry->d_inode)); + + if(nd) { /* BB remove begin */ + cFYI(1,("In lookup nd flags = 0x%x",nd->flags)); + cFYI(1,("Intent flags: 0x%x", nd->intent.open.flags)); + } +/* BB remove end BB */ + if (pTcon->ses->capabilities & CAP_UNIX) rc = cifs_get_inode_info_unix(&newInode, full_path, parent_dir_inode->i_sb); @@ -269,6 +328,12 @@ /* lock_kernel(); *//* surely we do not want to lock the kernel for a whole network round trip which could take seconds */ + if(nd) { /* BB remove begin */ + cFYI(1,("In d_revalidate nd flags = 0x%x",nd->flags)); + cFYI(1,("Intent flags: 0x%x", nd->intent.open.flags)); + } +/* BB remove end BB */ + if (direntry->d_inode) { if (cifs_revalidate(direntry)) { /* unlock_kernel(); */ diff -urN linux-2.5.75-bk1/fs/cifs/file.c linux-2.5.75-bk2/fs/cifs/file.c --- linux-2.5.75-bk1/fs/cifs/file.c 2003-07-10 13:03:37.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/file.c 2003-07-12 04:37:47.000000000 -0700 @@ -52,12 +52,12 @@ xid = GetXid(); - cFYI(1, (" inode = 0x%p file flags are %x", inode, file->f_flags)); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(file->f_dentry); + cFYI(1, (" inode = 0x%p file flags are %x for %s", inode, file->f_flags,full_path)); if ((file->f_flags & O_ACCMODE) == O_RDONLY) desiredAccess = GENERIC_READ; else if ((file->f_flags & O_ACCMODE) == O_WRONLY) @@ -125,6 +125,7 @@ CIFSSMBUnixSetPerms(xid, pTcon, full_path, inode->i_mode, (__u64)-1, (__u64)-1, + 0 /* dev */, cifs_sb->local_nls); else {/* BB implement via Windows security descriptors */ /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ diff -urN linux-2.5.75-bk1/fs/cifs/inode.c linux-2.5.75-bk2/fs/cifs/inode.c --- linux-2.5.75-bk1/fs/cifs/inode.c 2003-07-10 13:04:05.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/inode.c 2003-07-12 04:37:47.000000000 -0700 @@ -397,6 +397,7 @@ CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, (__u64)-1, (__u64)-1, + 0 /* dev_t */, cifs_sb->local_nls); else { /* BB to be implemented via Windows secrty descriptors*/ /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ @@ -702,7 +703,7 @@ if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX) && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID))) rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid, - cifs_sb->local_nls); + 0 /* dev_t */, cifs_sb->local_nls); else if (attrs->ia_valid & ATTR_MODE) { if((mode & S_IWUGO) == 0) /* not writeable */ { if((cifsInode->cifsAttrs & ATTR_READONLY) == 0) diff -urN linux-2.5.75-bk1/fs/cifs/misc.c linux-2.5.75-bk2/fs/cifs/misc.c --- linux-2.5.75-bk1/fs/cifs/misc.c 2003-07-10 13:14:55.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/misc.c 2003-07-12 04:37:47.000000000 -0700 @@ -338,13 +338,8 @@ netfile = list_entry(tmp1,struct cifsFileInfo,tlist); if(pSMB->Fid == netfile->netfid) { struct cifsInodeInfo *pCifsInode; - /* BB Add following logic: - 2) look up inode from tcon->openFileList->file->f_dentry->d_inode - 3) flush dirty pages and cached byte range locks and mark inode - 4) depending on break type change to r/o caching or no caching - cifsinode->clientCanCacheAll = 0 - 5) inode->i_data.a_ops = &cifs_addr_ops_writethrough; - 6) send oplock break response to server */ + /* BB Add following logic to mark inode for write through + inode->i_data.a_ops = &cifs_addr_ops_writethrough; */ read_unlock(&GlobalSMBSeslock); cFYI(1,("Matching file id, processing oplock break")); pCifsInode = @@ -354,7 +349,7 @@ pCifsInode->clientCanCacheRead = FALSE; pCifsInode->oplockPending = TRUE; AllocOplockQEntry(netfile->pfile, tcon); - cFYI(1,("about to wake up oplock thd")); + cFYI(1,("about to wake up oplock thd")); wake_up_process(oplockThread); return TRUE; } diff -urN linux-2.5.75-bk1/fs/cifs/smbencrypt.c linux-2.5.75-bk2/fs/cifs/smbencrypt.c --- linux-2.5.75-bk1/fs/cifs/smbencrypt.c 2003-07-10 13:09:01.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/smbencrypt.c 2003-07-12 04:37:47.000000000 -0700 @@ -470,58 +470,3 @@ return TRUE; } - - -/*********************************************************** - SMB signing - setup the MAC key. -************************************************************/ - -void -cli_calculate_mac_key(__u8 * mac_key, int *pmac_key_len, - const char *ntpasswd, const unsigned char resp[24]) -{ - /* Get first 16 bytes. */ - E_md4hash(ntpasswd, mac_key); - memcpy(mac_key + 16, resp, 24); - *pmac_key_len = 40; - - /* Reset the sequence number in case we had a previous (aborted) attempt */ -/* cli->sign_info.send_seq_num = 0; */ -} - -/*********************************************************** - SMB signing - calculate a MAC to send. -************************************************************/ - -void -cli_caclulate_sign_mac(struct smb_hdr *outbuf, __u8 * mac_key, - int mac_key_len, __u32 * send_seq_num, - __u32 * reply_seq_num) -{ - unsigned char calc_md5_mac[16]; - struct MD5Context md5_ctx; - -/* if (!cli->sign_info.use_smb_signing) { - return; - } */ - - /* - * Firstly put the sequence number into the first 4 bytes. - * and zero out the next 4 bytes. - */ -/* - SIVAL(outbuf, smb_ss_field, *send_seq_num); - SIVAL(outbuf, smb_ss_field + 4, 0); */ - - /* Calculate the 16 byte MAC and place first 8 bytes into the field. */ - MD5Init(&md5_ctx); - MD5Update(&md5_ctx, mac_key, mac_key_len); - MD5Update(&md5_ctx, outbuf->Protocol, - be32_to_cpu(outbuf->smb_buf_length)); - MD5Final(calc_md5_mac, &md5_ctx); - - memcpy(outbuf->Signature.SecuritySignature, calc_md5_mac, 8); - (*send_seq_num)++; - *reply_seq_num = *send_seq_num; - (*send_seq_num)++; -} diff -urN linux-2.5.75-bk1/fs/cifs/transport.c linux-2.5.75-bk2/fs/cifs/transport.c --- linux-2.5.75-bk1/fs/cifs/transport.c 2003-07-10 13:06:54.000000000 -0700 +++ linux-2.5.75-bk2/fs/cifs/transport.c 2003-07-12 04:37:47.000000000 -0700 @@ -199,13 +199,15 @@ if (in_buf->smb_buf_length > 12) in_buf->Flags2 = cpu_to_le16(in_buf->Flags2); - + rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->sockAddr)); + if (long_op == -1) + goto cifs_no_response_exit; if (long_op > 1) /* writes past end of file can take looooong time */ timeout = 300 * HZ; else if (long_op == 1) @@ -283,7 +285,7 @@ } else rc = -EIO; } - +cifs_no_response_exit: DeleteMidQEntry(midQ); /* BB what if process is killed? - BB add background daemon to clean up Mid entries from killed processes & test killing process with active mid */ diff -urN linux-2.5.75-bk1/include/linux/ac97_codec.h linux-2.5.75-bk2/include/linux/ac97_codec.h --- linux-2.5.75-bk1/include/linux/ac97_codec.h 2003-07-10 13:04:47.000000000 -0700 +++ linux-2.5.75-bk2/include/linux/ac97_codec.h 2003-07-12 04:37:47.000000000 -0700 @@ -214,6 +214,9 @@ (CODEC)->supported_mixers & (1<,,," to the kernel command line. +config SOUND_AD1889 + tristate "AD1889 based cards (AD1819 codec) (EXPERIMENTAL)" + depends on EXPERIMENTAL && SOUND_OSS + help + Say M here if you have a sound card based on the Analog Devices + AD1889 chip. + config SOUND_SGALAXY tristate "Aztech Sound Galaxy (non-PnP) cards" depends on SOUND_OSS diff -urN linux-2.5.75-bk1/sound/oss/Makefile linux-2.5.75-bk2/sound/oss/Makefile --- linux-2.5.75-bk1/sound/oss/Makefile 2003-07-10 13:06:54.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/Makefile 2003-07-12 04:37:47.000000000 -0700 @@ -33,6 +33,7 @@ obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o obj-$(CONFIG_SOUND_AD1816) += ad1816.o +obj-$(CONFIG_SOUND_AD1889) += ad1889.o ac97_codec.o obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o diff -urN linux-2.5.75-bk1/sound/oss/ac97_codec.c linux-2.5.75-bk2/sound/oss/ac97_codec.c --- linux-2.5.75-bk1/sound/oss/ac97_codec.c 2003-07-10 13:05:27.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/ac97_codec.c 2003-07-12 04:37:48.000000000 -0700 @@ -1,4 +1,3 @@ - /* * ac97_codec.c: Generic AC97 mixer/modem module * @@ -31,6 +30,10 @@ ************************************************************************** * * History + * May 02, 2003 Liam Girdwood + * Removed non existant WM9700 + * Added support for WM9705, WM9708, WM9709, WM9710, WM9711 + * WM9712 and WM9717 * Mar 28, 2002 Randolph Bentson * corrections to support WM9707 in ViewPad 1000 * v0.4 Mar 15 2000 Ollie Lho @@ -43,7 +46,9 @@ * Isolated from trident.c to support multiple ac97 codec */ #include +#include #include +#include #include #include #include @@ -62,9 +67,10 @@ static int ac97_init_mixer(struct ac97_codec *codec); -static int wolfson_init00(struct ac97_codec * codec); static int wolfson_init03(struct ac97_codec * codec); static int wolfson_init04(struct ac97_codec * codec); +static int wolfson_init05(struct ac97_codec * codec); +static int wolfson_init11(struct ac97_codec * codec); static int tritech_init(struct ac97_codec * codec); static int tritech_maestro_init(struct ac97_codec * codec); static int sigmatel_9708_init(struct ac97_codec *codec); @@ -72,7 +78,10 @@ static int sigmatel_9744_init(struct ac97_codec *codec); static int ad1886_init(struct ac97_codec *codec); static int eapd_control(struct ac97_codec *codec, int); -static int crystal_digital_control(struct ac97_codec *codec, int mode); +static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); +static int cmedia_init(struct ac97_codec * codec); +static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); +static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode); /* @@ -93,9 +102,11 @@ static struct ac97_ops null_ops = { NULL, NULL, NULL }; static struct ac97_ops default_ops = { NULL, eapd_control, NULL }; -static struct ac97_ops wolfson_ops00 = { wolfson_init00, NULL, NULL }; +static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control}; static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL }; static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL }; +static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL }; +static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL }; static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL }; static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL }; static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL }; @@ -103,12 +114,15 @@ static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL }; static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control }; static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL }; +static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL}; +static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control}; /* sorted by vendor/device id */ static const struct { u32 id; char *name; struct ac97_ops *ops; + int flags; } ac97_codec_ids[] = { {0x41445303, "Analog Devices AD1819", &null_ops}, {0x41445340, "Analog Devices AD1881", &null_ops}, @@ -120,8 +134,12 @@ {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, {0x414B4D01, "Asahi Kasei AK4542", &null_ops}, {0x414B4D02, "Asahi Kasei AK4543", &null_ops}, + {0x414C4326, "ALC100P", &null_ops}, {0x414C4710, "ALC200/200P", &null_ops}, - {0x414C4720, "ALC650", &null_ops}, + {0x414C4720, "ALC650", &default_digital_ops}, + {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME }, + {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME }, + {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME }, {0x43525900, "Cirrus Logic CS4297", &default_ops}, {0x43525903, "Cirrus Logic CS4297", &default_ops}, {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops}, @@ -132,6 +150,8 @@ {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops}, {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops}, {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops}, + {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM }, + {0x44543031, "Diamond Technology DT0893", &default_ops}, {0x45838308, "ESS Allegro ES1988", &null_ops}, {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */ {0x4e534331, "National Semiconductor LM4549", &null_ops}, @@ -143,9 +163,11 @@ {0x54524106, "TriTech TR28026", &null_ops}, {0x54524108, "TriTech TR28028", &tritech_ops}, {0x54524123, "TriTech TR A5", &null_ops}, - {0x574D4C00, "Wolfson WM9700A", &wolfson_ops00}, - {0x574D4C03, "Wolfson WM9703/WM9707", &wolfson_ops03}, + {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03}, {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04}, + {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05}, + {0x574D4C09, "Wolfson WM9709", &null_ops}, + {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11}, {0x83847600, "SigmaTel STAC????", &null_ops}, {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops}, {0x83847605, "SigmaTel STAC9704", &null_ops}, @@ -271,6 +293,10 @@ [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE }; +static LIST_HEAD(codecs); +static LIST_HEAD(codec_drivers); +static DECLARE_MUTEX(codec_sem); + /* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows about that given mixer, and should be holding a spinlock for the card */ static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) @@ -445,7 +471,7 @@ } /* read or write the recmask, the ac97 can really have left and right recording - inputs independently set, but OSS doesn't seem to want us to express that to + inputs independantly set, but OSS doesn't seem to want us to express that to the user. the caller guarantees that we have a supported bit set, and they must be holding the card's spinlock */ static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) @@ -487,8 +513,8 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, codec->name, sizeof(info.id)); - strlcpy(info.name, codec->name, sizeof(info.name)); + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); info.modify_counter = codec->modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -496,8 +522,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, codec->name, sizeof(info.id)); - strlcpy(info.name, codec->name, sizeof(info.name)); + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -678,6 +704,75 @@ } /** + * ac97_check_modem - Check if the Codec is a modem + * @codec: codec to check + * + * Return true if the device is an AC97 1.0 or AC97 2.0 modem + */ + +static int ac97_check_modem(struct ac97_codec *codec) +{ + /* Check for an AC97 1.0 soft modem (ID1) */ + if(codec->codec_read(codec, AC97_RESET) & 2) + return 1; + /* Check for an AC97 2.x soft modem */ + codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); + if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1) + return 1; + return 0; +} + + +/** + * ac97_alloc_codec - Allocate an AC97 codec + * + * Returns a new AC97 codec structure. AC97 codecs may become + * refcounted soon so this interface is needed. Returns with + * one reference taken. + */ + +struct ac97_codec *ac97_alloc_codec(void) +{ + struct ac97_codec *codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL); + if(!codec) + return NULL; + + memset(codec, 0, sizeof(*codec)); + spin_lock_init(&codec->lock); + INIT_LIST_HEAD(&codec->list); + return codec; +} + +EXPORT_SYMBOL(ac97_alloc_codec); + +/** + * ac97_release_codec - Release an AC97 codec + * @codec: codec to release + * + * Release an allocated AC97 codec. This will be refcounted in + * time but for the moment is trivial. Calls the unregister + * handler if the codec is now defunct. + */ + +void ac97_release_codec(struct ac97_codec *codec) +{ + /* Remove from the list first, we don't want to be + "rediscovered" */ + down(&codec_sem); + list_del(&codec->list); + up(&codec_sem); + /* + * The driver needs to deal with internal + * locking to avoid accidents here. + */ + if(codec->driver) + codec->driver->remove(codec, codec->driver); + kfree(codec); +} + +EXPORT_SYMBOL(ac97_release_codec); + +/** * ac97_probe_codec - Initialize and setup AC97-compatible codec * @codec: (in/out) Kernel info for a single AC97 codec * @@ -703,10 +798,13 @@ int ac97_probe_codec(struct ac97_codec *codec) { u16 id1, id2; - u16 audio, modem; + u16 audio; int i; char cidbuf[CODEC_ID_BUFSZ]; - + u16 f; + struct list_head *l; + struct ac97_driver *d; + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should * be read zero. * @@ -729,13 +827,9 @@ } /* probe for Modem Codec */ - codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); - modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1; - modem |= (audio&2); - audio &= ~2; - + codec->modem = ac97_check_modem(codec); codec->name = NULL; - codec->codec_ops = &null_ops; + codec->codec_ops = &default_ops; id1 = codec->codec_read(codec, AC97_VENDOR_ID1); id2 = codec->codec_read(codec, AC97_VENDOR_ID2); @@ -744,16 +838,51 @@ codec->type = ac97_codec_ids[i].id; codec->name = ac97_codec_ids[i].name; codec->codec_ops = ac97_codec_ids[i].ops; + codec->flags = ac97_codec_ids[i].flags; break; } } + + codec->model = (id1 << 16) | id2; + + f = codec->codec_read(codec, AC97_EXTENDED_STATUS); + if(f & 4) + codec->codec_ops = &default_digital_ops; + + /* A device which thinks its a modem but isnt */ + if(codec->flags & AC97_DELUDED_MODEM) + codec->modem = 0; + if (codec->name == NULL) codec->name = "Unknown"; printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n", - modem ? "Modem" : (audio ? "Audio" : ""), + codec->modem ? "Modem" : (audio ? "Audio" : ""), codec_id(id1, id2, cidbuf), codec->name); - return ac97_init_mixer(codec); + if(!ac97_init_mixer(codec)) + return 0; + + /* + * Attach last so the caller can override the mixer + * callbacks. + */ + + down(&codec_sem); + list_add(&codec->list, &codecs); + + list_for_each(l, &codec_drivers) { + d = list_entry(l, struct ac97_driver, list); + if ((codec->model ^ d->codec_id) & d->codec_mask) + continue; + if(d->probe(codec, d) == 0) + { + codec->driver = d; + break; + } + } + + up(&codec_sem); + return 1; } static int ac97_init_mixer(struct ac97_codec *codec) @@ -772,6 +901,7 @@ if (!(cap & 0x10)) codec->supported_mixers &= ~SOUND_MASK_ALTPCM; + /* detect bit resolution */ codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020); if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020) @@ -800,6 +930,15 @@ ac97_set_mixer(codec, md->mixer, md->value); } + /* + * Volume is MUTE only on this device. We have to initialise + * it but its useless beyond that. + */ + if(codec->flags & AC97_NO_PCM_VOLUME) + { + codec->supported_mixers &= ~SOUND_MASK_PCM; + printk(KERN_WARNING "AC97 codec does not have proper volume support.\n"); + } return 1; } @@ -872,54 +1011,75 @@ return 0; } - -static int wolfson_init00(struct ac97_codec * codec) +static int cmedia_init(struct ac97_codec *codec) { - /* This initialization is suspect, but not known to be wrong. - It was copied from the initialization for the WM9704Q, but - that same sequence is known to fail for the WM9707. Thus - this warning may help someone with hardware to test - this code. */ - codec->codec_write(codec, 0x72, 0x0808); - codec->codec_write(codec, 0x74, 0x0808); - - // patch for DVD noise - codec->codec_write(codec, 0x5a, 0x0200); - - // init vol as PCM vol - codec->codec_write(codec, 0x70, - codec->codec_read(codec, AC97_PCMOUT_VOL)); - - codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + /* Initialise the CMedia 9739 */ + /* + We could set various options here + Register 0x20 bit 0x100 sets mic as center bass + Also do multi_channel_ctrl &=~0x3000 |=0x1000 + + For now we set up the GPIO and PC beep + */ + + u16 v; + + /* MIC */ + codec->codec_write(codec, 0x64, 0x3000); + v = codec->codec_read(codec, 0x64); + v &= ~0x8000; + codec->codec_write(codec, 0x64, v); + codec->codec_write(codec, 0x70, 0x0100); + codec->codec_write(codec, 0x72, 0x0020); return 0; } - + +#define AC97_WM97XX_FMIXER_VOL 0x72 +#define AC97_WM97XX_RMIXER_VOL 0x74 +#define AC97_WM97XX_TEST 0x5a +#define AC97_WM9704_RPCM_VOL 0x70 +#define AC97_WM9711_OUT3VOL 0x16 static int wolfson_init03(struct ac97_codec * codec) { /* this is known to work for the ViewSonic ViewPad 1000 */ - codec->codec_write(codec, 0x72, 0x0808); - codec->codec_write(codec, 0x20, 0x8000); + codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000); return 0; } - static int wolfson_init04(struct ac97_codec * codec) { - codec->codec_write(codec, 0x72, 0x0808); - codec->codec_write(codec, 0x74, 0x0808); + codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808); // patch for DVD noise - codec->codec_write(codec, 0x5a, 0x0200); + codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200); // init vol as PCM vol - codec->codec_write(codec, 0x70, + codec->codec_write(codec, AC97_WM9704_RPCM_VOL, codec->codec_read(codec, AC97_PCMOUT_VOL)); + /* set rear surround volume */ codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); return 0; } +/* WM9705, WM9710 */ +static int wolfson_init05(struct ac97_codec * codec) +{ + /* set front mixer volume */ + codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + return 0; +} + +/* WM9711, WM9712 */ +static int wolfson_init11(struct ac97_codec * codec) +{ + /* set out3 volume */ + codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808); + return 0; +} static int tritech_init(struct ac97_codec * codec) { @@ -980,26 +1140,115 @@ return 0; } +static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode) +{ + u16 reg; + + reg = codec->codec_read(codec, AC97_SPDIF_CONTROL); + + switch(rate) + { + /* Off by default */ + default: + case 0: + reg = codec->codec_read(codec, AC97_EXTENDED_STATUS); + codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF)); + if(rate == 0) + return 0; + return -EINVAL; + case 1: + reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; + break; + case 2: + reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; + break; + case 3: + reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; + break; + } + + reg &= ~AC97_SC_CC_MASK; + reg |= (mode & AUDIO_CCMASK) << 6; + + if(mode & AUDIO_DIGITAL) + reg |= 2; + if(mode & AUDIO_PRO) + reg |= 1; + if(mode & AUDIO_DRS) + reg |= 0x4000; + + codec->codec_write(codec, AC97_SPDIF_CONTROL, reg); + + reg = codec->codec_read(codec, AC97_EXTENDED_STATUS); + reg &= (AC97_EA_SLOT_MASK); + reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots; + codec->codec_write(codec, AC97_EXTENDED_STATUS, reg); + + reg = codec->codec_read(codec, AC97_EXTENDED_STATUS); + if(!(reg & 0x0400)) + { + codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF); + return -EINVAL; + } + return 0; +} + /* - * Crystal digital audio control (CS4299 + * Crystal digital audio control (CS4299) */ -static int crystal_digital_control(struct ac97_codec *codec, int mode) +static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode) { u16 cv; - switch(mode) + if(mode & AUDIO_DIGITAL) + return -EINVAL; + + switch(rate) { case 0: cv = 0x0; break; /* SPEN off */ - case 1: cv = 0x8004; break; /* 48KHz digital */ - case 2: cv = 0x8104; break; /* 44.1KHz digital */ + case 48000: cv = 0x8004; break; /* 48KHz digital */ + case 44100: cv = 0x8104; break; /* 44.1KHz digital */ + case 32768: /* 32Khz */ default: - return -1; /* Not supported yet(eg AC3) */ + return -EINVAL; } codec->codec_write(codec, 0x68, cv); return 0; } +/* + * CMedia digital audio control + * Needs more work. + */ + +static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode) +{ + u16 cv; + + if(mode & AUDIO_DIGITAL) + return -EINVAL; + + switch(rate) + { + case 0: cv = 0x0001; break; /* SPEN off */ + case 48000: cv = 0x0009; break; /* 48KHz digital */ + default: + return -EINVAL; + } + codec->codec_write(codec, 0x2A, 0x05c4); + codec->codec_write(codec, 0x6C, cv); + + /* Switch on mix to surround */ + cv = codec->codec_read(codec, 0x64); + cv &= ~0x0200; + if(mode) + cv |= 0x0200; + codec->codec_write(codec, 0x64, cv); + return 0; +} + + /* copied from drivers/sound/maestro.c */ #if 0 /* there has been 1 person on the planet with a pt101 that we know of. If they care, they can put this back in :) */ @@ -1137,4 +1386,67 @@ EXPORT_SYMBOL(ac97_restore_state); +/** + * ac97_register_driver - register a codec helper + * @driver: Driver handler + * + * Register a handler for codecs matching the codec id. The handler + * attach function is called for all present codecs and will be + * called when new codecs are discovered. + */ + +int ac97_register_driver(struct ac97_driver *driver) +{ + struct list_head *l; + struct ac97_codec *c; + + down(&codec_sem); + INIT_LIST_HEAD(&driver->list); + list_add(&driver->list, &codec_drivers); + + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if(c->driver != NULL || ((c->model ^ driver->codec_id) & driver->codec_mask)) + continue; + if(driver->probe(c, driver)) + continue; + c->driver = driver; + } + up(&codec_sem); + return 0; +} + +EXPORT_SYMBOL_GPL(ac97_register_driver); + +/** + * ac97_unregister_driver - unregister a codec helper + * @driver: Driver handler + * + * Register a handler for codecs matching the codec id. The handler + * attach function is called for all present codecs and will be + * called when new codecs are discovered. + */ + +void ac97_unregister_driver(struct ac97_driver *driver) +{ + struct list_head *l; + struct ac97_codec *c; + + down(&codec_sem); + list_del_init(&driver->list); + + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if(c->driver == driver) + driver->remove(c, driver); + c->driver = NULL; + } + + up(&codec_sem); +} + +EXPORT_SYMBOL_GPL(ac97_unregister_driver); + MODULE_LICENSE("GPL"); diff -urN linux-2.5.75-bk1/sound/oss/ac97_plugin_ad1980.c linux-2.5.75-bk2/sound/oss/ac97_plugin_ad1980.c --- linux-2.5.75-bk1/sound/oss/ac97_plugin_ad1980.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.75-bk2/sound/oss/ac97_plugin_ad1980.c 2003-07-12 04:37:48.000000000 -0700 @@ -0,0 +1,124 @@ +/* + ac97_plugin_ad1980.c Copyright (C) 2003 Red Hat, Inc. All rights reserved. + + The contents of this file are subject to the Open Software License version 1.1 + that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is + included herein by reference. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL") as + distributed in the kernel source COPYING file, in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the OSL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the OSL or the GPL. + + Authors: Arjan van de Ven + + This is an example codec plugin. This one switches the connections + around to match the setups some vendors use with audio switched to + non standard front connectors not the normal rear ones + + This code primarily exists to demonstrate how to use the codec + interface + +*/ + +#include +#include +#include +#include + +/** + * ad1980_remove - codec remove callback + * @codec: The codec that is being removed + * + * This callback occurs when an AC97 codec is being removed. A + * codec remove call will not occur for a codec during that codec + * probe callback. + * + * Most drivers will need to lock their remove versus their + * use of the codec after the probe function. + */ + +static void ad1980_remove(struct ac97_codec *codec) +{ + /* Nothing to do in the simple example */ +} + + +/** + * ad1980_probe - codec found callback + * @codec: ac97 codec matching the idents + * @driver: ac97_driver it matched + * + * This entry point is called when a codec is found which matches + * the driver. At the point it is called the codec is basically + * operational, mixer operations have been initialised and can + * be overriden. Called in process context. The field driver_private + * is available for the driver to use to store stuff. + * + * The caller can claim the device by returning zero, or return + * a negative error code. + */ + +static int ad1980_probe(struct ac97_codec *codec, struct ac97_driver *driver) +{ + u16 control; + +#define AC97_AD_MISC 0x76 + + /* Switch the inputs/outputs over (from Dell code) */ + control = codec->codec_read(codec, AC97_AD_MISC); + codec->codec_write(codec, AC97_AD_MISC, control | 0x0420); + + /* We could refuse the device since we dont need to hang around, + but we will claim it */ + return 0; +} + + +static struct ac97_driver ad1980_driver = { + codec_id: 0x41445370, + codec_mask: 0xFFFFFFFF, + name: "AD1980 example", + probe: ad1980_probe, + remove: __devexit_p(ad1980_remove), +}; + +/** + * ad1980_exit - module exit path + * + * Our module is being unloaded. At this point unregister_driver + * will call back our remove handler for any existing codecs. You + * may not unregister_driver from interrupt context or from a + * probe/remove callback. + */ + +static void ad1980_exit(void) +{ + ac97_unregister_driver(&ad1980_driver); +} + +/** + * ad1980_init - set up ad1980 handlers + * + * After we call the register function it will call our probe + * function for each existing matching device before returning to us. + * Any devices appearing afterwards whose id's match the codec_id + * will also cause the probe function to be called. + * You may not register_driver from interrupt context or from a + * probe/remove callback. + */ + +static int ad1980_init(void) +{ + return ac97_register_driver(&ad1980_driver); +} + +module_init(ad1980_init); +module_exit(ad1980_exit); diff -urN linux-2.5.75-bk1/sound/oss/ad1889.c linux-2.5.75-bk2/sound/oss/ad1889.c --- linux-2.5.75-bk1/sound/oss/ad1889.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.75-bk2/sound/oss/ad1889.c 2003-07-12 04:37:48.000000000 -0700 @@ -0,0 +1,1079 @@ +/* + * Copyright 2001 Randolph Chung + * + * Analog Devices 1889 PCI audio driver (AD1819 AC97-compatible codec) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Notes: + * 1. Only flat DMA is supported; s-g is not supported right now + * + * + tausq: Anyway, to set up sample rates for D to A, you just use the sample rate on the codec. For A to D, you need to set the codec always to 48K (using the split sample rate feature on the codec) and then set the resampler on the AD1889 to the sample rate you want. + Also, when changing the sample rate on the codec you need to power it down and re power it up for the change to take effect! + * + * $Id: ad1889.c,v 1.3 2002/10/19 21:31:44 grundler Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ad1889.h" + +#define DBG(fmt, arg...) printk(fmt, ##arg) +#define DEVNAME "ad1889" + +#define NR_HW_CH 4 +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define UNDERRUN(dev) (0) + +#define AD1889_READW(dev,reg) readw(dev->regbase + reg) +#define AD1889_WRITEW(dev,reg,val) writew((val), dev->regbase + reg) +#define AD1889_READL(dev,reg) readl(dev->regbase + reg) +#define AD1889_WRITEL(dev,reg,val) writel((val), dev->regbase + reg) + +//now 100ms +#define WAIT_10MS() do { int __i; for (__i = 0; __i < 100; __i++) udelay(1000); } while(0) + +/* currently only support a single device */ +static ad1889_dev_t *ad1889_dev = NULL; + +/************************* helper routines ***************************** */ +static inline void ad1889_set_wav_rate(ad1889_dev_t *dev, int rate) +{ + dev->state[AD_WAV_STATE].dmabuf.rate = rate; + AD1889_WRITEW(dev, AD_DSWAS, rate); +} + +static inline void ad1889_set_adc_rate(ad1889_dev_t *dev, int rate) +{ + dev->state[AD_ADC_STATE].dmabuf.rate = rate; + AD1889_WRITEW(dev, AD_DSRES, rate); +} + +static inline void ad1889_set_wav_fmt(ad1889_dev_t *dev, int fmt) +{ + u16 tmp; + + tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); + if (fmt == AFMT_S16_LE) { + //tmp |= 0x0100; /* set WA16 */ + tmp |= 0x0300; /* set WA16 stereo */ + } else if (fmt == AFMT_U8) { + tmp &= ~0x0100; /* clear WA16 */ + } + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); +} + +static inline void ad1889_set_adc_fmt(ad1889_dev_t *dev, int fmt) +{ + u16 tmp; + + tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); + if (fmt == AFMT_S16_LE) { + tmp |= 0x0100; /* set WA16 */ + } else if (fmt == AFMT_U8) { + tmp &= ~0x0100; /* clear WA16 */ + } + AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); +} + +static void ad1889_start_wav(ad1889_state_t *state) +{ + unsigned long flags; + struct dmabuf *dmabuf = &state->dmabuf; + int cnt; + u16 tmp; + + spin_lock_irqsave(&state->card->lock, flags); + + if (dmabuf->dma_len) /* DMA already in flight */ + goto skip_dma; + + /* setup dma */ + cnt = dmabuf->wr_ptr - dmabuf->rd_ptr; + if (cnt == 0) /* done - don't need to do anything */ + goto skip_dma; + + /* If the wr_ptr has wrapped, only map to the end */ + if (cnt < 0) + cnt = DMA_SIZE - dmabuf->rd_ptr; + + dmabuf->dma_handle = pci_map_single(ad1889_dev->pci, + dmabuf->rawbuf + dmabuf->rd_ptr, + cnt, PCI_DMA_TODEVICE); + dmabuf->dma_len = cnt; + dmabuf->ready = 1; + + /* load up the current register set */ + AD1889_WRITEL(ad1889_dev, AD_DMAWAVCC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVICC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVCA, dmabuf->dma_handle); + + /* TODO: for now we load the base registers with the same thing */ + AD1889_WRITEL(ad1889_dev, AD_DMAWAVBC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVIBC, cnt); + AD1889_WRITEL(ad1889_dev, AD_DMAWAVBA, dmabuf->dma_handle); + + /* and we're off to the races... */ + AD1889_WRITEL(ad1889_dev, AD_DMACHSS, 0x8); + tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); + tmp |= 0x0400; /* set WAEN */ + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); + (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */ + + dmabuf->enable |= DAC_RUNNING; + +skip_dma: + spin_unlock_irqrestore(&state->card->lock, flags); +} + + +static void ad1889_stop_wav(ad1889_state_t *state) +{ + unsigned long flags; + struct dmabuf *dmabuf = &state->dmabuf; + + spin_lock_irqsave(&state->card->lock, flags); + + if (dmabuf->enable & DAC_RUNNING) { + u16 tmp; + unsigned long cnt = dmabuf->dma_len; + + tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); + tmp &= ~0x0400; /* clear WAEN */ + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); + (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */ + pci_unmap_single(ad1889_dev->pci, dmabuf->dma_handle, + cnt, PCI_DMA_TODEVICE); + + dmabuf->enable &= ~DAC_RUNNING; + + /* update dma pointers */ + dmabuf->rd_ptr += cnt; + dmabuf->rd_ptr &= (DMA_SIZE - 1); + + dmabuf->dma_handle = 0; + dmabuf->dma_len = 0; + dmabuf->ready = 0; + + wake_up(&dmabuf->wait); + } + + spin_unlock_irqrestore(&state->card->lock, flags); +} + + +#if 0 +static void ad1889_startstop_adc(ad1889_state_t *state, int start) +{ + u16 tmp; + unsigned long flags; + + spin_lock_irqsave(&state->card->lock, flags); + + tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); + if (start) { + state->dmabuf.enable |= ADC_RUNNING; + tmp |= 0x0004; /* set ADEN */ + } else { + state->dmabuf.enable &= ~ADC_RUNNING; + tmp &= ~0x0004; /* clear ADEN */ + } + AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); + + spin_unlock_irqrestore(&state->card->lock, flags); +} +#endif + +static ad1889_dev_t *ad1889_alloc_dev(struct pci_dev *pci) +{ + ad1889_dev_t *dev; + struct dmabuf *dmabuf; + int i; + + if ((dev = kmalloc(sizeof(ad1889_dev_t), GFP_KERNEL)) == NULL) + return NULL; + memset(dev, 0, sizeof(ad1889_dev_t)); + spin_lock_init(&dev->lock); + dev->pci = pci; + + for (i = 0; i < AD_MAX_STATES; i++) { + dev->state[i].card = dev; + init_MUTEX(&dev->state[i].sem); + init_waitqueue_head(&dev->state[i].dmabuf.wait); + } + + /* allocate dma buffer */ + + for (i = 0; i < AD_MAX_STATES; i++) { + dmabuf = &dev->state[i].dmabuf; + if ((dmabuf->rawbuf = kmalloc(DMA_SIZE, GFP_KERNEL|GFP_DMA)) == NULL) + return NULL; + dmabuf->rawbuf_size = DMA_SIZE; + dmabuf->dma_handle = 0; + dmabuf->rd_ptr = dmabuf->wr_ptr = dmabuf->dma_len = 0UL; + dmabuf->ready = 0; + dmabuf->rate = 44100; + } + + return dev; +} + +static void ad1889_free_dev(ad1889_dev_t *dev) +{ + int j; + struct dmabuf *dmabuf; + + if (dev == NULL) + return; + + if (dev->ac97_codec) + ac97_release_codec(dev->ac97_codec); + + for (j = 0; j < AD_MAX_STATES; j++) { + dmabuf = &dev->state[j].dmabuf; + if (dmabuf->rawbuf != NULL) + kfree(dmabuf->rawbuf); + } + + kfree(dev); +} + +static inline void ad1889_trigger_playback(ad1889_dev_t *dev) +{ +#if 0 + u32 val; + struct dmabuf *dmabuf = &dev->state[AD_WAV_STATE].dmabuf; +#endif + + ad1889_start_wav(&dev->state[AD_WAV_STATE]); +} + +int ad1889_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int len, i; + ad1889_dev_t *dev = data; + ad1889_reg_t regs[] = { + { "WSMC", AD_DSWSMC, 16 }, + { "RAMC", AD_DSRAMC, 16 }, + { "WADA", AD_DSWADA, 16 }, + { "SYDA", AD_DSSYDA, 16 }, + { "WAS", AD_DSWAS, 16 }, + { "RES", AD_DSRES, 16 }, + { "CCS", AD_DSCCS, 16 }, + { "ADCBA", AD_DMAADCBA, 32 }, + { "ADCCA", AD_DMAADCCA, 32 }, + { "ADCBC", AD_DMAADCBC, 32 }, + { "ADCCC", AD_DMAADCCC, 32 }, + { "ADCIBC", AD_DMAADCIBC, 32 }, + { "ADCICC", AD_DMAADCICC, 32 }, + { "ADCCTRL", AD_DMAADCCTRL, 16 }, + { "WAVBA", AD_DMAWAVBA, 32 }, + { "WAVCA", AD_DMAWAVCA, 32 }, + { "WAVBC", AD_DMAWAVBC, 32 }, + { "WAVCC", AD_DMAWAVCC, 32 }, + { "WAVIBC", AD_DMAWAVIBC, 32 }, + { "WAVICC", AD_DMAWAVICC, 32 }, + { "WAVCTRL", AD_DMAWAVCTRL, 16 }, + { "DISR", AD_DMADISR, 32 }, + { "CHSS", AD_DMACHSS, 32 }, + { "IPC", AD_GPIOIPC, 16 }, + { "OP", AD_GPIOOP, 16 }, + { "IP", AD_GPIOIP, 16 }, + { "ACIC", AD_ACIC, 16 }, + { "AC97_RESET", 0x100 + AC97_RESET, 16 }, + { "AC97_MASTER_VOL_STEREO", 0x100 + AC97_MASTER_VOL_STEREO, 16 }, + { "AC97_HEADPHONE_VOL", 0x100 + AC97_HEADPHONE_VOL, 16 }, + { "AC97_MASTER_VOL_MONO", 0x100 + AC97_MASTER_VOL_MONO, 16 }, + { "AC97_MASTER_TONE", 0x100 + AC97_MASTER_TONE, 16 }, + { "AC97_PCBEEP_VOL", 0x100 + AC97_PCBEEP_VOL, 16 }, + { "AC97_PHONE_VOL", 0x100 + AC97_PHONE_VOL, 16 }, + { "AC97_MIC_VOL", 0x100 + AC97_MIC_VOL, 16 }, + { "AC97_LINEIN_VOL", 0x100 + AC97_LINEIN_VOL, 16 }, + { "AC97_CD_VOL", 0x100 + AC97_CD_VOL, 16 }, + { "AC97_VIDEO_VOL", 0x100 + AC97_VIDEO_VOL, 16 }, + { "AC97_AUX_VOL", 0x100 + AC97_AUX_VOL, 16 }, + { "AC97_PCMOUT_VOL", 0x100 + AC97_PCMOUT_VOL, 16 }, + { "AC97_RECORD_SELECT", 0x100 + AC97_RECORD_SELECT, 16 }, + { "AC97_RECORD_GAIN", 0x100 + AC97_RECORD_GAIN, 16 }, + { "AC97_RECORD_GAIN_MIC", 0x100 + AC97_RECORD_GAIN_MIC, 16 }, + { "AC97_GENERAL_PURPOSE", 0x100 + AC97_GENERAL_PURPOSE, 16 }, + { "AC97_3D_CONTROL", 0x100 + AC97_3D_CONTROL, 16 }, + { "AC97_MODEM_RATE", 0x100 + AC97_MODEM_RATE, 16 }, + { "AC97_POWER_CONTROL", 0x100 + AC97_POWER_CONTROL, 16 }, + { 0 } + }; + + if (dev == NULL) + return -ENODEV; + + for (i = 0; regs[i].name != 0; i++) + out += sprintf(out, "%s: 0x%0*x\n", regs[i].name, + regs[i].width >> 2, + (regs[i].width == 16 + ? AD1889_READW(dev, regs[i].offset) + : AD1889_READL(dev, regs[i].offset))); + + for (i = 0; i < AD_MAX_STATES; i++) { + out += sprintf(out, "DMA status for %s:\n", + (i == AD_WAV_STATE ? "WAV" : "ADC")); + out += sprintf(out, "\t\t0x%p (IOVA: 0x%u)\n", + dev->state[i].dmabuf.rawbuf, + dev->state[i].dmabuf.dma_handle); + + out += sprintf(out, "\tread ptr: offset %u\n", + (unsigned int)dev->state[i].dmabuf.rd_ptr); + out += sprintf(out, "\twrite ptr: offset %u\n", + (unsigned int)dev->state[i].dmabuf.wr_ptr); + out += sprintf(out, "\tdma len: offset %u\n", + (unsigned int)dev->state[i].dmabuf.dma_len); + } + + len = out - page - off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else { + len = count; + } + *start = page + off; + return len; +} + +/***************************** DMA interfaces ************************** */ +#if 0 +static inline unsigned long ad1889_get_dma_addr(ad1889_state_t *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 offset; + + if (!(dmabuf->enable & (DAC_RUNNING | ADC_RUNNING))) { + printk(KERN_ERR DEVNAME ": get_dma_addr called without dma enabled\n"); + return 0; + } + + if (dmabuf->enable & DAC_RUNNING) + offset = le32_to_cpu(AD1889_READL(state->card, AD_DMAWAVBA)); + else + offset = le32_to_cpu(AD1889_READL(state->card, AD_DMAADCBA)); + + return (unsigned long)bus_to_virt((unsigned long)offset) - (unsigned long)dmabuf->rawbuf; +} + +static void ad1889_update_ptr(ad1889_dev_t *dev, int wake) +{ + ad1889_state_t *state; + struct dmabuf *dmabuf; + unsigned long hwptr; + int diff; + + /* check ADC first */ + state = &dev->adc_state; + dmabuf = &state->dmabuf; + if (dmabuf->enable & ADC_RUNNING) { + hwptr = ad1889_get_dma_addr(state); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + if (dmabuf->count > dmabuf->dmasize) + dmabuf->count = dmabuf->dmasize; + + if (dmabuf->mapped) { + if (wake & dmabuf->count >= dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + if (wake & dmabuf->count > 0) + wake_up(&dmabuf->wait); + } + } + + /* check DAC */ + state = &dev->wav_state; + dmabuf = &state->dmabuf; + if (dmabuf->enable & DAC_RUNNING) { +XXX + +} +#endif + +/************************* /dev/dsp interfaces ************************* */ + +static ssize_t ad1889_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + return 0; +} + +static ssize_t ad1889_write(struct file *file, const char *buffer, size_t count, + loff_t *ppos) +{ + ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; + ad1889_state_t *state = &dev->state[AD_WAV_STATE]; + volatile struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret = 0; + DECLARE_WAITQUEUE(wait, current); + + if (ppos != &file->f_pos) + return -ESPIPE; + + down(&state->sem); +#if 0 + if (dmabuf->mapped) { + ret = -ENXIO; + goto err1; + } +#endif + if (!access_ok(VERIFY_READ, buffer, count)) { + ret = -EFAULT; + goto err1; + } + + add_wait_queue(&state->dmabuf.wait, &wait); + + /* start filling dma buffer.... */ + while (count > 0) { + long rem; + long cnt = count; + unsigned long flags; + + + for (;;) { + long used_bytes; + long timeout; /* max time for DMA in jiffies */ + + /* buffer is full if wr catches up to rd */ + spin_lock_irqsave(&state->card->lock, flags); + used_bytes = dmabuf->wr_ptr - dmabuf->rd_ptr; + timeout = (dmabuf->dma_len * HZ) / dmabuf->rate; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* adjust for buffer wrap around */ + used_bytes = (used_bytes + DMA_SIZE) & (DMA_SIZE - 1); + + /* If at least one page unused */ + if (used_bytes < (DMA_SIZE - 0x1000)) + break; + + /* dma buffer full */ + + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto err2; + } + + set_current_state(TASK_INTERRUPTIBLE); + if (!schedule_timeout(timeout + 1)) + printk(KERN_WARNING "AD1889 timeout(%ld) r/w %lx/%lx len %lx\n", + timeout+1, + dmabuf->rd_ptr, dmabuf->wr_ptr, + dmabuf->dma_len); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto err2; + } + + } + + /* watch out for wrapping around static buffer */ + spin_lock_irqsave(&state->card->lock, flags); + rem = DMA_SIZE - dmabuf->wr_ptr; + if (cnt > rem) + cnt = rem; + + rem = dmabuf->wr_ptr; + + /* update dma pointers */ + dmabuf->wr_ptr += cnt; + dmabuf->wr_ptr &= DMA_SIZE - 1; /* wrap ptr if necessary */ + spin_unlock_irqrestore(&state->card->lock, flags); + + /* transfer unwrapped chunk */ + if (copy_from_user(dmabuf->rawbuf + rem, buffer, cnt)) { + ret = -EFAULT; + goto err2; + } + + DBG("Writing 0x%lx bytes to +0x%lx\n", cnt, rem); + + /* update counters */ + count -= cnt; + buffer += cnt; + ret += cnt; + + /* we have something to play - go play it! */ + ad1889_trigger_playback(dev); + } + +err2: + remove_wait_queue(&state->dmabuf.wait, &wait); +err1: + up(&state->sem); + return ret; +} + +static unsigned int ad1889_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; +#if 0 + ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; + ad1889_state_t *state = NULL; + struct dmabuf *dmabuf; + unsigned long flags; + + if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) + return -EINVAL; + + if (file->f_mode & FMODE_WRITE) { + state = &dev->state[AD_WAV_STATE]; + if (!state) return 0; + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + + if (file->f_mode & FMODE_READ) { + state = &dev->state[AD_ADC_STATE]; + if (!state) return 0; + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + + spin_lock_irqsave(&dev->lock, flags); + ad1889_update_ptr(dev, 0); + + if (file->f_mode & FMODE_WRITE) { + state = &dev->state[WAV_STATE]; + dmabuf = &state->dmabuf; + if (dmabuf->mapped) { + if (dmabuf->count >= (int)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((int)dmabuf->dmasize >= dmabuf->count + + (int)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + + if (file ->f_mode & FMODE_READ) { + state = &dev->state[AD_ADC_STATE]; + dmabuf = &state->dmabuf; + if (dmabuf->count >= (int)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&dev->lock, flags); + +#endif + return mask; +} + +static int ad1889_mmap(struct file *file, struct vm_area_struct *vma) +{ + return 0; +} + +static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int val = 0; + ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; + struct dmabuf *dmabuf; + audio_buf_info abinfo; + + switch (cmd) + { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + break; + + case SNDCTL_DSP_SYNC: + break; + + case SNDCTL_DSP_SPEED: + /* set sampling rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val > 5400 && val < 48000) + { + if (file->f_mode & FMODE_WRITE) + AD1889_WRITEW(ad1889_dev, AD_DSWAS, val); + if (file->f_mode & FMODE_READ) + AD1889_WRITEW(ad1889_dev, AD_DSRES, val); + } + return 0; + + case SNDCTL_DSP_STEREO: /* undocumented? */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + val = AD1889_READW(ad1889_dev, AD_DSWSMC); + if (val) { + val |= 0x0200; /* set WAST */ + } else { + val &= ~0x0200; /* clear WAST */ + } + AD1889_WRITEW(ad1889_dev, AD_DSWSMC, val); + } + if (file->f_mode & FMODE_WRITE) { + val = AD1889_READW(ad1889_dev, AD_DSRAMC); + if (val) { + val |= 0x0002; /* set ADST */ + } else { + val &= ~0x0002; /* clear ADST */ + } + AD1889_WRITEW(ad1889_dev, AD_DSRAMC, val); + } + + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + return put_user(DMA_SIZE, (int *)arg); + + case SNDCTL_DSP_GETFMTS: + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_READ) + ad1889_set_adc_fmt(dev, val); + + if (file->f_mode & FMODE_WRITE) + ad1889_set_wav_fmt(dev, val); + + return put_user(val, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + break; + + case SNDCTL_DSP_POST: + /* send all data to device */ + break; + + case SNDCTL_DSP_SUBDIVIDE: + break; + + case SNDCTL_DSP_SETFRAGMENT: + /* not supported; uses fixed fragment sizes */ + return put_user(DMA_SIZE, (int *)arg); + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + /* space left in dma buffers */ + if (cmd == SNDCTL_DSP_GETOSPACE) + dmabuf = &dev->state[AD_WAV_STATE].dmabuf; + else + dmabuf = &dev->state[AD_ADC_STATE].dmabuf; + abinfo.fragments = 1; + abinfo.fragstotal = 1; + abinfo.fragsize = DMA_SIZE; + abinfo.bytes = DMA_SIZE; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(0, (int *)arg); + + case SNDCTL_DSP_GETTRIGGER: + case SNDCTL_DSP_SETTRIGGER: + break; + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + break; + + case SNDCTL_DSP_SETDUPLEX: + break; + + case SNDCTL_DSP_GETODELAY: + break; + + case SOUND_PCM_READ_RATE: + return put_user(AD1889_READW(ad1889_dev, AD_DSWAS), (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + break; + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + break; + + default: + break; + } + + return -ENOTTY; +} + +static int ad1889_open(struct inode *inode, struct file *file) +{ + /* check minor; only support /dev/dsp atm */ + if (minor(inode->i_rdev) != 3) + return -ENXIO; + + file->private_data = ad1889_dev; + + ad1889_set_wav_rate(ad1889_dev, 44100); + ad1889_set_wav_fmt(ad1889_dev, AFMT_S16_LE); + AD1889_WRITEW(ad1889_dev, AD_DSWADA, 0x0404); /* attenuation */ + MOD_INC_USE_COUNT; + return 0; +} + +static int ad1889_release(struct inode *inode, struct file *file) +{ + /* if we have state free it here */ + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations ad1889_fops = { + llseek: no_llseek, + read: ad1889_read, + write: ad1889_write, + poll: ad1889_poll, + ioctl: ad1889_ioctl, + mmap: ad1889_mmap, + open: ad1889_open, + release: ad1889_release, +}; + +/************************* /dev/mixer interfaces ************************ */ +static int ad1889_mixer_open(struct inode *inode, struct file *file) +{ + if (ad1889_dev->ac97_codec->dev_mixer != minor(inode->i_rdev)) + return -ENODEV; + + file->private_data = ad1889_dev->ac97_codec; + MOD_INC_USE_COUNT; + return 0; +} + +static int ad1889_mixer_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static int ad1889_mixer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static struct file_operations ad1889_mixer_fops = { + llseek: no_llseek, + ioctl: ad1889_mixer_ioctl, + open: ad1889_mixer_open, + release: ad1889_mixer_release, +}; + +/************************* AC97 interfaces ****************************** */ +static void ad1889_codec_write(struct ac97_codec *ac97, u8 reg, u16 val) +{ + ad1889_dev_t *dev = ac97->private_data; + + //DBG("Writing 0x%x to 0x%lx\n", val, dev->regbase + 0x100 + reg); + AD1889_WRITEW(dev, 0x100 + reg, val); +} + +static u16 ad1889_codec_read(struct ac97_codec *ac97, u8 reg) +{ + ad1889_dev_t *dev = ac97->private_data; + //DBG("Reading from 0x%lx\n", dev->regbase + 0x100 + reg); + return AD1889_READW(dev, 0x100 + reg); +} + +static int ad1889_ac97_init(ad1889_dev_t *dev, int id) +{ + struct ac97_codec *ac97; + u16 eid; + + if ((ac97 = ac97_alloc_codec()) == NULL) + return -ENOMEM; + + ac97->private_data = dev; + ac97->id = id; + + ac97->codec_read = ad1889_codec_read; + ac97->codec_write = ad1889_codec_write; + + if (ac97_probe_codec(ac97) == 0) { + printk(DEVNAME ": ac97_probe_codec failed\n"); + goto out_free; + } + + eid = ad1889_codec_read(ac97, AC97_EXTENDED_ID); + if (eid == 0xffffff) { + printk(KERN_WARNING DEVNAME ": no codec attached?\n"); + goto out_free; + } + + dev->ac97_features = eid; + + if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1)) < 0) { + printk(KERN_ERR DEVNAME ": cannot register mixer\n"); + goto out_free; + } + + dev->ac97_codec = ac97; + return 0; + +out_free: + ac97_release_codec(ac97); + return -ENODEV; +} + +static int ad1889_aclink_reset(struct pci_dev * pcidev) +{ + u16 stat; + int retry = 200; + ad1889_dev_t *dev = pci_get_drvdata(pcidev); + + AD1889_WRITEW(dev, AD_DSCCS, 0x8000); /* turn on clock */ + AD1889_READW(dev, AD_DSCCS); + + WAIT_10MS(); + + stat = AD1889_READW(dev, AD_ACIC); + stat |= 0x0002; /* Reset Disable */ + AD1889_WRITEW(dev, AD_ACIC, stat); + (void) AD1889_READW(dev, AD_ACIC); /* flush posted write */ + + udelay(10); + + stat = AD1889_READW(dev, AD_ACIC); + stat |= 0x0001; /* Interface Enable */ + AD1889_WRITEW(dev, AD_ACIC, stat); + + do { + if (AD1889_READW(dev, AD_ACIC) & 0x8000) /* Ready */ + break; + WAIT_10MS(); + retry--; + } while (retry > 0); + + if (!retry) { + printk(KERN_ERR "ad1889_aclink_reset: codec is not ready [0x%x]\n", + AD1889_READW(dev, AD_ACIC)); + return -EBUSY; + } + + /* TODO reset AC97 codec */ + /* TODO set wave/adc pci ctrl status */ + + stat = AD1889_READW(dev, AD_ACIC); + stat |= 0x0004; /* Audio Stream Output Enable */ + AD1889_WRITEW(dev, AD_ACIC, stat); + return 0; +} + +/************************* PCI interfaces ****************************** */ +/* PCI device table */ +static struct pci_device_id ad1889_id_tbl[] __devinitdata = { + { PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, (unsigned long)DEVNAME }, + { }, +}; +MODULE_DEVICE_TABLE(pci, ad1889_id_tbl); + +static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 stat; + ad1889_dev_t *dev = (ad1889_dev_t *)dev_id; + + stat = AD1889_READL(dev, AD_DMADISR); + + /* clear ISR */ + AD1889_WRITEL(dev, AD_DMADISR, stat); + + if (stat & 0x8) { /* WAVI */ + DBG("WAV interrupt\n"); + dev->stats.wav_intrs++; + if (dev->state[AD_WAV_STATE].dmabuf.ready) { + ad1889_stop_wav(&dev->state[AD_WAV_STATE]); /* clean up */ + ad1889_start_wav(&dev->state[AD_WAV_STATE]); /* start new */ + } + + } + + if ((stat & 0x2) && dev->state[AD_ADC_STATE].dmabuf.ready) { /* ADCI */ + DBG("ADC interrupt\n"); + dev->stats.adc_intrs++; + } + if(stat) + return IRQ_HANDLED; + return IRQ_NONE; +} + +static void ad1889_initcfg(ad1889_dev_t *dev) +{ + u16 tmp; + + /* make sure the interrupt bits are setup the way we want */ + tmp = AD1889_READW(dev, AD_DMAWAVCTRL); + tmp &= ~0x00ff; /* flat dma, no sg, mask out the intr bits */ + tmp |= 0x0004; /* intr on count, loop */ + AD1889_WRITEW(dev, AD_DMAWAVCTRL, tmp); + + /* unmute... */ + tmp = AD1889_READW(dev, AD_DSWADA); + tmp &= ~0x8080; + AD1889_WRITEW(dev, AD_DSWADA, tmp); +} + +static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + int err; + ad1889_dev_t *dev; + unsigned long bar; + struct proc_dir_entry *proc_root = NULL; + + if ((err = pci_enable_device(pcidev)) != 0) { + printk(KERN_ERR DEVNAME ": pci_enable_device failed\n"); + return err; + } + + pci_set_master(pcidev); + if ((dev = ad1889_alloc_dev(pcidev)) == NULL) { + printk(KERN_ERR DEVNAME ": cannot allocate memory for device\n"); + return -ENOMEM; + } + pci_set_drvdata(pcidev, dev); + bar = pci_resource_start(pcidev, 0); + + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM)) { + printk(KERN_ERR DEVNAME ": memory region not assigned\n"); + goto err_free_mem; + } + + if (request_irq(pcidev->irq, ad1889_interrupt, SA_SHIRQ, DEVNAME, dev) != 0) { + printk(KERN_ERR DEVNAME ": unable to request interrupt\n"); + goto err_free_mem; + } + + request_mem_region(bar, AD_DSIOMEMSIZE, DEVNAME); + dev->regbase = (unsigned long)ioremap_nocache(bar, AD_DSIOMEMSIZE); + + printk(KERN_INFO DEVNAME ": %s at 0x%lx IRQ %d\n", + (char *)ent->driver_data, dev->regbase, pcidev->irq); + + if (ad1889_aclink_reset(pcidev) != 0) + goto err_free_mem; + + /* register /dev/dsp */ + if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1)) < 0) { + printk(KERN_ERR DEVNAME ": cannot register /dev/dsp\n"); + goto err_free_irq; + } + + if ((err = ad1889_ac97_init(dev, 0)) != 0) + goto err_free_dsp; + + if (((proc_root = proc_mkdir("driver/ad1889", 0)) == NULL) || + create_proc_read_entry("ac97", S_IFREG|S_IRUGO, proc_root, ac97_read_proc, dev->ac97_codec) == NULL || + create_proc_read_entry("info", S_IFREG|S_IRUGO, proc_root, ad1889_read_proc, dev) == NULL) + goto err_free_dsp; + + ad1889_initcfg(dev); + + //DBG(DEVNAME ": Driver initialization done!\n"); + + ad1889_dev = dev; + + return 0; + +err_free_dsp: + unregister_sound_dsp(dev->dev_audio); + +err_free_irq: + free_irq(pcidev->irq, dev); + +err_free_mem: + ad1889_free_dev(dev); + pci_set_drvdata(pcidev, 0); + + return -ENODEV; +} + +static void __devexit ad1889_remove(struct pci_dev *pcidev) +{ + ad1889_dev_t *dev = pci_get_drvdata(pcidev); + + if (dev == NULL) return; + + unregister_sound_mixer(dev->ac97_codec->dev_mixer); + unregister_sound_dsp(dev->dev_audio); + free_irq(pcidev->irq, dev); + release_mem_region(dev->regbase, AD_DSIOMEMSIZE); + + /* any hw programming needed? */ + ad1889_free_dev(dev); +} + +MODULE_AUTHOR("Randolph Chung"); +MODULE_DESCRIPTION("Analog Devices AD1889 PCI Audio"); +MODULE_LICENSE("GPL"); + +static struct pci_driver ad1889_driver = { + name: DEVNAME, + id_table: ad1889_id_tbl, + probe: ad1889_probe, + remove: __devexit_p(ad1889_remove), +}; + +static int __init ad1889_init_module(void) +{ + return pci_module_init(&ad1889_driver); +} + +static void ad1889_exit_module(void) +{ + pci_unregister_driver(&ad1889_driver); + return; +} + +module_init(ad1889_init_module); +module_exit(ad1889_exit_module); diff -urN linux-2.5.75-bk1/sound/oss/ad1889.h linux-2.5.75-bk2/sound/oss/ad1889.h --- linux-2.5.75-bk1/sound/oss/ad1889.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.75-bk2/sound/oss/ad1889.h 2003-07-12 04:37:48.000000000 -0700 @@ -0,0 +1,134 @@ +#ifndef _AD1889_H_ +#define _AD1889_H_ + +#define AD_DSWSMC 0x00 /* DMA input wave/syn mixer control */ +#define AD_DSRAMC 0x02 /* DMA output resamp/ADC mixer control */ +#define AD_DSWADA 0x04 /* DMA input wave attenuation */ +#define AD_DSSYDA 0x06 /* DMA input syn attentuation */ +#define AD_DSWAS 0x08 /* wave input sample rate */ +#define AD_DSRES 0x0a /* resampler output sample rate */ +#define AD_DSCCS 0x0c /* chip control/status */ + +#define AD_DMARESBA 0x40 /* RES base addr */ +#define AD_DMARESCA 0x44 /* RES current addr */ +#define AD_DMARESBC 0x48 /* RES base cnt */ +#define AD_DMARESCC 0x4c /* RES current count */ +#define AD_DMAADCBA 0x50 /* ADC */ +#define AD_DMAADCCA 0x54 +#define AD_DMAADCBC 0x58 +#define AD_DMAADCCC 0x5c +#define AD_DMASYNBA 0x60 /* SYN */ +#define AD_DMASYNCA 0x64 +#define AD_DMASYNBC 0x68 +#define AD_DMASYNCC 0x6c +#define AD_DMAWAVBA 0x70 /* WAV */ +#define AD_DMAWAVCA 0x74 +#define AD_DMAWAVBC 0x78 +#define AD_DMAWAVCC 0x7c +#define AD_DMARESICC 0x80 /* RES interrupt current count */ +#define AD_DMARESIBC 0x84 /* RES interrupt base count */ +#define AD_DMAADCICC 0x88 /* ADC interrupt current count */ +#define AD_DMAADCIBC 0x8c /* ADC interrupt base count */ +#define AD_DMASYNICC 0x90 /* SYN interrupt current count */ +#define AD_DMASYNIBC 0x94 /* SYN interrupt base count */ +#define AD_DMAWAVICC 0x98 /* WAV interrupt current count */ +#define AD_DMAWAVIBC 0x9c /* WAV interrupt base count */ +#define AD_DMARESCTRL 0xa0 /* RES PCI control/status */ +#define AD_DMAADCCTRL 0xa8 /* RES PCI control/status */ +#define AD_DMASYNCTRL 0xb0 /* RES PCI control/status */ +#define AD_DMAWAVCTRL 0xb8 /* RES PCI control/status */ +#define AD_DMADISR 0xc0 /* PCI DMA intr status */ +#define AD_DMACHSS 0xc4 /* PCI DMA channel stop status */ + +#define AD_GPIOIPC 0xc8 /* IO port ctrl */ +#define AD_GPIOOP 0xca /* IO output status */ +#define AD_GPIOIP 0xcc /* IO input status */ + +/* AC97 registers, 0x100 - 0x17f; see ac97.h */ +#define AD_ACIC 0x180 /* AC Link interface ctrl */ + +/* OPL3; BAR1 */ +#define AD_OPLM0AS 0x00 /* Music0 address/status */ +#define AD_OPLM0DATA 0x01 /* Music0 data */ +#define AD_OPLM1A 0x02 /* Music1 address */ +#define AD_OPLM1DATA 0x03 /* Music1 data */ +/* 0x04-0x0f reserved */ + +/* MIDI; BAR2 */ +#define AD_MIDA 0x00 /* MIDI data */ +#define AD_MISC 0x01 /* MIDI status/cmd */ +/* 0x02-0xff reserved */ + +#define AD_DSIOMEMSIZE 512 +#define AD_OPLMEMSIZE 16 +#define AD_MIDIMEMSIZE 16 + +#define AD_WAV_STATE 0 +#define AD_ADC_STATE 1 +#define AD_MAX_STATES 2 + +#define DMA_SIZE (128*1024) + +#define DMA_FLAG_MAPPED 1 + +struct ad1889_dev; + +typedef struct ad1889_state { + struct ad1889_dev *card; + + mode_t open_mode; + struct dmabuf { + unsigned int rate; + unsigned char fmt, enable; + + /* buf management */ + size_t rawbuf_size; + void *rawbuf; + dma_addr_t dma_handle; /* mapped address */ + unsigned long dma_len; /* number of bytes mapped */ + + /* indexes into rawbuf for setting up DMA engine */ + volatile unsigned long rd_ptr, wr_ptr; + + wait_queue_head_t wait; /* to wait for buf servicing */ + + /* OSS bits */ + unsigned int mapped:1; + unsigned int ready:1; + unsigned int ossfragshift; + int ossmaxfrags; + unsigned int subdivision; + } dmabuf; + + struct semaphore sem; +} ad1889_state_t; + +typedef struct ad1889_dev { + unsigned long regbase; + struct pci_dev *pci; + + spinlock_t lock; + + int dev_audio; + + /* states; one per channel; right now only WAV and ADC */ + struct ad1889_state state[AD_MAX_STATES]; + + /* AC97 codec */ + struct ac97_codec *ac97_codec; + u16 ac97_features; + + /* debugging stuff */ + struct stats { + unsigned int wav_intrs, adc_intrs; + unsigned int blocks, underrun, error; + } stats; +} ad1889_dev_t; + +typedef struct ad1889_reg { + const char *name; + int offset; + int width; +} ad1889_reg_t; + +#endif diff -urN linux-2.5.75-bk1/sound/oss/ali5455.c linux-2.5.75-bk2/sound/oss/ali5455.c --- linux-2.5.75-bk1/sound/oss/ali5455.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.75-bk2/sound/oss/ali5455.c 2003-07-12 04:37:48.000000000 -0700 @@ -0,0 +1,3735 @@ +/* + * ALI ali5455 and friends ICH driver for Linux + * LEI HU + * + * Built from: + * drivers/sound/i810_audio + * + * The ALi 5455 is similar but not quite identical to the Intel ICH + * series of controllers. Its easier to keep the driver separated from + * the i810 driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * ALi 5455 theory of operation + * + * The chipset provides three DMA channels that talk to an AC97 + * CODEC (AC97 is a digital/analog mixer standard). At its simplest + * you get 48Khz audio with basic volume and mixer controls. At the + * best you get rate adaption in the codec. We set the card up so + * that we never take completion interrupts but instead keep the card + * chasing its tail around a ring buffer. This is needed for mmap + * mode audio and happens to work rather well for non-mmap modes too. + * + * The board has one output channel for PCM audio (supported) and + * a stereo line in and mono microphone input. Again these are normally + * locked to 48Khz only. Right now recording is not finished. + * + * There is no midi support, no synth support. Use timidity. To get + * esd working you need to use esd -r 48000 as it won't probe 48KHz + * by default. mpg123 can't handle 48Khz only audio so use xmms. + * + * If you need to force a specific rate set the clocking= option + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PCI_DEVICE_ID_ALI_5455 +#define PCI_DEVICE_ID_ALI_5455 0x5455 +#endif + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +static int strict_clocking = 0; +static unsigned int clocking = 0; +static unsigned int codec_pcmout_share_spdif_locked = 0; +static unsigned int codec_independent_spdif_locked = 0; +static unsigned int controller_pcmout_share_spdif_locked = 0; +static unsigned int controller_independent_spdif_locked = 0; +static unsigned int globel = 0; + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 +#define CODEC_SPDIFOUT_RUNNING 8 +#define CONTROLLER_SPDIFOUT_RUNNING 4 + +#define SPDIF_ENABLE_OUTPUT 4 /* bits 0,1 are PCM */ + +#define ALI5455_FMT_16BIT 1 +#define ALI5455_FMT_STEREO 2 +#define ALI5455_FMT_MASK 3 + +#define SPDIF_ON 0x0004 +#define SURR_ON 0x0010 +#define CENTER_LFE_ON 0x0020 +#define VOL_MUTED 0x8000 + + +#define ALI_SPDIF_OUT_CH_STATUS 0xbf +/* the 810's array of pointers to data buffers */ + +struct sg_item { +#define BUSADDR_MASK 0xFFFFFFFE + u32 busaddr; +#define CON_IOC 0x80000000 /* interrupt on completion */ +#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */ +#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */ + u32 control; +}; + +/* an instance of the ali channel */ +#define SG_LEN 32 +struct ali_channel { + /* these sg guys should probably be allocated + separately as nocache. Must be 8 byte aligned */ + struct sg_item sg[SG_LEN]; /* 32*8 */ + u32 offset; /* 4 */ + u32 port; /* 4 */ + u32 used; + u32 num; +}; + +/* + * we have 3 separate dma engines. pcm in, pcm out, and mic. + * each dma engine has controlling registers. These goofy + * names are from the datasheet, but make it easy to write + * code while leafing through it. + */ + +#define ENUM_ENGINE(PRE,DIG) \ +enum { \ + PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ + PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ + PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ + PRE##_SR = 0x##DIG##6, /* Status Register */ \ + PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \ + PRE##_CR = 0x##DIG##b /* Control Register */ \ +} + +ENUM_ENGINE(OFF, 0); /* Offsets */ +ENUM_ENGINE(PI, 4); /* PCM In */ +ENUM_ENGINE(PO, 5); /* PCM Out */ +ENUM_ENGINE(MC, 6); /* Mic In */ +ENUM_ENGINE(CODECSPDIFOUT, 7); /* CODEC SPDIF OUT */ +ENUM_ENGINE(CONTROLLERSPDIFIN, A); /* CONTROLLER SPDIF In */ +ENUM_ENGINE(CONTROLLERSPDIFOUT, B); /* CONTROLLER SPDIF OUT */ + + +enum { + ALI_SCR = 0x00, /* System Control Register */ + ALI_SSR = 0x04, /* System Status Register */ + ALI_DMACR = 0x08, /* DMA Control Register */ + ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */ + ALI_INTERFACECR = 0x10, /* Interface Control Register */ + ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */ + ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */ + ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */ + ALI_CPR = 0x20, /* Command Port Register */ + ALI_SPR = 0x24, /* Status Port Register */ + ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */ + ALI_TTSR = 0x30, /* Transmit Tag Slot Register */ + ALI_RTSR = 0x34, /* Receive Tag Slot Register */ + ALI_CSPSR = 0x38, /* Command/Status Port Status Register */ + ALI_CAS = 0x3c, /* Codec Write Semaphore Register */ + ALI_SPDIFCSR = 0xf8, /* spdif channel status register */ + ALI_SPDIFICS = 0xfc /* spdif interface control/status */ +}; + +// x-status register(x:pcm in ,pcm out, mic in,) +/* interrupts for a dma engine */ +#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */ +#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */ +#define DMA_INT_LVI (1<<2) /* last valid done */ +#define DMA_INT_CELV (1<<1) /* last valid is current */ +#define DMA_INT_DCH (1) /* DMA Controller Halted (happens on LVI interrupts) */ //not eqult intel +#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) + +/* interrupts for the whole chip */// by interrupt status register finish + +#define INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */ +#define INT_SPDIFIN (1<<22) +#define INT_CODECSPDIFOUT (1<<19) +#define INT_MICIN (1<<18) +#define INT_PCMOUT (1<<17) +#define INT_PCMIN (1<<16) +#define INT_CPRAIS (1<<7) +#define INT_SPRAIS (1<<5) +#define INT_GPIO (1<<1) +#define INT_MASK (INT_SPDIFOUT|INT_CODECSPDIFOUT|INT_MICIN|INT_PCMOUT|INT_PCMIN) + +#define DRIVER_VERSION "0.02ac" + +/* magic numbers to protect our data structures */ +#define ALI5455_CARD_MAGIC 0x5072696E /* "Prin" */ +#define ALI5455_STATE_MAGIC 0x63657373 /* "cess" */ +#define ALI5455_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ +#define NR_HW_CH 5 //I think 5 channel + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */ +/* stream at a minimum for this card to be happy */ +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */ +/* values are one less than might be expected */ +static const unsigned sample_shift[] = { -1, 0, 0, 1 }; + +#define ALI5455 +static char *card_names[] = { + "ALI 5455" +}; + +static struct pci_device_id ali_pci_tbl[] __initdata = { + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5455, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI5455}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, ali_pci_tbl); + +#ifdef CONFIG_PM +#define PM_SUSPENDED(card) (card->pm_suspended) +#else +#define PM_SUSPENDED(card) (0) +#endif + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct ali_state { + unsigned int magic; + struct ali_card *card; /* Card info */ + + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + wait_queue_head_t open_wait; + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + +#ifdef CONFIG_PM + unsigned int pm_saved_dac_rate, pm_saved_adc_rate; +#endif + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable, trigger; + + /* hardware channel */ + struct ali_channel *read_channel; + struct ali_channel *write_channel; + struct ali_channel *codec_spdifout_channel; + struct ali_channel *controller_spdifout_channel; + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, updated by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be consumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + unsigned error; /* number of over/underruns */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + /* what the hardware uses */ + unsigned dmasize; + unsigned fragsize; + unsigned fragsamples; + + /* what we tell the user to expect */ + unsigned userfrags; + unsigned userfragsize; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned update_flag; + unsigned ossfragsize; + unsigned ossmaxfrags; + unsigned subdivision; + } dmabuf; +}; + + +struct ali_card { + struct ali_channel channel[5]; + unsigned int magic; + + /* We keep ali5455 cards in a linked list */ + struct ali_card *next; + + /* The ali has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + spinlock_t ac97_lock; + + /* PCI device stuff */ + struct pci_dev *pci_dev; + u16 pci_id; +#ifdef CONFIG_PM + u16 pm_suspended; + u32 pm_save_state[64 / sizeof(u32)]; + int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97]; +#endif + /* soundcore stuff */ + int dev_audio; + + /* structures for abstraction of hardware facilities, codecs, banks and channels */ + struct ac97_codec *ac97_codec[NR_AC97]; + struct ali_state *states[NR_HW_CH]; + + u16 ac97_features; + u16 ac97_status; + u16 channels; + + /* hardware resources */ + unsigned long iobase; + + u32 irq; + + /* Function support */ + struct ali_channel *(*alloc_pcm_channel) (struct ali_card *); + struct ali_channel *(*alloc_rec_pcm_channel) (struct ali_card *); + struct ali_channel *(*alloc_rec_mic_channel) (struct ali_card *); + struct ali_channel *(*alloc_codec_spdifout_channel) (struct ali_card *); + struct ali_channel *(*alloc_controller_spdifout_channel) (struct ali_card *); + void (*free_pcm_channel) (struct ali_card *, int chan); + + /* We have a *very* long init time possibly, so use this to block */ + /* attempts to open our devices before we are ready (stops oops'es) */ + int initializing; +}; + + +static struct ali_card *devs = NULL; + +static int ali_open_mixdev(struct inode *inode, struct file *file); +static int ali_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg); +static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct ali_channel *ali_alloc_pcm_channel(struct ali_card *card) +{ + if (card->channel[1].used == 1) + return NULL; + card->channel[1].used = 1; + return &card->channel[1]; +} + +static struct ali_channel *ali_alloc_rec_pcm_channel(struct ali_card *card) +{ + if (card->channel[0].used == 1) + return NULL; + card->channel[0].used = 1; + return &card->channel[0]; +} + +static struct ali_channel *ali_alloc_rec_mic_channel(struct ali_card *card) +{ + if (card->channel[2].used == 1) + return NULL; + card->channel[2].used = 1; + return &card->channel[2]; +} + +static struct ali_channel *ali_alloc_codec_spdifout_channel(struct ali_card *card) +{ + if (card->channel[3].used == 1) + return NULL; + card->channel[3].used = 1; + return &card->channel[3]; +} + +static struct ali_channel *ali_alloc_controller_spdifout_channel(struct ali_card *card) +{ + if (card->channel[4].used == 1) + return NULL; + card->channel[4].used = 1; + return &card->channel[4]; +} +static void ali_free_pcm_channel(struct ali_card *card, int channel) +{ + card->channel[channel].used = 0; +} + + +//add support codec spdif out +static int ali_valid_spdif_rate(struct ac97_codec *codec, int rate) +{ + unsigned long id = 0L; + + id = (ali_ac97_get(codec, AC97_VENDOR_ID1) << 16); + id |= ali_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; + switch (id) { + case 0x41445361: /* AD1886 */ + if (rate == 48000) { + return 1; + } + break; + case 0x414c4720: /* ALC650 */ + if (rate == 48000) { + return 1; + } + break; + default: /* all other codecs, until we know otherwiae */ + if (rate == 48000 || rate == 44100 || rate == 32000) { + return 1; + } + break; + } + return (0); +} + +/* ali_set_spdif_output + * + * Configure the S/PDIF output transmitter. When we turn on + * S/PDIF, we turn off the analog output. This may not be + * the right thing to do. + * + * Assumptions: + * The DSP sample rate must already be set to a supported + * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. + */ +static void ali_set_spdif_output(struct ali_state *state, int slots, + int rate) +{ + int vol; + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 4)) { + state->card->ac97_status &= ~SPDIF_ON; + } else { + if (slots == -1) { /* Turn off S/PDIF */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + + /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ + if (!(state->card->ac97_status & VOL_MUTED)) { + aud_reg = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); + ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, + (aud_reg & ~VOL_MUTED)); + } + state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); + return; + } + + vol = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); + state->card->ac97_status = vol & VOL_MUTED; + + /* Set S/PDIF transmitter sample rate */ + aud_reg = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + switch (rate) { + case 32000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; + break; + case 44100: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; + break; + case 48000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; + break; + default: + /* turn off S/PDIF */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + + ali_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); + + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_SPDIF; + ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + + aud_reg = ali_ac97_get(codec, AC97_POWER_CONTROL); + aud_reg |= 0x0002; + ali_ac97_set(codec, AC97_POWER_CONTROL, aud_reg); + udelay(1); + + state->card->ac97_status |= SPDIF_ON; + + /* Check to make sure the configuration is valid */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + if (!(aud_reg & 0x0400)) { + /* turn off S/PDIF */ + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + if (codec_independent_spdif_locked > 0) { + aud_reg = ali_ac97_get(codec, 0x6a); + ali_ac97_set(codec, 0x6a, (aud_reg & 0xefff)); + } + /* Mute the analog output */ + /* Should this only mute the PCM volume??? */ + } +} + +/* ali_set_dac_channels + * + * Configure the codec's multi-channel DACs + * + * The logic is backwards. Setting the bit to 1 turns off the DAC. + * + * What about the ICH? We currently configure it using the + * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, + * does that imply that we want the ICH set to support + * these channels? + * + * TODO: + * vailidate that the codec really supports these DACs + * before turning them on. + */ +static void ali_set_dac_channels(struct ali_state *state, int channel) +{ + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; + state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); + + switch (channel) { + case 2: /* always enabled */ + break; + case 4: + aud_reg &= ~AC97_EA_PRJ; + state->card->ac97_status |= SURR_ON; + break; + case 6: + aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); + state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; + break; + default: + break; + } + ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + +} + +/* set playback sample rate */ +static unsigned int ali_set_dac_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + + rate = (rate * clocking) / 48000; + + if (strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000) / clocking; + } + + new_rate = ac97_set_dac_rate(codec, rate); + if (new_rate != rate) { + dmabuf->rate = (new_rate * 48000) / clocking; + } + rate = new_rate; + return dmabuf->rate; +} + +/* set recording sample rate */ +static unsigned int ali_set_adc_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + + rate = (rate * clocking) / 48000; + if (strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000) / clocking; + } + + new_rate = ac97_set_adc_rate(codec, rate); + + if (new_rate != rate) { + dmabuf->rate = (new_rate * 48000) / clocking; + rate = new_rate; + } + return dmabuf->rate; +} + +/* set codec independent spdifout sample rate */ +static unsigned int ali_set_codecspdifout_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + return dmabuf->rate; +} + +/* set controller independent spdif out function sample rate */ +static void ali_set_spdifout_rate(struct ali_state *state, + unsigned int rate) +{ + unsigned char ch_st_sel; + unsigned short status_rate; + + switch (rate) { + case 44100: + status_rate = 0; + break; + case 32000: + status_rate = 0x300; + break; + case 48000: + default: + status_rate = 0x200; + break; + } + + ch_st_sel = inb(state->card->iobase + ALI_SPDIFICS) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out + + ch_st_sel |= 0x80; //select right + outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); + outb(status_rate | 0x20, (state->card->iobase + ALI_SPDIFCSR + 2)); + + ch_st_sel &= (~0x80); //select left + outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); + outw(status_rate | 0x10, (state->card->iobase + ALI_SPDIFCSR + 2)); +} + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +static inline unsigned ali_get_dma_addr(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int civ, offset, port, port_picb; + unsigned int data; + + if (!dmabuf->enable) + return 0; + + if (rec == 1) + port = state->card->iobase + dmabuf->read_channel->port; + else if (rec == 2) + port = state->card->iobase + dmabuf->codec_spdifout_channel->port; + else if (rec == 3) + port = state->card->iobase + dmabuf->controller_spdifout_channel->port; + else + port = state->card->iobase + dmabuf->write_channel->port; + + port_picb = port + OFF_PICB; + + do { + civ = inb(port + OFF_CIV) & 31; + offset = inw(port_picb); + /* Must have a delay here! */ + if (offset == 0) + udelay(1); + + /* Reread both registers and make sure that that total + * offset from the first reading to the second is 0. + * There is an issue with SiS hardware where it will count + * picb down to 0, then update civ to the next value, + * then set the new picb to fragsize bytes. We can catch + * it between the civ update and the picb update, making + * it look as though we are 1 fragsize ahead of where we + * are. The next to we get the address though, it will + * be back in thdelay is more than long enough + * that we won't have to worry about the chip still being + * out of sync with reality ;-) + */ + } while (civ != (inb(port + OFF_CIV) & 31) || offset != inw(port_picb)); + + data = ((civ + 1) * dmabuf->fragsize - (2 * offset)) % dmabuf->dmasize; + if (inw(port_picb) == 0) + data -= 2048; + + return data; +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + + outl((1 << 18) | (1 << 16), card->iobase + ALI_DMACR); + udelay(1); + + outb(0, card->iobase + PI_CR); + while (inb(card->iobase + PI_CR) != 0); + + // now clear any latent interrupt bits (like the halt bit) + outb(inb(card->iobase + PI_SR) | 0x001e, card->iobase + PI_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMIN, card->iobase + ALI_INTERRUPTSR); +} + +static void stop_adc(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_adc(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (dmabuf->count < dmabuf->dmasize && dmabuf->ready + && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->enable |= ADC_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + PI_CR); + if (state->card->channel[0].used == 1) + outl(1, state->card->iobase + ALI_DMACR); // DMA CONTROL REGISTRER + udelay(100); + if (state->card->channel[2].used == 1) + outl((1 << 2), state->card->iobase + ALI_DMACR); //DMA CONTROL REGISTER + udelay(100); + } +} + +static void start_adc(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __start_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + outl(0x00020000, card->iobase + 0x08); + outb(0, card->iobase + PO_CR); + while (inb(card->iobase + PO_CR) != 0) + cpu_relax(); + + outb(inb(card->iobase + PO_SR) | 0x001e, card->iobase + PO_SR); + + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMOUT, card->iobase + ALI_INTERRUPTSR); +} + +static void stop_dac(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_dac(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->enable |= DAC_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + PO_CR); + outl((1 << 1), state->card->iobase + 0x08); //dma control register + } +} + +static void start_dac(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __start_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop codec and controller spdif out (lock held) */ +static inline void __stop_spdifout(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + if (codec_independent_spdif_locked > 0) { + dmabuf->enable &= ~CODEC_SPDIFOUT_RUNNING; + outl((1 << 19), card->iobase + 0x08); + outb(0, card->iobase + CODECSPDIFOUT_CR); + + while (inb(card->iobase + CODECSPDIFOUT_CR) != 0) + cpu_relax(); + + outb(inb(card->iobase + CODECSPDIFOUT_SR) | 0x001e, card->iobase + CODECSPDIFOUT_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_CODECSPDIFOUT, card->iobase + ALI_INTERRUPTSR); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->enable &= ~CONTROLLER_SPDIFOUT_RUNNING; + outl((1 << 23), card->iobase + 0x08); + outb(0, card->iobase + CONTROLLERSPDIFOUT_CR); + while (inb(card->iobase + CONTROLLERSPDIFOUT_CR) != 0) + cpu_relax(); + outb(inb(card->iobase + CONTROLLERSPDIFOUT_SR) | 0x001e, card->iobase + CONTROLLERSPDIFOUT_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_SPDIFOUT, card->iobase + ALI_INTERRUPTSR); + } + } +} + +static void stop_spdifout(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_spdifout(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_spdifout(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + if (codec_independent_spdif_locked > 0) { + dmabuf->enable |= CODEC_SPDIFOUT_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + CODECSPDIFOUT_CR); + outl((1 << 3), state->card->iobase + 0x08); //dma control register + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->enable |= CONTROLLER_SPDIFOUT_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + CONTROLLERSPDIFOUT_CR); + outl((1 << 7), state->card->iobase + 0x08); //dma control register + } + } + } +} + +static void start_spdifout(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __start_spdifout(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback , recording,spdif out buffer should be allocated separately */ +static int alloc_dmabuf(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf = NULL; + int order, size; + struct page *page, *pend; + + /* If we don't have any oss frag params, then use our default ones */ + if (dmabuf->ossmaxfrags == 0) + dmabuf->ossmaxfrags = 4; + if (dmabuf->ossfragsize == 0) + dmabuf->ossfragsize = (PAGE_SIZE << DMABUF_DEFAULTORDER) / dmabuf->ossmaxfrags; + size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + + if (dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) + return 0; + /* alloc enough to satisfy the oss params */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + if ((PAGE_SIZE << order) > size) + continue; + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + } + if (!rawbuf) + return -ENOMEM; + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(rawbuf); page <= pend; page++) + SetPageReserved(page); + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct page *page, *pend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) + ClearPageReserved(page); + pci_free_consistent(state->card->pci_dev, + PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_handle); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct ali_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_channel *c = NULL; + struct sg_item *sg; + unsigned long flags; + int ret; + unsigned fragint; + int i; + + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + + dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer, let alloc_dmabuf determine if we are already + * allocated well enough or if we should replace the current buffer + * (assuming one is already allocated, if it isn't, then allocate it). + */ + if ((ret = alloc_dmabuf(state))) + return ret; + + /* FIXME: figure out all this OSS fragment stuff */ + /* I did, it now does what it should according to the OSS API. DL */ + /* We may not have realloced our dmabuf, but the fragment size to + * fragment number ratio may have changed, so go ahead and reprogram + * things + */ + + dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; + dmabuf->numfrag = SG_LEN; + dmabuf->fragsize = dmabuf->dmasize / dmabuf->numfrag; + dmabuf->fragsamples = dmabuf->fragsize >> 1; + dmabuf->userfragsize = dmabuf->ossfragsize; + dmabuf->userfrags = dmabuf->dmasize / dmabuf->ossfragsize; + + memset(dmabuf->rawbuf, 0, dmabuf->dmasize); + + if (dmabuf->ossmaxfrags == 4) { + fragint = 8; + dmabuf->fragshift = 2; + } else if (dmabuf->ossmaxfrags == 8) { + fragint = 4; + dmabuf->fragshift = 3; + } else if (dmabuf->ossmaxfrags == 16) { + fragint = 2; + dmabuf->fragshift = 4; + } else { + fragint = 1; + dmabuf->fragshift = 5; + } + /* + * Now set up the ring + */ + + if (rec == 1) + c = dmabuf->read_channel; + else if (rec == 2) + c = dmabuf->codec_spdifout_channel; + else if (rec == 3) + c = dmabuf->controller_spdifout_channel; + else if (rec == 0) + c = dmabuf->write_channel; + if (c != NULL) { + sg = &c->sg[0]; + /* + * Load up 32 sg entries and take an interrupt at half + * way (we might want more interrupts later..) + */ + for (i = 0; i < dmabuf->numfrag; i++) { + sg->busaddr = + virt_to_bus(dmabuf->rawbuf + + dmabuf->fragsize * i); + // the card will always be doing 16bit stereo + sg->control = dmabuf->fragsamples; + sg->control |= CON_BUFPAD; //I modify + // set us up to get IOC interrupts as often as needed to + // satisfy numfrag requirements, no more + if (((i + 1) % fragint) == 0) { + sg->control |= CON_IOC; + } + sg++; + } + spin_lock_irqsave(&state->card->lock, flags); + outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ + outl(virt_to_bus(&c->sg[0]), state->card->iobase + c->port + OFF_BDBAR); + outb(0, state->card->iobase + c->port + OFF_CIV); + outb(0, state->card->iobase + c->port + OFF_LVI); + spin_unlock_irqrestore(&state->card->lock, flags); + } + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + return 0; +} + +static void __ali_update_lvi(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int x, port; + port = state->card->iobase; + if (rec == 1) + port += dmabuf->read_channel->port; + else if (rec == 2) + port += dmabuf->codec_spdifout_channel->port; + else if (rec == 3) + port += dmabuf->controller_spdifout_channel->port; + else if (rec == 0) + port += dmabuf->write_channel->port; + /* if we are currently stopped, then our CIV is actually set to our + * *last* sg segment and we are ready to wrap to the next. However, + * if we set our LVI to the last sg segment, then it won't wrap to + * the next sg segment, it won't even get a start. So, instead, when + * we are stopped, we set both the LVI value and also we increment + * the CIV value to the next sg segment to be played so that when + * we call start_{dac,adc}, things will operate properly + */ + if (!dmabuf->enable && dmabuf->ready) { + if (rec && dmabuf->count < dmabuf->dmasize && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_adc(state); + while (! (inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else if (!rec && dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_dac(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else if (rec && dmabuf->count && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + if (codec_independent_spdif_locked > 0) { + // outb((inb(port+OFF_CIV))&31, port+OFF_LVI); + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_spdifout(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else { + if (controller_independent_spdif_locked > 0) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_spdifout(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } + } + } + } + + /* swptr - 1 is the tail of our transfer */ + x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize; + x /= dmabuf->fragsize; + outb(x, port + OFF_LVI); +} + +static void ali_update_lvi(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + if (!dmabuf->ready) + return; + spin_lock_irqsave(&state->card->lock, flags); + __ali_update_lvi(state, rec); + spin_unlock_irqrestore(&state->card->lock, flags); +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void ali_update_ptr(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned hwptr; + int diff; + + /* error handling and process wake up for DAC */ + if (dmabuf->enable == ADC_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 1); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + if (dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a read */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + PI_CIV) & 31) != (inb(state->card->iobase + PI_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on read\n"); + dmabuf->error++; + } + } + if (dmabuf->count > dmabuf->userfragsize) + wake_up(&dmabuf->wait); + } + /* error handling and process wake up for DAC */ + if (dmabuf->enable == DAC_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 0); + diff = + (dmabuf->dmasize + hwptr - + dmabuf->hwptr) % dmabuf->dmasize; +#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) + printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#endif + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + PO_CIV) & 31) != (inb(state->card->iobase + PO_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); + printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", + inb(state->card->iobase + PO_CIV) & 31, + inb(state->card->iobase + PO_LVI) & 31, + dmabuf->hwptr, + dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } + + /* error handling and process wake up for CODEC SPDIF OUT */ + if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 2); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); + printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", + inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31, + inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31, + dmabuf->hwptr, dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } + /* error handling and process wake up for CONTROLLER SPDIF OUT */ + if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 3); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31)) { + printk(KERN_WARNING + "ali_audio: DMA overrun on write\n"); + printk("ali_audio: CIV %d, LVI %d, hwptr %x, " + "count %d\n", + inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31, + inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31, + dmabuf->hwptr, dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } +} + +static inline int ali_get_free_write_space(struct + ali_state + *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int free; + ali_update_ptr(state); + // catch underruns during playback + if (dmabuf->count < 0) { + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + free = dmabuf->dmasize - dmabuf->count; + free -= (dmabuf->hwptr % dmabuf->fragsize); + if (free < 0) + return (0); + return (free); +} + +static inline int ali_get_available_read_data(struct + ali_state + *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int avail; + ali_update_ptr(state); + // catch overruns during record + if (dmabuf->count > dmabuf->dmasize) { + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + avail = dmabuf->count; + avail -= (dmabuf->hwptr % dmabuf->fragsize); + if (avail < 0) + return (0); + return (avail); +} + +static int drain_dac(struct ali_state *state, int signals_allowed) +{ + + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + if (!dmabuf->ready) + return 0; + if (dmabuf->mapped) { + stop_dac(state); + return 0; + } + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + if (count <= 0) + break; + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + if (!dmabuf->enable) { + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_update_lvi(state, 0); + } + if (signal_pending(current) && signals_allowed) { + break; + } + + /* It seems that we have to set the current state to + * TASK_INTERRUPTIBLE every time to make the process + * really go to sleep. This also has to be *after* the + * update_ptr() call because update_ptr is likely to + * do a wake_up() which will unset this before we ever + * try to sleep, resuling in a tight loop in this code + * instead of actually sleeping and waiting for an + * interrupt to wake us up! + */ + set_current_state(TASK_INTERRUPTIBLE); + /* + * set the timeout to significantly longer than it *should* + * take for the DAC to drain the DMA buffer + */ + tmo = (count * HZ) / (dmabuf->rate); + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR "ali_audio: drain_dac, dma timeout?\n"); + count = 0; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &wait); + if (count > 0 && signal_pending(current) && signals_allowed) + return -ERESTARTSYS; + stop_dac(state); + return 0; +} + + +static int drain_spdifout(struct ali_state *state, int signals_allowed) +{ + + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + if (!dmabuf->ready) + return 0; + if (dmabuf->mapped) { + stop_spdifout(state); + return 0; + } + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + if (count <= 0) + break; + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + if (!dmabuf->enable) { + if (codec_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 3); + } + } + } + if (signal_pending(current) && signals_allowed) { + break; + } + + /* It seems that we have to set the current state to + * TASK_INTERRUPTIBLE every time to make the process + * really go to sleep. This also has to be *after* the + * update_ptr() call because update_ptr is likely to + * do a wake_up() which will unset this before we ever + * try to sleep, resuling in a tight loop in this code + * instead of actually sleeping and waiting for an + * interrupt to wake us up! + */ + set_current_state(TASK_INTERRUPTIBLE); + /* + * set the timeout to significantly longer than it *should* + * take for the DAC to drain the DMA buffer + */ + tmo = (count * HZ) / (dmabuf->rate); + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR "ali_audio: drain_spdifout, dma timeout?\n"); + count = 0; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &wait); + if (count > 0 && signal_pending(current) && signals_allowed) + return -ERESTARTSYS; + stop_spdifout(state); + return 0; +} + +static void ali_channel_interrupt(struct ali_card *card) +{ + int i, count; + + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + struct ali_channel *c = NULL; + struct dmabuf *dmabuf; + unsigned long port = card->iobase; + u16 status; + if (!state) + continue; + if (!state->dmabuf.ready) + continue; + dmabuf = &state->dmabuf; + if (codec_independent_spdif_locked > 0) { + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + c = dmabuf->codec_spdifout_channel; + } + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + c = dmabuf->controller_spdifout_channel; + } else { + if (dmabuf->enable & DAC_RUNNING) { + c = dmabuf->write_channel; + } else if (dmabuf->enable & ADC_RUNNING) { + c = dmabuf->read_channel; + } else + continue; + } + } + port += c->port; + + status = inw(port + OFF_SR); + + if (status & DMA_INT_COMPLETE) { + /* only wake_up() waiters if this interrupt signals + * us being beyond a userfragsize of data open or + * available, and ali_update_ptr() does that for + * us + */ + ali_update_ptr(state); + } + + if (status & DMA_INT_LVI) { + ali_update_ptr(state); + wake_up(&dmabuf->wait); + + if (dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & ADC_RUNNING) + count = dmabuf->dmasize - dmabuf->count; + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + count = dmabuf->count; + else count = 0; + + if (count > 0) { + if (dmabuf->enable & DAC_RUNNING) + outl((1 << 1), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + outl((1 << 3), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + outl((1 << 7), state->card->iobase + ALI_DMACR); + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); + } + + } + if (!(status & DMA_INT_DCH)) { + ali_update_ptr(state); + wake_up(&dmabuf->wait); + if (dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & ADC_RUNNING) + count = dmabuf->dmasize - dmabuf->count; + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + count = dmabuf->count; + else + count = 0; + + if (count > 0) { + if (dmabuf->enable & DAC_RUNNING) + outl((1 << 1), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + outl((1 << 3), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + outl((1 << 7), state->card->iobase + ALI_DMACR); + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); + } + } + outw(status & DMA_INT_MASK, port + OFF_SR); + } +} + +static irqreturn_t ali_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ali_card *card = (struct ali_card *) dev_id; + u32 status; + u16 status2; + + spin_lock(&card->lock); + status = inl(card->iobase + ALI_INTERRUPTSR); + if (!(status & INT_MASK)) { + spin_unlock(&card->lock); + return IRQ_NONE; /* not for us */ + } + + if (codec_independent_spdif_locked > 0) { + if (globel == 0) { + globel += 1; + status2 = inw(card->iobase + 0x76); + outw(status2 | 0x000c, card->iobase + 0x76); + } else { + if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) + ali_channel_interrupt(card); + } + } else { + if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) + ali_channel_interrupt(card); + } + + /* clear 'em */ + outl(status & INT_MASK, card->iobase + ALI_INTERRUPTSR); + spin_unlock(&card->lock); + return IRQ_HANDLED; +} + +/* 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 ali_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state ? state->card : 0; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; + DECLARE_WAITQUEUE(waita, current); +#ifdef DEBUG2 + printk("ali_audio: ali_read called, count = %d\n", count); +#endif + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & DAC_RUNNING) + return -ENODEV; + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = card->alloc_rec_pcm_channel(card); + if (!dmabuf->read_channel) { + return -EBUSY; + } + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; + cnt = ali_get_available_read_data(state); + // this is to make the copy_to_user simpler below + if (cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&card->lock, flags); + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + /* + * Don't let us deadlock. The ADC won't start if + * dmabuf->trigger isn't set. A call to SETTRIGGER + * could have turned it off after we set it to on + * previously. + */ + dmabuf->trigger = PCM_ENABLE_INPUT; + /* + * This does three things. Updates LVI to be correct, + * makes sure the ADC is running, and updates the + * hwptr. + */ + ali_update_lvi(state, 1); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto done; + } + /* Set the timeout to how long it would take to fill + * two of our buffers. If we haven't been woke up + * by then, then we know something is wrong. + */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR + "ali_audio: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, + dmabuf->count, dmabuf->hwptr, + dmabuf->swptr); + /* a buffer overrun, we delay the recovery until next time the + while loop begin and we REALLY have space to record */ + } + if (signal_pending(current)) { + ret = ret ? ret : -ERESTARTSYS; + goto done; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto done; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } +done: + ali_update_lvi(state, 1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + return ret; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to + the soundcard. it is drained by the dma machine and filled by this loop. */ +static ssize_t ali_write(struct file *file, + const char *buffer, size_t count, loff_t * ppos) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state ? state->card : 0; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr = 0; + int cnt, x; + DECLARE_WAITQUEUE(waita, current); +#ifdef DEBUG2 + printk("ali_audio: ali_write called, count = %d\n", count); +#endif + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & ADC_RUNNING) + return -ENODEV; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->codec_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card); + if (!dmabuf->codec_spdifout_channel) + return -EBUSY; + } + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->controller_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card); + if (!dmabuf->controller_spdifout_channel) + return -EBUSY; + } + } else { + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = + card->alloc_pcm_channel(card); + if (!dmabuf->write_channel) + return -EBUSY; + } + } + } + + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) + return ret; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) + return ret; + } else { + + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + } + } + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -EAGAIN; + break; + } + continue; + } + + swptr = dmabuf->swptr; + cnt = ali_get_free_write_space(state); + /* Bound the maximum size to how much we can copy to the + * dma buffer before we hit the end. If we have more to + * copy then it will get done in a second pass of this + * loop starting from the beginning of the buffer. + */ + if (cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&state->card->lock, flags); +#ifdef DEBUG2 + printk(KERN_INFO + "ali_audio: ali_write: %d bytes available space\n", + cnt); +#endif + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + // There is data waiting to be played + /* + * Force the trigger setting since we would + * deadlock with it set any other way + */ + if (codec_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 3); + } else { + + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_update_lvi(state, 0); + } + } + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto ret; + } + /* Not strictly correct but works */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer underrun. And worse, there is + NOTHING we can do to prevent it. */ + + /* FIXME - do timeout handling here !! */ + + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto ret; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto ret; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + + dmabuf->swptr = swptr; + dmabuf->count += cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (swptr % dmabuf->fragsize) { + x = dmabuf->fragsize - (swptr % dmabuf->fragsize); + memset(dmabuf->rawbuf + swptr, '\0', x); + } +ret: + if (codec_independent_spdif_locked > 0) { + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + ali_update_lvi(state, 3); + } else { + ali_update_lvi(state, 0); + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int ali_poll(struct file *file, struct poll_table_struct + *wait) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned int mask = 0; + if (!dmabuf->ready) + return 0; + poll_wait(file, &dmabuf->wait, wait); + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) { + if (dmabuf->count >= (signed) dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE && (dmabuf->enable & (DAC_RUNNING|CODEC_SPDIFOUT_RUNNING|CONTROLLER_SPDIFOUT_RUNNING))) { + if ((signed) dmabuf->dmasize >= dmabuf->count + (signed) dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&state->card->lock, flags); + return mask; +} + +static int ali_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + int ret = -EINVAL; + unsigned long size; + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if (vma->vm_flags & VM_READ) { + if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if ((ret = prog_dmabuf(state, 0)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf), size, vma->vm_page_prot)) + goto out; + dmabuf->mapped = 1; + dmabuf->trigger = 0; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int ali_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_channel *c = NULL; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + unsigned int i_scr; + int val = 0, ret; + struct ac97_codec *codec = state->card->ac97_codec[0]; +#ifdef DEBUG + printk("ali_audio: ali_ioctl, arg=0x%x, cmd=", + arg ? *(int *) arg : 0); +#endif + switch (cmd) { + case OSS_GETVERSION: +#ifdef DEBUG + printk("OSS_GETVERSION\n"); +#endif + return put_user(SOUND_VERSION, (int *) arg); + case SNDCTL_DSP_RESET: +#ifdef DEBUG + printk("SNDCTL_DSP_RESET\n"); +#endif + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable == DAC_RUNNING) { + c = dmabuf->write_channel; + __stop_dac(state); + } + if (dmabuf->enable == ADC_RUNNING) { + c = dmabuf->read_channel; + __stop_adc(state); + } + if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + c = dmabuf->codec_spdifout_channel; + __stop_spdifout(state); + } + if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + c = dmabuf->controller_spdifout_channel; + __stop_spdifout(state); + } + if (c != NULL) { + outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ + outl(virt_to_bus(&c->sg[0]), + state->card->iobase + c->port + OFF_BDBAR); + outb(0, state->card->iobase + c->port + OFF_CIV); + outb(0, state->card->iobase + c->port + OFF_LVI); + } + + spin_unlock_irqrestore(&state->card->lock, flags); + synchronize_irq(state->card->pci_dev->irq); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + return 0; + case SNDCTL_DSP_SYNC: +#ifdef DEBUG + printk("SNDCTL_DSP_SYNC\n"); +#endif + if (codec_independent_spdif_locked > 0) { + if (dmabuf->enable != CODEC_SPDIFOUT_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_spdifout(state, 1))) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->enable != + CONTROLLER_SPDIFOUT_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_spdifout(state, 1))) + return val; + } else { + if (dmabuf->enable != DAC_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_dac(state, 1))) + return val; + } + } + dmabuf->total_bytes = 0; + return 0; + case SNDCTL_DSP_SPEED: /* set smaple rate */ +#ifdef DEBUG + printk("SNDCTL_DSP_SPEED\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + if ((state->card->ac97_status & SPDIF_ON)) { /* S/PDIF Enabled */ + /* RELTEK ALC650 only support 48000, need to check that */ + if (ali_valid_spdif_rate(codec, val)) { + if (codec_independent_spdif_locked > 0) { + ali_set_spdif_output(state, -1, 0); + stop_spdifout(state); + dmabuf->ready = 0; + /* I add test codec independent spdif out */ + spin_lock_irqsave(&state->card->lock, flags); + ali_set_codecspdifout_rate(state, val); // I modified + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + i_scr = inl(state->card->iobase + ALI_SCR); + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr&0x00300000) == 0x00200000) + { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } else { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } + } + } + + if (!(state->card->ac97_status & SPDIF_ON)) { + val = dmabuf->rate; + } + } else { + if (controller_independent_spdif_locked > 0) + { + stop_spdifout(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_spdifout_rate(state, controller_independent_spdif_locked); + spin_unlock_irqrestore(&state->card->lock, flags); + } else { + /* Set DAC rate */ + ali_set_spdif_output(state, -1, 0); + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, val); + if (!(state->card->ac97_status & SPDIF_ON)) + { + val = dmabuf->rate; + } + } + } + } else { /* Not a valid rate for S/PDIF, ignore it */ + val = dmabuf->rate; + } + } else { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + return put_user(dmabuf->rate, (int *) arg); + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ +#ifdef DEBUG + printk("SNDCTL_DSP_STEREO\n"); +#endif + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + return put_user(1, (int *) arg); + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2))) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3))) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0))) + return val; + } + } + } + + if (file->f_mode & FMODE_READ) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 1))) + return val; + } +#ifdef DEBUG + printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize); +#endif + return put_user(dmabuf->userfragsize, (int *) arg); + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETFMTS\n"); +#endif + return put_user(AFMT_S16_LE, (int *) arg); + case SNDCTL_DSP_SETFMT: /* Select sample format */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETFMT\n"); +#endif + return put_user(AFMT_S16_LE, (int *) arg); + case SNDCTL_DSP_CHANNELS: // add support 4,6 channel +#ifdef DEBUG + printk("SNDCTL_DSP_CHANNELS\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val > 0) { + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + } else { + return put_user(state->card->channels, (int *) arg); + } + + i_scr = inl(state->card->iobase + ALI_SCR); + /* Current # of channels enabled */ + if (i_scr & 0x00000100) + ret = 4; + else if (i_scr & 0x00000200) + ret = 6; + else + ret = 2; + switch (val) { + case 2: /* 2 channels is always supported */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00100000), (state->card->iobase + ALI_SCR)); + } else + outl((i_scr & 0xfffffcff), (state->card->iobase + ALI_SCR)); + /* Do we need to change mixer settings???? */ + break; + case 4: /* Supported on some chipsets, better check first */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00000100 | 0x00200000), (state->card->iobase + ALI_SCR)); + } else + outl(((i_scr & 0xfffffcff) | 0x00000100), (state->card->iobase + ALI_SCR)); + break; + case 6: /* Supported on some chipsets, better check first */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000 | 0x00300000), (state->card->iobase + ALI_SCR)); + } else + outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000), (state->card->iobase + ALI_SCR)); + break; + default: /* nothing else is ever supported by the chipset */ + val = ret; + break; + } + return put_user(val, (int *) arg); + case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */ + /* we update the swptr to the end of the last sg segment then return */ +#ifdef DEBUG + printk("SNDCTL_DSP_POST\n"); +#endif + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready || (dmabuf->enable != CODEC_SPDIFOUT_RUNNING)) + return 0; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready || (dmabuf->enable != CONTROLLER_SPDIFOUT_RUNNING)) + return 0; + } else { + if (!dmabuf->ready || (dmabuf->enable != DAC_RUNNING)) + return 0; + } + } + if ((dmabuf->swptr % dmabuf->fragsize) != 0) { + val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize); + dmabuf->swptr += val; + dmabuf->count += val; + } + return 0; + case SNDCTL_DSP_SUBDIVIDE: + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; +#ifdef DEBUG + printk("SNDCTL_DSP_SUBDIVIDE %d\n", val); +#endif + dmabuf->subdivision = val; + dmabuf->ready = 0; + return 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + dmabuf->ossfragsize = 1 << (val & 0xffff); + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags) + return -EINVAL; + /* + * Bound the frag size into our allowed range of 256 - 4096 + */ + if (dmabuf->ossfragsize < 256) + dmabuf->ossfragsize = 256; + else if (dmabuf->ossfragsize > 4096) + dmabuf->ossfragsize = 4096; + /* + * The numfrags could be something reasonable, or it could + * be 0xffff meaning "Give me as much as possible". So, + * we check the numfrags * fragsize doesn't exceed our + * 64k buffer limit, nor is it less than our 8k minimum. + * If it fails either one of these checks, then adjust the + * number of fragments, not the size of them. It's OK if + * our number of fragments doesn't equal 32 or anything + * like our hardware based number now since we are using + * a different frag count for the hardware. Before we get + * into this though, bound the maxfrags to avoid overflow + * issues. A reasonable bound would be 64k / 256 since our + * maximum buffer size is 64k and our minimum frag size is + * 256. On the other end, our minimum buffer size is 8k and + * our maximum frag size is 4k, so the lower bound should + * be 2. + */ + if (dmabuf->ossmaxfrags > 256) + dmabuf->ossmaxfrags = 256; + else if (dmabuf->ossmaxfrags < 2) + dmabuf->ossmaxfrags = 2; + val = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + while (val < 8192) { + val <<= 1; + dmabuf->ossmaxfrags <<= 1; + } + while (val > 65536) { + val >>= 1; + dmabuf->ossmaxfrags >>= 1; + } + dmabuf->ready = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val, + dmabuf->ossfragsize, dmabuf->ossmaxfrags); +#endif + return 0; + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + } + } + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; + else + abinfo.bytes = ali_get_free_write_space(state); + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", + abinfo.bytes, abinfo.fragsize, abinfo.fragments, + abinfo.fragstotal); +#endif + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + } + } + spin_lock_irqsave(&state->card->lock, flags); + val = ali_get_free_write_space(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.ptr = dmabuf->hwptr; + cinfo.blocks = val / dmabuf->userfragsize; + if (codec_independent_spdif_locked > 0) { + if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 2); + } + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 3); + } + } else { + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 0); + } + } + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + abinfo.bytes = ali_get_available_read_data(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", + abinfo.bytes, abinfo.fragsize, abinfo.fragments, + abinfo.fragstotal); +#endif + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + val = ali_get_available_read_data(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = val / dmabuf->userfragsize; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->count -= val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 1); + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_NONBLOCK: +#ifdef DEBUG + printk("SNDCTL_DSP_NONBLOCK\n"); +#endif + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_GETCAPS: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCAPS\n"); +#endif + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | + DSP_CAP_MMAP | DSP_CAP_BIND, (int *) arg); + case SNDCTL_DSP_GETTRIGGER: + val = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger); +#endif + return put_user(dmabuf->trigger, (int *) arg); + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val); +#endif + if (!(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) { + stop_adc(state); + } + if (!(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) { + stop_dac(state); + } + if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + dmabuf->trigger = val; + if (val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) { + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = state->card->alloc_pcm_channel(state->card); + if (!dmabuf->write_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 0); + spin_unlock_irqrestore(&state->card->lock, + flags); + } else + start_dac(state); + } + if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CODEC_SPDIFOUT_RUNNING)) { + if (!dmabuf->codec_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->codec_spdifout_channel = state->card->alloc_codec_spdifout_channel(state->card); + if (!dmabuf->codec_spdifout_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 2); + spin_unlock_irqrestore(&state->card->lock, + flags); + } else + start_spdifout(state); + } + if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)) { + if (!dmabuf->controller_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->controller_spdifout_channel = state->card->alloc_controller_spdifout_channel(state->card); + if (!dmabuf->controller_spdifout_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 3); + spin_unlock_irqrestore(&state->card->lock, flags); + } else + start_spdifout(state); + } + if (val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) { + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card); + if (!dmabuf->read_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, + flags); + ali_update_ptr(state); + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + } + ali_update_lvi(state, 1); + start_adc(state); + } + return 0; + case SNDCTL_DSP_SETDUPLEX: +#ifdef DEBUG + printk("SNDCTL_DSP_SETDUPLEX\n"); +#endif + return -EINVAL; + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); +#ifdef DEBUG + printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count); +#endif + return put_user(val, (int *) arg); + case SOUND_PCM_READ_RATE: +#ifdef DEBUG + printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate); +#endif + return put_user(dmabuf->rate, (int *) arg); + case SOUND_PCM_READ_CHANNELS: +#ifdef DEBUG + printk("SOUND_PCM_READ_CHANNELS\n"); +#endif + return put_user(2, (int *) arg); + case SOUND_PCM_READ_BITS: +#ifdef DEBUG + printk("SOUND_PCM_READ_BITS\n"); +#endif + return put_user(AFMT_S16_LE, (int *) arg); + case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETSPDIF\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + /* Check to make sure the codec supports S/PDIF transmitter */ + if ((state->card->ac97_features & 4)) { + /* mask out the transmitter speed bits so the user can't set them */ + val &= ~0x3000; + /* Add the current transmitter speed bits to the passed value */ + ret = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + val |= (ret & 0x3000); + ali_ac97_set(codec, AC97_SPDIF_CONTROL, val); + if (ali_ac97_get(codec, AC97_SPDIF_CONTROL) != val) { + printk(KERN_ERR "ali_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); + return -EFAULT; + } + } +#ifdef DEBUG + else + printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); +#endif + return put_user(val, (int *) arg); + case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETSPDIF\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + /* Check to make sure the codec supports S/PDIF transmitter */ + if (!(state->card->ac97_features & 4)) { +#ifdef DEBUG + printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); +#endif + val = 0; + } else { + val = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + } + + return put_user(val, (int *) arg); +//end add support spdif out +//add support 4,6 channel + case SNDCTL_DSP_GETCHANNELMASK: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCHANNELMASK\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + /* Based on AC'97 DAC support, not ICH hardware */ + val = DSP_BIND_FRONT; + if (state->card->ac97_features & 0x0004) + val |= DSP_BIND_SPDIF; + if (state->card->ac97_features & 0x0080) + val |= DSP_BIND_SURR; + if (state->card->ac97_features & 0x0140) + val |= DSP_BIND_CENTER_LFE; + return put_user(val, (int *) arg); + case SNDCTL_DSP_BIND_CHANNEL: +#ifdef DEBUG + printk("SNDCTL_DSP_BIND_CHANNEL\n"); +#endif + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val == DSP_BIND_QUERY) { + val = DSP_BIND_FRONT; /* Always report this as being enabled */ + if (state->card->ac97_status & SPDIF_ON) + val |= DSP_BIND_SPDIF; + else { + if (state->card->ac97_status & SURR_ON) + val |= DSP_BIND_SURR; + if (state->card-> + ac97_status & CENTER_LFE_ON) + val |= DSP_BIND_CENTER_LFE; + } + } else { /* Not a query, set it */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (dmabuf->enable == DAC_RUNNING) { + stop_dac(state); + } + if (val & DSP_BIND_SPDIF) { /* Turn on SPDIF */ + /* Ok, this should probably define what slots + * to use. For now, we'll only set it to the + * defaults: + * + * non multichannel codec maps to slots 3&4 + * 2 channel codec maps to slots 7&8 + * 4 channel codec maps to slots 6&9 + * 6 channel codec maps to slots 10&11 + * + * there should be some way for the app to + * select the slot assignment. + */ + i_scr = inl(state->card->iobase + ALI_SCR); + if (codec_independent_spdif_locked > 0) { + + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00200000) { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } + } + } + } else { /* codec spdif out (pcm out share ) */ + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, dmabuf->rate); //I do not modify + } + + if (!(state->card->ac97_status & SPDIF_ON)) + val &= ~DSP_BIND_SPDIF; + } else { + int mask; + int channels; + /* Turn off S/PDIF if it was on */ + if (state->card->ac97_status & SPDIF_ON) + ali_set_spdif_output(state, -1, 0); + mask = + val & (DSP_BIND_FRONT | DSP_BIND_SURR | + DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT | DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + val = DSP_BIND_FRONT; + channels = 2; + break; + } + ali_set_dac_channels(state, channels); + /* check that they really got turned on */ + if (!state->card->ac97_status & SURR_ON) + val &= ~DSP_BIND_SURR; + if (!state->card-> + ac97_status & CENTER_LFE_ON) + val &= ~DSP_BIND_CENTER_LFE; + } + } + return put_user(val, (int *) arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + +static int ali_open(struct inode *inode, struct file *file) +{ + int i = 0; + struct ali_card *card = devs; + struct ali_state *state = NULL; + struct dmabuf *dmabuf = NULL; + unsigned int i_scr; + + /* find an available virtual channel (instance of /dev/dsp) */ + + while (card != NULL) { + + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + + for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct ali_state *) kmalloc(sizeof(struct ali_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct ali_state)); + dmabuf = &state->dmabuf; + goto found_virt; + } + } + card = card->next; + } + + /* no more virtual channel avaiable */ + if (!state) + return -ENODEV; +found_virt: + /* initialize the virtual channel */ + + state->virt = i; + state->card = card; + state->magic = ALI5455_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = state; + dmabuf->trigger = 0; + /* allocate hardware channels */ + if (file->f_mode & FMODE_READ) { + if ((dmabuf->read_channel = + card->alloc_rec_pcm_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= PCM_ENABLE_INPUT; + ali_set_adc_rate(state, 8000); + } + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + if ((dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; + ali_set_codecspdifout_rate(state, codec_independent_spdif_locked); //It must add + i_scr = inl(state->card->iobase + ALI_SCR); + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00200000) { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } else { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } + } + + } + } else { + if (controller_independent_spdif_locked > 0) { + if ((dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; + ali_set_spdifout_rate(state, controller_independent_spdif_locked); + } else { + if ((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + /* Initialize to 8kHz? What if we don't support 8kHz? */ + /* Let's change this to check for S/PDIF stuff */ + + dmabuf->trigger |= PCM_ENABLE_OUTPUT; + if (codec_pcmout_share_spdif_locked) { + ali_set_dac_rate(state, codec_pcmout_share_spdif_locked); + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, codec_pcmout_share_spdif_locked); + } else { + ali_set_dac_rate(state, 8000); + } + } + + } + } + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample, but we don't support those so we + set it immediately to stereo and 16bit, which is all we do support */ + dmabuf->fmt |= ALI5455_FMT_16BIT | ALI5455_FMT_STEREO; + dmabuf->ossfragsize = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + outl(0x00000000, card->iobase + ALI_INTERRUPTCR); + outl(0x00000000, card->iobase + ALI_INTERRUPTSR); + return 0; +} + +static int ali_release(struct inode *inode, struct file *file) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state->card; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + lock_kernel(); + + /* stop DMA state machine and free DMA buffers/channels */ + if (dmabuf->trigger & PCM_ENABLE_OUTPUT) + drain_dac(state, 0); + + if (dmabuf->trigger & SPDIF_ENABLE_OUTPUT) + drain_spdifout(state, 0); + + if (dmabuf->trigger & PCM_ENABLE_INPUT) + stop_adc(state); + + spin_lock_irqsave(&card->lock, flags); + dealloc_dmabuf(state); + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + state->card->free_pcm_channel(state->card, dmabuf->codec_spdifout_channel->num); + } else { + if (controller_independent_spdif_locked > 0) + state->card->free_pcm_channel(state->card, + dmabuf->controller_spdifout_channel->num); + else state->card->free_pcm_channel(state->card, + dmabuf->write_channel->num); + } + } + if (file->f_mode & FMODE_READ) + state->card->free_pcm_channel(state->card, dmabuf->read_channel->num); + + state->card->states[state->virt] = NULL; + kfree(state); + spin_unlock_irqrestore(&card->lock, flags); + unlock_kernel(); + return 0; +} + +static /*const */ struct file_operations ali_audio_fops = { + owner:THIS_MODULE, + llseek:no_llseek, + read:ali_read, + write:ali_write, + poll:ali_poll, + ioctl:ali_ioctl, + mmap:ali_mmap, + open:ali_open, + release:ali_release, +}; + +/* Read AC97 codec registers */ +static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct ali_card *card = dev->private_data; + int count1 = 100; + char val; + unsigned short int data = 0, count, addr1, addr2 = 0; + + spin_lock(&card->ac97_lock); + while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) + udelay(1); + + addr1 = reg; + reg |= 0x0080; + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x08) + break; + } + if (count == 0x7f) + { + spin_unlock(&card->ac97_lock); + return -1; + } + outw(reg, (card->iobase + ALI_CPR) + 2); + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x02) { + data = inw(card->iobase + ALI_SPR); + addr2 = inw((card->iobase + ALI_SPR) + 2); + break; + } + } + spin_unlock(&card->ac97_lock); + if (count == 0x7f) + return -1; + if (addr2 != addr1) + return -1; + return ((u16) data); +} + +/* write ac97 codec register */ + +static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct ali_card *card = dev->private_data; + int count1 = 100; + char val; + unsigned short int count; + + spin_lock(&card->ac97_lock); + while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) + udelay(1); + + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x08) + break; + } + if (count == 0x7f) { + printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); + spin_unlock(&card->ac97_lock); + return; + } + outw(data, (card->iobase + ALI_CPR)); + outb(reg, (card->iobase + ALI_CPR) + 2); + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x01) + break; + } + spin_unlock(&card->ac97_lock); + if (count == 0x7f) + printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); + return; +} + +/* OSS /dev/mixer file operation methods */ + +static int ali_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = minor(inode->i_rdev); + struct ali_card *card = devs; + for (card = devs; card != NULL; card = card->next) { + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + for (i = 0; i < NR_AC97 && card && !card->initializing; i++) + if (card->ac97_codec[i] != NULL + && card->ac97_codec[i]->dev_mixer == minor) { + file->private_data = card->ac97_codec[i]; + return 0; + } + } + return -ENODEV; +} + +static int ali_ioctl_mixdev(struct inode *inode, + struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *) file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const */ struct file_operations ali_mixer_fops = { + owner:THIS_MODULE, + llseek:no_llseek, + ioctl:ali_ioctl_mixdev, + open:ali_open_mixdev, +}; + +/* AC97 codec initialisation. These small functions exist so we don't + duplicate code between module init and apm resume */ + +static inline int ali_ac97_exists(struct ali_card *card, int ac97_number) +{ + unsigned int i = 1; + u32 reg = inl(card->iobase + ALI_RTSR); + if (ac97_number) { + while (i < 100) { + + reg = inl(card->iobase + ALI_RTSR); + if (reg & 0x40) { + break; + } else { + outl(reg | 0x00000040, + card->iobase + 0x34); + udelay(1); + } + i++; + } + + } else { + while (i < 100) { + reg = inl(card->iobase + ALI_RTSR); + if (reg & 0x80) { + break; + } else { + outl(reg | 0x00000080, + card->iobase + 0x34); + udelay(1); + } + i++; + } + } + + if (ac97_number) + return reg & 0x40; + else + return reg & 0x80; +} + +static inline int ali_ac97_enable_variable_rate(struct ac97_codec *codec) +{ + ali_ac97_set(codec, AC97_EXTENDED_STATUS, 9); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, ali_ac97_get(codec, AC97_EXTENDED_STATUS) | 0xE800); + return (ali_ac97_get(codec, AC97_EXTENDED_STATUS) & 1); +} + + +static int ali_ac97_probe_and_powerup(struct ali_card *card, struct ac97_codec *codec) +{ + /* Returns 0 on failure */ + int i; + u16 addr; + if (ac97_probe_codec(codec) == 0) + return 0; + /* ac97_probe_codec is success ,then begin to init codec */ + ali_ac97_set(codec, AC97_RESET, 0xffff); + if (card->channel[0].used == 1) { + ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); + ali_ac97_set(codec, AC97_LINEIN_VOL, 0x0808); + ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); + } + + if (card->channel[2].used == 1) //if MICin then init codec + { + ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); + ali_ac97_set(codec, AC97_MIC_VOL, 0x8808); + ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); + ali_ac97_set(codec, AC97_RECORD_GAIN_MIC, 0x0000); + } + + ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, 0x0000); + ali_ac97_set(codec, AC97_HEADPHONE_VOL, 0x0000); + ali_ac97_set(codec, AC97_PCMOUT_VOL, 0x0000); + ali_ac97_set(codec, AC97_CD_VOL, 0x0808); + ali_ac97_set(codec, AC97_VIDEO_VOL, 0x0808); + ali_ac97_set(codec, AC97_AUX_VOL, 0x0808); + ali_ac97_set(codec, AC97_PHONE_VOL, 0x8048); + ali_ac97_set(codec, AC97_PCBEEP_VOL, 0x0000); + ali_ac97_set(codec, AC97_GENERAL_PURPOSE, AC97_GP_MIX); + ali_ac97_set(codec, AC97_MASTER_VOL_MONO, 0x0000); + ali_ac97_set(codec, 0x38, 0x0000); + addr = ali_ac97_get(codec, 0x2a); + ali_ac97_set(codec, 0x2a, addr | 0x0001); + addr = ali_ac97_get(codec, 0x2a); + addr = ali_ac97_get(codec, 0x28); + ali_ac97_set(codec, 0x2c, 0xbb80); + addr = ali_ac97_get(codec, 0x2c); + /* power it all up */ + ali_ac97_set(codec, AC97_POWER_CONTROL, + ali_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i = 10; i && ((ali_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + /* FIXME !! */ + i++; + return i; +} + + +/* I clone ali5455(2.4.7 ) not clone i810_audio(2.4.18) */ + +static int ali_reset_5455(struct ali_card *card) +{ + outl(0x80000003, card->iobase + ALI_SCR); + outl(0x83838383, card->iobase + ALI_FIFOCR1); + outl(0x83838383, card->iobase + ALI_FIFOCR2); + if (controller_pcmout_share_spdif_locked > 0) { + outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), + card->iobase + ALI_SPDIFICS); + outl(0x0408000a, card->iobase + ALI_INTERFACECR); + } else { + if (codec_independent_spdif_locked > 0) { + outl((inl(card->iobase + ALI_SCR) | 0x00100000), card->iobase + ALI_SCR); // now I select slot 7 & 8 + outl(0x00200000, card->iobase + ALI_INTERFACECR); //enable codec independent spdifout + } else + outl(0x04080002, card->iobase + ALI_INTERFACECR); + } + + outl(0x00000000, card->iobase + ALI_INTERRUPTCR); + outl(0x00000000, card->iobase + ALI_INTERRUPTSR); + if (controller_independent_spdif_locked > 0) + outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), + card->iobase + ALI_SPDIFICS); + return 1; +} + + +static int ali_ac97_random_init_stuff(struct ali_card + *card) +{ + u32 reg = inl(card->iobase + ALI_SCR); + int i = 0; + reg = inl(card->iobase + ALI_SCR); + if ((reg & 2) == 0) /* Cold required */ + reg |= 2; + else + reg |= 1; /* Warm */ + reg &= ~0x80000000; /* ACLink on */ + outl(reg, card->iobase + ALI_SCR); + + while (i < 10) { + if ((inl(card->iobase + 0x18) & (1 << 1)) == 0) + break; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ / 20); + i++; + } + if (i == 10) { + printk(KERN_ERR "ali_audio: AC'97 reset failed.\n"); + return 0; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 2); + return 1; +} + +/* AC97 codec initialisation. */ + +static int __init ali_ac97_init(struct ali_card *card) +{ + int num_ac97 = 0; + int total_channels = 0; + struct ac97_codec *codec; + u16 eid; + + if (!ali_ac97_random_init_stuff(card)) + return 0; + + /* Number of channels supported */ + /* What about the codec? Just because the ICH supports */ + /* multiple channels doesn't mean the codec does. */ + /* we'll have to modify this in the codec section below */ + /* to reflect what the codec has. */ + /* ICH and ICH0 only support 2 channels so don't bother */ + /* to check.... */ + inl(card->iobase + ALI_CPR); + card->channels = 2; + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + + /* Assume codec isn't available until we go through the + * gauntlet below */ + card->ac97_codec[num_ac97] = NULL; + /* The ICH programmer's reference says you should */ + /* check the ready status before probing. So we chk */ + /* What do we do if it's not ready? Wait and try */ + /* again, or abort? */ + if (!ali_ac97_exists(card, num_ac97)) { + if (num_ac97 == 0) + printk(KERN_ERR "ali_audio: Primary codec not ready.\n"); + break; + } + + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + codec->codec_read = ali_ac97_get; + codec->codec_write = ali_ac97_set; + if (!ali_ac97_probe_and_powerup(card, codec)) { + printk(KERN_ERR "ali_audio: timed out waiting for codec %d analog ready", + num_ac97); + kfree(codec); + break; /* it didn't work */ + } + + /* Store state information about S/PDIF transmitter */ + card->ac97_status = 0; + /* Don't attempt to get eid until powerup is complete */ + eid = ali_ac97_get(codec, AC97_EXTENDED_ID); + if (eid == 0xFFFF) { + printk(KERN_ERR "ali_audio: no codec attached ?\n"); + kfree(codec); + break; + } + + card->ac97_features = eid; + /* Now check the codec for useful features to make up for + the dumbness of the ali5455 hardware engine */ + if (!(eid & 0x0001)) + printk(KERN_WARNING + "ali_audio: only 48Khz playback available.\n"); + else { + if (!ali_ac97_enable_variable_rate(codec)) { + printk(KERN_WARNING + "ali_audio: Codec refused to allow VRA, using 48Khz only.\n"); + card->ac97_features &= ~1; + } + } + + /* Determine how many channels the codec(s) support */ + /* - The primary codec always supports 2 */ + /* - If the codec supports AMAP, surround DACs will */ + /* automaticlly get assigned to slots. */ + /* * Check for surround DACs and increment if */ + /* found. */ + /* - Else check if the codec is revision 2.2 */ + /* * If surround DACs exist, assign them to slots */ + /* and increment channel count. */ + + /* All of this only applies to ICH2 and above. ICH */ + /* and ICH0 only support 2 channels. ICH2 will only */ + /* support multiple codecs in a "split audio" config. */ + /* as described above. */ + + /* TODO: Remove all the debugging messages! */ + + if ((eid & 0xc000) == 0) /* primary codec */ + total_channels += 2; + if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1)) < 0) { + printk(KERN_ERR "ali_audio: couldn't register mixer!\n"); + kfree(codec); + break; + } + card->ac97_codec[num_ac97] = codec; + } + /* pick the minimum of channels supported by ICHx or codec(s) */ + card->channels = (card->channels > total_channels) ? total_channels : card->channels; + return num_ac97; +} + +static void __init ali_configure_clocking(void) +{ + struct ali_card *card; + struct ali_state *state; + struct dmabuf *dmabuf; + unsigned int i, offset, new_offset; + unsigned long flags; + card = devs; + + /* We could try to set the clocking for multiple cards, but can you even have + * more than one ali in a machine? Besides, clocking is global, so unless + * someone actually thinks more than one ali in a machine is possible and + * decides to rewrite that little bit, setting the rate for more than one card + * is a waste of time. + */ + if (card != NULL) { + state = card->states[0] = (struct ali_state *) + kmalloc(sizeof(struct ali_state), GFP_KERNEL); + if (state == NULL) + return; + memset(state, 0, sizeof(struct ali_state)); + dmabuf = &state->dmabuf; + dmabuf->write_channel = card->alloc_pcm_channel(card); + state->virt = 0; + state->card = card; + state->magic = ALI5455_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + dmabuf->fmt = ALI5455_FMT_STEREO | ALI5455_FMT_16BIT; + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_set_dac_rate(state, 48000); + if (prog_dmabuf(state, 0) != 0) + goto config_out_nodmabuf; + + if (dmabuf->dmasize < 16384) + goto config_out; + + dmabuf->count = dmabuf->dmasize; + outb(31, card->iobase + dmabuf->write_channel->port + OFF_LVI); + + local_irq_save(flags); + start_dac(state); + offset = ali_get_dma_addr(state, 0); + mdelay(50); + new_offset = ali_get_dma_addr(state, 0); + stop_dac(state); + + outb(2, card->iobase + dmabuf->write_channel->port + OFF_CR); + local_irq_restore(flags); + + i = new_offset - offset; + + if (i == 0) + goto config_out; + i = i / 4 * 20; + if (i > 48500 || i < 47500) { + clocking = clocking * clocking / i; + } +config_out: + dealloc_dmabuf(state); +config_out_nodmabuf: + state->card->free_pcm_channel(state->card, state->dmabuf. write_channel->num); + kfree(state); + card->states[0] = NULL; + } +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ + +static int __init ali_probe(struct pci_dev *pci_dev, const struct pci_device_id + *pci_id) +{ + struct ali_card *card; + if (pci_enable_device(pci_dev)) + return -EIO; + if (pci_set_dma_mask(pci_dev, ALI5455_DMA_MASK)) { + printk(KERN_ERR "ali5455: architecture does not support" + " 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if ((card = kmalloc(sizeof(struct ali_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "ali_audio: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + card->initializing = 1; + card->iobase = pci_resource_start(pci_dev, 0); + card->pci_dev = pci_dev; + card->pci_id = pci_id->device; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = ALI5455_CARD_MAGIC; +#ifdef CONFIG_PM + card->pm_suspended = 0; +#endif + spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); + devs = card; + pci_set_master(pci_dev); + printk(KERN_INFO "ali: %s found at IO 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->irq); + card->alloc_pcm_channel = ali_alloc_pcm_channel; + card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; + card->alloc_rec_mic_channel = ali_alloc_rec_mic_channel; + card->alloc_codec_spdifout_channel = ali_alloc_codec_spdifout_channel; + card->alloc_controller_spdifout_channel = ali_alloc_controller_spdifout_channel; + card->free_pcm_channel = ali_free_pcm_channel; + card->channel[0].offset = 0; + card->channel[0].port = 0x40; + card->channel[0].num = 0; + card->channel[1].offset = 0; + card->channel[1].port = 0x50; + card->channel[1].num = 1; + card->channel[2].offset = 0; + card->channel[2].port = 0x60; + card->channel[2].num = 2; + card->channel[3].offset = 0; + card->channel[3].port = 0x70; + card->channel[3].num = 3; + card->channel[4].offset = 0; + card->channel[4].port = 0xb0; + card->channel[4].num = 4; + /* claim our iospace and irq */ + request_region(card->iobase, 256, card_names[pci_id->driver_data]); + if (request_irq(card->irq, &ali_interrupt, SA_SHIRQ, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "ali_audio: unable to allocate irq %d\n", + card->irq); + release_region(card->iobase, 256); + kfree(card); + return -ENODEV; + } + + if (ali_reset_5455(card) <= 0) { + unregister_sound_dsp(card->dev_audio); + release_region(card->iobase, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + + /* initialize AC97 codec and register /dev/mixer */ + if (ali_ac97_init(card) < 0) { + release_region(card->iobase, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + + pci_set_drvdata(pci_dev, card); + + if (clocking == 0) { + clocking = 48000; + ali_configure_clocking(); + } + + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1)) < 0) { + int i; + printk(KERN_ERR"ali_audio: couldn't register DSP device!\n"); + release_region(card->iobase, 256); + free_irq(card->irq, card); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree(card->ac97_codec[i]); + } + kfree(card); + return -ENODEV; + } + card->initializing = 0; + return 0; +} + +static void __devexit ali_remove(struct pci_dev *pci_dev) +{ + int i; + struct ali_card *card = pci_get_drvdata(pci_dev); + /* free hardware resources */ + free_irq(card->irq, devs); + release_region(card->iobase, 256); + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]-> + dev_mixer); + ac97_release_codec(card->ac97_codec[i]); + card->ac97_codec[i] = NULL; + } + unregister_sound_dsp(card->dev_audio); + kfree(card); +} + +#ifdef CONFIG_PM +static int ali_pm_suspend(struct pci_dev *dev, u32 pm_state) +{ + struct ali_card *card = pci_get_drvdata(dev); + struct ali_state *state; + unsigned long flags; + struct dmabuf *dmabuf; + int i, num_ac97; + + if (!card) + return 0; + spin_lock_irqsave(&card->lock, flags); + card->pm_suspended = 1; + for (i = 0; i < NR_HW_CH; i++) { + state = card->states[i]; + if (!state) + continue; + /* this happens only if there are open files */ + dmabuf = &state->dmabuf; + if (dmabuf->enable & DAC_RUNNING || + (dmabuf->count + && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) { + state->pm_saved_dac_rate = dmabuf->rate; + stop_dac(state); + } else { + state->pm_saved_dac_rate = 0; + } + if (dmabuf->enable & ADC_RUNNING) { + state->pm_saved_adc_rate = dmabuf->rate; + stop_adc(state); + } else { + state->pm_saved_adc_rate = 0; + } + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + + spin_unlock_irqrestore(&card->lock, flags); + /* save mixer settings */ + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + if (!codec) + continue; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if ((supported_mixer(codec, i)) && (codec->read_mixer)) { + card->pm_saved_mixer_settings[i][num_ac97] = codec->read_mixer(codec, i); + } + } + } + pci_save_state(dev, card->pm_save_state); /* XXX do we need this? */ + pci_disable_device(dev); /* disable busmastering */ + pci_set_power_state(dev, 3); /* Zzz. */ + return 0; +} + + +static int ali_pm_resume(struct pci_dev *dev) +{ + int num_ac97, i = 0; + struct ali_card *card = pci_get_drvdata(dev); + pci_enable_device(dev); + pci_restore_state(dev, card->pm_save_state); + /* observation of a toshiba portege 3440ct suggests that the + hardware has to be more or less completely reinitialized from + scratch after an apm suspend. Works For Me. -dan */ + ali_ac97_random_init_stuff(card); + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + /* check they haven't stolen the hardware while we were + away */ + if (!codec || !ali_ac97_exists(card, num_ac97)) { + if (num_ac97) + continue; + else + BUG(); + } + if (!ali_ac97_probe_and_powerup(card, codec)) + BUG(); + if ((card->ac97_features & 0x0001)) { + /* at probe time we found we could do variable + rates, but APM suspend has made it forget + its magical powers */ + if (!ali_ac97_enable_variable_rate(codec)) + BUG(); + } + /* we lost our mixer settings, so restore them */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (supported_mixer(codec, i)) { + int val = card->pm_saved_mixer_settings[i][num_ac97]; + codec->mixer_state[i] = val; + codec->write_mixer(codec, i, + (val & 0xff), + ((val >> 8) & 0xff)); + } + } + } + + /* we need to restore the sample rate from whatever it was */ + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + if (state) { + if (state->pm_saved_adc_rate) + ali_set_adc_rate(state, state->pm_saved_adc_rate); + if (state->pm_saved_dac_rate) + ali_set_dac_rate(state, state->pm_saved_dac_rate); + } + } + + card->pm_suspended = 0; + /* any processes that were reading/writing during the suspend + probably ended up here */ + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + if (state) + wake_up(&state->dmabuf.wait); + } + return 0; +} +#endif /* CONFIG_PM */ + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("ALI 5455 audio support"); +MODULE_LICENSE("GPL"); +MODULE_PARM(clocking, "i"); +MODULE_PARM(strict_clocking, "i"); +MODULE_PARM(codec_pcmout_share_spdif_locked, "i"); +MODULE_PARM(codec_independent_spdif_locked, "i"); +MODULE_PARM(controller_pcmout_share_spdif_locked, "i"); +MODULE_PARM(controller_independent_spdif_locked, "i"); +#define ALI5455_MODULE_NAME "ali5455" +static struct pci_driver ali_pci_driver = { + name:ALI5455_MODULE_NAME, id_table:ali_pci_tbl, probe:ali_probe, + remove:__devexit_p(ali_remove), +#ifdef CONFIG_PM + suspend:ali_pm_suspend, resume:ali_pm_resume, +#endif /* CONFIG_PM */ +}; + +static int __init ali_init_module(void) +{ + printk(KERN_INFO "ALI 5455 + AC97 Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (codec_independent_spdif_locked > 0) { + if (codec_independent_spdif_locked == 32000 + || codec_independent_spdif_locked == 44100 + || codec_independent_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_independent_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + codec_independent_spdif_locked = 0; + } + } + if (controller_independent_spdif_locked > 0) { + if (controller_independent_spdif_locked == 32000 + || controller_independent_spdif_locked == 44100 + || controller_independent_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", controller_independent_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + controller_independent_spdif_locked = 0; + } + } + + if (codec_pcmout_share_spdif_locked > 0) { + if (codec_pcmout_share_spdif_locked == 32000 + || codec_pcmout_share_spdif_locked == 44100 + || codec_pcmout_share_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_pcmout_share_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + codec_pcmout_share_spdif_locked = 0; + } + } + if (controller_pcmout_share_spdif_locked > 0) { + if (controller_pcmout_share_spdif_locked == 32000 + || controller_pcmout_share_spdif_locked == 44100 + || controller_pcmout_share_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling controller S/PDIF at sample rate %dHz.\n", controller_pcmout_share_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + controller_pcmout_share_spdif_locked = 0; + } + } + if (!pci_register_driver(&ali_pci_driver)) { + pci_unregister_driver(&ali_pci_driver); + return -ENODEV; + } + return 0; +} + +static void __exit ali_cleanup_module(void) +{ + pci_unregister_driver(&ali_pci_driver); +} + +module_init(ali_init_module); +module_exit(ali_cleanup_module); +/* +Local Variables: +c-basic-offset: 8 +End: +*/ diff -urN linux-2.5.75-bk1/sound/oss/au1000.c linux-2.5.75-bk2/sound/oss/au1000.c --- linux-2.5.75-bk1/sound/oss/au1000.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.75-bk2/sound/oss/au1000.c 2003-07-12 04:37:48.000000000 -0700 @@ -0,0 +1,2238 @@ +/* + * au1000.c -- Sound driver for Alchemy Au1000 MIPS Internet Edge + * Processor. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Module command line parameters: + * + * Supported devices: + * /dev/dsp standard OSS /dev/dsp device + * /dev/mixer standard OSS /dev/mixer device + * + * Notes: + * + * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are + * taken, slightly modified or not at all, from the ES1371 driver, + * so refer to the credits in es1371.c for those. The rest of the + * code (probe, open, read, write, the ISR, etc.) is new. + * + * Revision history + * 06.27.2001 Initial version + * 03.20.2002 Added mutex locks around read/write methods, to prevent + * simultaneous access on SMP or preemptible kernels. Also + * removed the counter/pointer fragment aligning at the end + * of read/write methods [stevel]. + * 03.21.2002 Add support for coherent DMA on the audio read/write DMA + * channels [stevel]. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define AU1000_DEBUG +#undef AU1000_VERBOSE_DEBUG + +#define USE_COHERENT_DMA + +#define AU1000_MODULE_NAME "Au1000 audio" +#define PFX AU1000_MODULE_NAME + +#ifdef AU1000_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + + +/* misc stuff */ +#define POLL_COUNT 0x5000 +#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC) + +/* Boot options */ +static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports it +MODULE_PARM(vra, "i"); +MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); + + +/* --------------------------------------------------------------------- */ + +struct au1000_state { + /* soundcore stuff */ + int dev_audio; + +#ifdef AU1000_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; +#endif /* AU1000_DEBUG */ + + struct ac97_codec *codec; + unsigned codec_base_caps;// AC'97 reg 00h, "Reset Register" + unsigned codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID" + int no_vra; // do not use VRA + + spinlock_t lock; + struct semaphore open_sem; + struct semaphore sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + unsigned int dmanr; // DMA Channel number + unsigned sample_rate; // Hz + unsigned src_factor; // SRC interp/decimation (no vra) + unsigned sample_size; // 8 or 16 + int num_channels; // 1 = mono, 2 = stereo, 4, 6 + int dma_bytes_per_sample;// DMA bytes per audio sample frame + int user_bytes_per_sample;// User bytes per audio sample frame + int cnt_factor; // user-to-DMA bytes per audio + // sample frame + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; // # of DMA fragments in DMA buffer + unsigned fragshift; + void *nextIn; // ptr to next-in to DMA buffer + void *nextOut;// ptr to next-out from DMA buffer + int count; // current byte count in DMA buffer + unsigned total_bytes; // total bytes written or read + unsigned error; // over/underrun + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; // user perception of fragment size + unsigned dma_fragsize; // DMA (real) fragment size + unsigned dmasize; // Total DMA buffer size + // (mult. of DMA fragsize) + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned stopped:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac , dma_adc; +} au1000_state; + +/* --------------------------------------------------------------------- */ + + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + + +#ifdef USE_COHERENT_DMA +static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + void* ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, + get_order(size)); + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} + +static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle) +{ + free_pages((unsigned long)va, get_order(size)); +} +#else +static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + return pci_alloc_consistent(NULL, size, dma_handle); +} + +static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle) +{ + pci_free_consistent(NULL, size, va, dma_handle); +} +#endif + +/* --------------------------------------------------------------------- */ + +static void au1000_delay(int msec) +{ + unsigned long tmo; + signed long tmo2; + + if (in_interrupt()) + return; + + tmo = jiffies + (msec * HZ) / 1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } +} + + +/* --------------------------------------------------------------------- */ + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct au1000_state *s = (struct au1000_state *)codec->private_data; + unsigned long flags; + u32 cmd; + u16 data; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) + err("rdcodec: codec cmd pending expired!"); + + cmd = (u32) addr & AC97C_INDEX_MASK; + cmd |= AC97C_READ; // read command + au_writel(cmd, AC97C_CMD); + + /* now wait for the data */ + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) { + err("rdcodec: read poll expired!"); + return 0; + } + + data = au_readl(AC97C_CMD) & 0xffff; + + spin_unlock_irqrestore(&s->lock, flags); + + return data; +} + + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct au1000_state *s = (struct au1000_state *)codec->private_data; + unsigned long flags; + u32 cmd; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) + err("wrcodec: codec cmd pending expired!"); + + cmd = (u32) addr & AC97C_INDEX_MASK; + cmd &= ~AC97C_READ; // write command + cmd |= ((u32) data << AC97C_WD_BIT); // OR in the data word + au_writel(cmd, AC97C_CMD); + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void waitcodec(struct ac97_codec *codec) +{ + u16 temp; + int i; + + /* codec_wait is used to wait for a ready state after + an AC97C_RESET. */ + au1000_delay(10); + + // first poll the CODEC_READY tag bit + for (i = 0; i < POLL_COUNT; i++) + if (au_readl(AC97C_STATUS) & AC97C_READY) + break; + if (i == POLL_COUNT) { + err("waitcodec: CODEC_READY poll expired!"); + return; + } + // get AC'97 powerdown control/status register + temp = rdcodec(codec, AC97_POWER_CONTROL); + + // If anything is powered down, power'em up + if (temp & 0x7f00) { + // Power on + wrcodec(codec, AC97_POWER_CONTROL, 0); + au1000_delay(100); + // Reread + temp = rdcodec(codec, AC97_POWER_CONTROL); + } + + // Check if Codec REF,ANL,DAC,ADC ready + if ((temp & 0x7f0f) != 0x000f) + err("codec reg 26 status (0x%x) not ready!!", temp); +} + + +/* --------------------------------------------------------------------- */ + +/* stop the ADC before calling */ +static void set_adc_rate(struct au1000_state *s, unsigned rate) +{ + struct dmabuf *adc = &s->dma_adc; + struct dmabuf *dac = &s->dma_dac; + unsigned adc_rate, dac_rate; + u16 ac97_extstat; + + if (s->no_vra) { + // calc SRC factor + adc->src_factor = ((96000 / rate) + 1) >> 1; + adc->sample_rate = 48000 / adc->src_factor; + return; + } + + adc->src_factor = 1; + + ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); + + rate = rate > 48000 ? 48000 : rate; + + // enable VRA + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ac97_extstat | AC97_EXTSTAT_VRA); + // now write the sample rate + wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate); + // read it back for actual supported rate + adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE); + +#ifdef AU1000_VERBOSE_DEBUG + dbg(__FUNCTION__ ": set to %d Hz", adc_rate); +#endif + + // some codec's don't allow unequal DAC and ADC rates, in which case + // writing one rate reg actually changes both. + dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); + if (dac->num_channels > 2) + wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate); + if (dac->num_channels > 4) + wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate); + + adc->sample_rate = adc_rate; + dac->sample_rate = dac_rate; +} + +/* stop the DAC before calling */ +static void set_dac_rate(struct au1000_state *s, unsigned rate) +{ + struct dmabuf *dac = &s->dma_dac; + struct dmabuf *adc = &s->dma_adc; + unsigned adc_rate, dac_rate; + u16 ac97_extstat; + + if (s->no_vra) { + // calc SRC factor + dac->src_factor = ((96000 / rate) + 1) >> 1; + dac->sample_rate = 48000 / dac->src_factor; + return; + } + + dac->src_factor = 1; + + ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); + + rate = rate > 48000 ? 48000 : rate; + + // enable VRA + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ac97_extstat | AC97_EXTSTAT_VRA); + // now write the sample rate + wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate); + // I don't support different sample rates for multichannel, + // so make these channels the same. + if (dac->num_channels > 2) + wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate); + if (dac->num_channels > 4) + wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate); + // read it back for actual supported rate + dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); + +#ifdef AU1000_VERBOSE_DEBUG + dbg(__FUNCTION__ ": set to %d Hz", dac_rate); +#endif + + // some codec's don't allow unequal DAC and ADC rates, in which case + // writing one rate reg actually changes both. + adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE); + + dac->sample_rate = dac_rate; + adc->sample_rate = adc_rate; +} + +static void stop_dac(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_dac; + unsigned long flags; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + disable_dma(db->dmanr); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void stop_adc(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_adc; + unsigned long flags; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + disable_dma(db->dmanr); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void set_xmit_slots(int num_channels) +{ + u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_XMIT_SLOTS_MASK; + + switch (num_channels) { + case 1: // mono + case 2: // stereo, slots 3,4 + ac97_config |= (0x3 << AC97C_XMIT_SLOTS_BIT); + break; + case 4: // stereo with surround, slots 3,4,7,8 + ac97_config |= (0x33 << AC97C_XMIT_SLOTS_BIT); + break; + case 6: // stereo with surround and center/LFE, slots 3,4,6,7,8,9 + ac97_config |= (0x7b << AC97C_XMIT_SLOTS_BIT); + break; + } + + au_writel(ac97_config, AC97C_CONFIG); +} + +static void set_recv_slots(int num_channels) +{ + u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_RECV_SLOTS_MASK; + + /* + * Always enable slots 3 and 4 (stereo). Slot 6 is + * optional Mic ADC, which I don't support yet. + */ + ac97_config |= (0x3 << AC97C_RECV_SLOTS_BIT); + + au_writel(ac97_config, AC97C_CONFIG); +} + +static void start_dac(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_dac; + unsigned long flags; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + au_readl(AC97C_STATUS); // read status to clear sticky bits + + // reset Buffer 1 and 2 pointers to nextOut and nextOut+dma_fragsize + buf1 = virt_to_phys(db->nextOut); + buf2 = buf1 + db->dma_fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + set_xmit_slots(db->num_channels); + + init_dma(db->dmanr); + if (get_dma_active_buffer(db->dmanr) == 0) { + clear_dma_done0(db->dmanr); // clear DMA done bit + set_dma_addr0(db->dmanr, buf1); + set_dma_addr1(db->dmanr, buf2); + } else { + clear_dma_done1(db->dmanr); // clear DMA done bit + set_dma_addr1(db->dmanr, buf1); + set_dma_addr0(db->dmanr, buf2); + } + set_dma_count(db->dmanr, db->dma_fragsize>>1); + enable_dma_buffers(db->dmanr); + + start_dma(db->dmanr); + +#ifdef AU1000_VERBOSE_DEBUG + dump_au1000_dma_channel(db->dmanr); +#endif + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_adc; + unsigned long flags; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + au_readl(AC97C_STATUS); // read status to clear sticky bits + + // reset Buffer 1 and 2 pointers to nextIn and nextIn+dma_fragsize + buf1 = virt_to_phys(db->nextIn); + buf2 = buf1 + db->dma_fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + set_recv_slots(db->num_channels); + + init_dma(db->dmanr); + if (get_dma_active_buffer(db->dmanr) == 0) { + clear_dma_done0(db->dmanr); // clear DMA done bit + set_dma_addr0(db->dmanr, buf1); + set_dma_addr1(db->dmanr, buf2); + } else { + clear_dma_done1(db->dmanr); // clear DMA done bit + set_dma_addr1(db->dmanr, buf1); + set_dma_addr0(db->dmanr, buf2); + } + set_dma_count(db->dmanr, db->dma_fragsize>>1); + enable_dma_buffers(db->dmanr); + + start_dma(db->dmanr); + +#ifdef AU1000_VERBOSE_DEBUG + dump_au1000_dma_channel(db->dmanr); +#endif + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +extern inline void dealloc_dmabuf(struct au1000_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + dma_free(PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = db->nextIn = db->nextOut = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct au1000_state *s, struct dmabuf *db) +{ + int order; + unsigned user_bytes_per_sec; + unsigned bufs; + struct page *page, *pend; + unsigned rate = db->sample_rate; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = dma_alloc(PAGE_SIZE << order, + &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; + otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + db->cnt_factor = 1; + if (db->sample_size == 8) + db->cnt_factor *= 2; + if (db->num_channels == 1) + db->cnt_factor *= 2; + db->cnt_factor *= db->src_factor; + + db->count = 0; + db->nextIn = db->nextOut = db->rawbuf; + + db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels; + db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ? + 2 : db->num_channels); + + user_bytes_per_sec = rate * db->user_bytes_per_sample; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < user_bytes_per_sec) + db->fragshift = ld2(user_bytes_per_sec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(user_bytes_per_sec / 100 / + (db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + + db->fragsize = 1 << db->fragshift; + db->dma_fragsize = db->fragsize * db->cnt_factor; + db->numfrag = bufs / db->dma_fragsize; + + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->fragsize = 1 << db->fragshift; + db->dma_fragsize = db->fragsize * db->cnt_factor; + db->numfrag = bufs / db->dma_fragsize; + } + + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + + db->dmasize = db->dma_fragsize * db->numfrag; + memset(db->rawbuf, 0, bufs); + +#ifdef AU1000_VERBOSE_DEBUG + dbg("rate=%d, samplesize=%d, channels=%d", + rate, db->sample_size, db->num_channels); + dbg("fragsize=%d, cnt_factor=%d, dma_fragsize=%d", + db->fragsize, db->cnt_factor, db->dma_fragsize); + dbg("numfrag=%d, dmasize=%d", db->numfrag, db->dmasize); +#endif + + db->ready = 1; + return 0; +} + +extern inline int prog_dmabuf_adc(struct au1000_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc); + +} + +extern inline int prog_dmabuf_dac(struct au1000_state *s) +{ + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac); +} + + +/* hold spinlock for the following */ +static void dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct au1000_state *s = (struct au1000_state *) dev_id; + struct dmabuf *dac = &s->dma_dac; + unsigned long newptr; + u32 ac97c_stat, buff_done; + + ac97c_stat = au_readl(AC97C_STATUS); +#ifdef AU1000_VERBOSE_DEBUG + if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE)) + dbg("AC97C status = 0x%08x", ac97c_stat); +#endif + + if ((buff_done = get_dma_buffer_done(dac->dmanr)) == 0) { + /* fastpath out, to ease interrupt sharing */ + return; + } + + spin_lock(&s->lock); + + if (buff_done != (DMA_D0 | DMA_D1)) { + dac->nextOut += dac->dma_fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + /* update playback pointers */ + newptr = virt_to_phys(dac->nextOut) + dac->dma_fragsize; + if (newptr >= dac->dmaaddr + dac->dmasize) + newptr -= dac->dmasize; + + dac->count -= dac->dma_fragsize; + dac->total_bytes += dac->dma_fragsize; + + if (dac->count <= 0) { +#ifdef AU1000_VERBOSE_DEBUG + dbg("dac underrun"); +#endif + spin_unlock(&s->lock); + stop_dac(s); + spin_lock(&s->lock); + dac->count = 0; + dac->nextIn = dac->nextOut; + } else if (buff_done == DMA_D0) { + clear_dma_done0(dac->dmanr); // clear DMA done bit + set_dma_count0(dac->dmanr, dac->dma_fragsize>>1); + set_dma_addr0(dac->dmanr, newptr); + enable_dma_buffer0(dac->dmanr); // reenable + } else { + clear_dma_done1(dac->dmanr); // clear DMA done bit + set_dma_count1(dac->dmanr, dac->dma_fragsize>>1); + set_dma_addr1(dac->dmanr, newptr); + enable_dma_buffer1(dac->dmanr); // reenable + } + } else { + // both done bits set, we missed an interrupt + spin_unlock(&s->lock); + stop_dac(s); + spin_lock(&s->lock); + + dac->nextOut += 2*dac->dma_fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + dac->count -= 2*dac->dma_fragsize; + dac->total_bytes += 2*dac->dma_fragsize; + + if (dac->count > 0) { + spin_unlock(&s->lock); + start_dac(s); + spin_lock(&s->lock); + } + } + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up(&dac->wait); + + spin_unlock(&s->lock); +} + + +static void adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct au1000_state *s = (struct au1000_state *) dev_id; + struct dmabuf *adc = &s->dma_adc; + unsigned long newptr; + u32 ac97c_stat, buff_done; + + ac97c_stat = au_readl(AC97C_STATUS); +#ifdef AU1000_VERBOSE_DEBUG + if (ac97c_stat & (AC97C_RU | AC97C_RO)) + dbg("AC97C status = 0x%08x", ac97c_stat); +#endif + + if ((buff_done = get_dma_buffer_done(adc->dmanr)) == 0) { + /* fastpath out, to ease interrupt sharing */ + return; + } + + spin_lock(&s->lock); + + if (buff_done != (DMA_D0 | DMA_D1)) { + if (adc->count + adc->dma_fragsize > adc->dmasize) { + // Overrun. Stop ADC and log the error + spin_unlock(&s->lock); + stop_adc(s); + adc->error++; + err("adc overrun"); + return; + } + + adc->nextIn += adc->dma_fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + /* update capture pointers */ + newptr = virt_to_phys(adc->nextIn) + adc->dma_fragsize; + if (newptr >= adc->dmaaddr + adc->dmasize) + newptr -= adc->dmasize; + + adc->count += adc->dma_fragsize; + adc->total_bytes += adc->dma_fragsize; + + if (buff_done == DMA_D0) { + clear_dma_done0(adc->dmanr); // clear DMA done bit + set_dma_count0(adc->dmanr, adc->dma_fragsize>>1); + set_dma_addr0(adc->dmanr, newptr); + enable_dma_buffer0(adc->dmanr); // reenable + } else { + clear_dma_done1(adc->dmanr); // clear DMA done bit + set_dma_count1(adc->dmanr, adc->dma_fragsize>>1); + set_dma_addr1(adc->dmanr, newptr); + enable_dma_buffer1(adc->dmanr); // reenable + } + } else { + // both done bits set, we missed an interrupt + spin_unlock(&s->lock); + stop_adc(s); + spin_lock(&s->lock); + + if (adc->count + 2*adc->dma_fragsize > adc->dmasize) { + // Overrun. Log the error + adc->error++; + err("adc overrun"); + spin_unlock(&s->lock); + return; + } + + adc->nextIn += 2*adc->dma_fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + adc->count += 2*adc->dma_fragsize; + adc->total_bytes += 2*adc->dma_fragsize; + + spin_unlock(&s->lock); + start_adc(s); + spin_lock(&s->lock); + } + + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) + wake_up(&adc->wait); + + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static loff_t au1000_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + +static int au1000_open_mixdev(struct inode *inode, struct file *file) +{ + file->private_data = &au1000_state; + return 0; +} + +static int au1000_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, + unsigned long arg) +{ + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int au1000_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct ac97_codec *codec = s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const */ struct file_operations au1000_mixer_fops = { + owner:THIS_MODULE, + llseek:au1000_llseek, + ioctl:au1000_ioctl_mixdev, + open:au1000_open_mixdev, + release:au1000_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct au1000_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped) + return 0; + + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) + return -EBUSY; + tmo = 1000 * count / (s->no_vra ? + 48000 : s->dma_dac.sample_rate); + tmo /= s->dma_dac.dma_bytes_per_sample; + au1000_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static inline u8 S16_TO_U8(s16 ch) +{ + return (u8) (ch >> 8) + 0x80; +} +static inline s16 U8_TO_S16(u8 ch) +{ + return (s16) (ch - 0x80) << 8; +} + +/* + * Translates user samples to dma buffer suitable for AC'97 DAC data: + * If mono, copy left channel to right channel in dma buffer. + * If 8 bit samples, cvt to 16-bit before writing to dma buffer. + * If interpolating (no VRA), duplicate every audio frame src_factor times. + */ +static int translate_from_user(struct dmabuf *db, + char* dmabuf, + char* userbuf, + int dmacount) +{ + int sample, i; + int interp_bytes_per_sample; + int num_samples; + int mono = (db->num_channels == 1); + char usersample[12]; + s16 ch, dmasample[6]; + + if (db->sample_size == 16 && !mono && db->src_factor == 1) { + // no translation necessary, just copy + if (copy_from_user(dmabuf, userbuf, dmacount)) + return -EFAULT; + return dmacount; + } + + interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; + num_samples = dmacount / interp_bytes_per_sample; + + for (sample = 0; sample < num_samples; sample++) { + if (copy_from_user(usersample, userbuf, + db->user_bytes_per_sample)) { + dbg(__FUNCTION__ ": fault"); + return -EFAULT; + } + + for (i = 0; i < db->num_channels; i++) { + if (db->sample_size == 8) + ch = U8_TO_S16(usersample[i]); + else + ch = *((s16 *) (&usersample[i * 2])); + dmasample[i] = ch; + if (mono) + dmasample[i + 1] = ch; // right channel + } + + // duplicate every audio frame src_factor times + for (i = 0; i < db->src_factor; i++) + memcpy(dmabuf, dmasample, db->dma_bytes_per_sample); + + userbuf += db->user_bytes_per_sample; + dmabuf += interp_bytes_per_sample; + } + + return num_samples * interp_bytes_per_sample; +} + +/* + * Translates AC'97 ADC samples to user buffer: + * If mono, send only left channel to user buffer. + * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer. + * If decimating (no VRA), skip over src_factor audio frames. + */ +static int translate_to_user(struct dmabuf *db, + char* userbuf, + char* dmabuf, + int dmacount) +{ + int sample, i; + int interp_bytes_per_sample; + int num_samples; + int mono = (db->num_channels == 1); + char usersample[12]; + + if (db->sample_size == 16 && !mono && db->src_factor == 1) { + // no translation necessary, just copy + if (copy_to_user(userbuf, dmabuf, dmacount)) + return -EFAULT; + return dmacount; + } + + interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; + num_samples = dmacount / interp_bytes_per_sample; + + for (sample = 0; sample < num_samples; sample++) { + for (i = 0; i < db->num_channels; i++) { + if (db->sample_size == 8) + usersample[i] = + S16_TO_U8(*((s16 *) (&dmabuf[i * 2]))); + else + *((s16 *) (&usersample[i * 2])) = + *((s16 *) (&dmabuf[i * 2])); + } + + if (copy_to_user(userbuf, usersample, + db->user_bytes_per_sample)) { + dbg(__FUNCTION__ ": fault"); + return -EFAULT; + } + + userbuf += db->user_bytes_per_sample; + dmabuf += interp_bytes_per_sample; + } + + return num_samples * interp_bytes_per_sample; +} + +/* + * Copy audio data to/from user buffer from/to dma buffer, taking care + * that we wrap when reading/writing the dma buffer. Returns actual byte + * count written to or read from the dma buffer. + */ +static int copy_dmabuf_user(struct dmabuf *db, char* userbuf, + int count, int to_user) +{ + char *bufptr = to_user ? db->nextOut : db->nextIn; + char *bufend = db->rawbuf + db->dmasize; + int cnt, ret; + + if (bufptr + count > bufend) { + int partial = (int) (bufend - bufptr); + if (to_user) { + if ((cnt = translate_to_user(db, userbuf, + bufptr, partial)) < 0) + return cnt; + ret = cnt; + if ((cnt = translate_to_user(db, userbuf + partial, + db->rawbuf, + count - partial)) < 0) + return cnt; + ret += cnt; + } else { + if ((cnt = translate_from_user(db, bufptr, userbuf, + partial)) < 0) + return cnt; + ret = cnt; + if ((cnt = translate_from_user(db, db->rawbuf, + userbuf + partial, + count - partial)) < 0) + return cnt; + ret += cnt; + } + } else { + if (to_user) + ret = translate_to_user(db, userbuf, bufptr, count); + else + ret = translate_from_user(db, bufptr, userbuf, count); + } + + return ret; +} + + +static ssize_t au1000_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + int cnt, usercnt, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + count *= db->cnt_factor; + + down(&s->sem); + add_wait_queue(&db->wait, &wait); + + while (count > 0) { + // wait for samples in ADC dma buffer + do { + if (db->stopped) + start_adc(s); + spin_lock_irqsave(&s->lock, flags); + avail = db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + down(&s->sem); + } + } while (avail <= 0); + + // copy from nextOut to user + if ((cnt = copy_dmabuf_user(db, buffer, + count > avail ? + avail : count, 1)) < 0) { + if (!ret) + ret = -EFAULT; + goto out; + } + + spin_lock_irqsave(&s->lock, flags); + db->count -= cnt; + db->nextOut += cnt; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); + + count -= cnt; + usercnt = cnt / db->cnt_factor; + buffer += usercnt; + ret += usercnt; + } // while (count > 0) + +out: + up(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t au1000_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + int cnt, usercnt, avail; + +#ifdef AU1000_VERBOSE_DEBUG + dbg("write: count=%d", count); +#endif + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + count *= db->cnt_factor; + + down(&s->sem); + add_wait_queue(&db->wait, &wait); + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = (int) db->dmasize - db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + down(&s->sem); + } + } while (avail <= 0); + + // copy from user to nextIn + if ((cnt = copy_dmabuf_user(db, (char *) buffer, + count > avail ? + avail : count, 0)) < 0) { + if (!ret) + ret = -EFAULT; + goto out; + } + + spin_lock_irqsave(&s->lock, flags); + db->count += cnt; + db->nextIn += cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); + if (db->stopped) + start_dac(s); + + count -= cnt; + usercnt = cnt / db->cnt_factor; + buffer += usercnt; + ret += usercnt; + } // while (count > 0) + +out: + up(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + + +/* No kernel lock - we have our own spinlock */ +static unsigned int au1000_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed)s->dma_dac.dma_fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed) s->dma_dac.dmasize >= + s->dma_dac.count + (signed)s->dma_dac.dma_fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int au1000_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db; + unsigned long size; + int ret = 0; + + dbg(__FUNCTION__); + + lock_kernel(); + down(&s->sem); + if (vma->vm_flags & VM_WRITE) + db = &s->dma_dac; + else if (vma->vm_flags & VM_READ) + db = &s->dma_adc; + else { + ret = -EINVAL; + goto out; + } + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + ret = -EINVAL; + goto out; + } + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), + size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + vma->vm_flags &= ~VM_IO; + db->mapped = 1; +out: + up(&s->sem); + unlock_kernel(); + return ret; +} + + +#ifdef AU1000_VERBOSE_DEBUG +static struct ioctl_str_t { + unsigned int cmd; + const char *str; +} ioctl_str[] = { + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} +}; +#endif + +// Need to hold a spin-lock before calling this! +static int dma_count_done(struct dmabuf *db) +{ + if (db->stopped) + return 0; + + return db->dma_fragsize - get_dma_residue(db->dmanr); +} + + +static int au1000_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret, diff; + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + +#ifdef AU1000_VERBOSE_DEBUG + for (count=0; countf_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = + s->dma_dac.rawbuf; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = + s->dma_adc.rawbuf; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + } + if (s->open_mode & FMODE_READ) + if ((ret = prog_dmabuf_adc(s))) + return ret; + if (s->open_mode & FMODE_WRITE) + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return put_user((file->f_mode & FMODE_READ) ? + s->dma_adc.sample_rate : + s->dma_dac.sample_rate, + (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.num_channels = val ? 2 : 1; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.num_channels = val ? 2 : 1; + if (s->codec_ext_caps & AC97_EXT_DACS) { + // disable surround and center/lfe in AC'97 + u16 ext_stat = rdcodec(s->codec, + AC97_EXTENDED_STATUS); + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ext_stat | (AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRJ | + AC97_EXTSTAT_PRK)); + } + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + if (val < 0 || val > 2) + return -EINVAL; + stop_adc(s); + s->dma_adc.num_channels = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + switch (val) { + case 1: + case 2: + break; + case 3: + case 5: + return -EINVAL; + case 4: + if (!(s->codec_ext_caps & + AC97_EXTID_SDAC)) + return -EINVAL; + break; + case 6: + if ((s->codec_ext_caps & + AC97_EXT_DACS) != AC97_EXT_DACS) + return -EINVAL; + break; + default: + return -EINVAL; + } + + stop_dac(s); + if (val <= 2 && + (s->codec_ext_caps & AC97_EXT_DACS)) { + // disable surround and center/lfe + // channels in AC'97 + u16 ext_stat = + rdcodec(s->codec, + AC97_EXTENDED_STATUS); + wrcodec(s->codec, + AC97_EXTENDED_STATUS, + ext_stat | (AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRJ | + AC97_EXTSTAT_PRK)); + } else if (val >= 4) { + // enable surround, center/lfe + // channels in AC'97 + u16 ext_stat = + rdcodec(s->codec, + AC97_EXTENDED_STATUS); + ext_stat &= ~AC97_EXTSTAT_PRJ; + if (val == 6) + ext_stat &= + ~(AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRK); + wrcodec(s->codec, + AC97_EXTENDED_STATUS, + ext_stat); + } + + s->dma_dac.num_channels = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */ + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val == AFMT_S16_LE) + s->dma_adc.sample_size = 16; + else { + val = AFMT_U8; + s->dma_adc.sample_size = 8; + } + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val == AFMT_S16_LE) + s->dma_dac.sample_size = 16; + else { + val = AFMT_U8; + s->dma_dac.sample_size = 8; + } + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + if (file->f_mode & FMODE_READ) + val = (s->dma_adc.sample_size == 16) ? + AFMT_S16_LE : AFMT_U8; + else + val = (s->dma_dac.sample_size == 16) ? + AFMT_S16_LE : AFMT_U8; + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) + val |= PCM_ENABLE_OUTPUT; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) + start_adc(s); + else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) + start_dac(s); + else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + count -= dma_count_done(&s->dma_dac); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = (s->dma_dac.dmasize - count) / + s->dma_dac.cnt_factor; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; +#ifdef AU1000_VERBOSE_DEBUG + dbg("bytes=%d, fragments=%d", abinfo.bytes, abinfo.fragments); +#endif + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + count += dma_count_done(&s->dma_adc); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count / s->dma_adc.cnt_factor; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + count -= dma_count_done(&s->dma_dac); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + count /= s->dma_dac.cnt_factor; + return put_user(count, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (!s->dma_adc.stopped) { + diff = dma_count_done(&s->dma_adc); + count += diff; + cinfo.bytes += diff; + cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff - + s->dma_adc.dmaaddr; + } else + cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) - + s->dma_adc.dmaaddr; + if (s->dma_adc.mapped) + s->dma_adc.count &= (s->dma_adc.dma_fragsize-1); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (!s->dma_dac.stopped) { + diff = dma_count_done(&s->dma_dac); + count -= diff; + cinfo.bytes += diff; + cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff - + s->dma_dac.dmaaddr; + } else + cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) - + s->dma_dac.dmaaddr; + if (s->dma_dac.mapped) + s->dma_dac.count &= (s->dma_dac.dma_fragsize-1); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragsize, (int *) arg); + else + return put_user(s->dma_adc.fragsize, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.subdivision = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.subdivision = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->dma_adc.sample_rate : + s->dma_dac.sample_rate, + (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->dma_adc.num_channels, (int *)arg); + else + return put_user(s->dma_dac.num_channels, (int *)arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return put_user(s->dma_adc.sample_size, (int *)arg); + else + return put_user(s->dma_dac.sample_size, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + + return mixdev_ioctl(s->codec, cmd, arg); +} + + +static int au1000_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct au1000_state *s = &au1000_state; + int ret; + +#ifdef AU1000_VERBOSE_DEBUG + if (file->f_flags & O_NONBLOCK) + dbg(__FUNCTION__ ": non-blocking"); + else + dbg(__FUNCTION__ ": blocking"); +#endif + + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + stop_dac(s); + stop_adc(s); + + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; + s->dma_adc.num_channels = 1; + s->dma_adc.sample_size = 8; + set_adc_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->dma_adc.sample_size = 16; + } + + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; + s->dma_dac.num_channels = 1; + s->dma_dac.sample_size = 8; + set_dac_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->dma_dac.sample_size = 16; + } + + if (file->f_mode & FMODE_READ) { + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + init_MUTEX(&s->sem); + return 0; +} + +static int au1000_release(struct inode *inode, struct file *file) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + + lock_kernel(); + + if (file->f_mode & FMODE_WRITE) { + unlock_kernel(); + drain_dac(s, file->f_flags & O_NONBLOCK); + lock_kernel(); + } + + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const */ struct file_operations au1000_audio_fops = { + owner: THIS_MODULE, + llseek: au1000_llseek, + read: au1000_read, + write: au1000_write, + poll: au1000_poll, + ioctl: au1000_ioctl, + mmap: au1000_mmap, + open: au1000_open, + release: au1000_release, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef AU1000_DEBUG +static int proc_au1000_dump(char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct au1000_state *s = &au1000_state; + int cnt, len = 0; + + /* print out header */ + len += sprintf(buf + len, "\n\t\tAU1000 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf(buf + len, "AU1000 Audio Controller registers\n"); + len += sprintf(buf + len, "---------------------------------\n"); + len += sprintf (buf + len, "AC97C_CONFIG = %08x\n", + au_readl(AC97C_CONFIG)); + len += sprintf (buf + len, "AC97C_STATUS = %08x\n", + au_readl(AC97C_STATUS)); + len += sprintf (buf + len, "AC97C_CNTRL = %08x\n", + au_readl(AC97C_CNTRL)); + + /* print out CODEC state */ + len += sprintf(buf + len, "\nAC97 CODEC registers\n"); + len += sprintf(buf + len, "----------------------\n"); + for (cnt = 0; cnt <= 0x7e; cnt += 2) + len += sprintf(buf + len, "reg %02x = %04x\n", + cnt, rdcodec(s->codec, cnt)); + + if (fpos >= len) { + *start = buf; + *eof = 1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof = 1; + return len; + +} +#endif /* AU1000_DEBUG */ + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); +MODULE_DESCRIPTION("Au1000 Audio Driver"); + +/* --------------------------------------------------------------------- */ + +static int __devinit au1000_probe(void) +{ + struct au1000_state *s = &au1000_state; + int val; + char proc_str[80]; + + memset(s, 0, sizeof(struct au1000_state)); + + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + + s->codec = ac97_alloc_codec(); + if(s->codec == NULL) + { + error("Out of memory"); + return -1; + } + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; + s->codec->codec_wait = waitcodec; + + if (!request_region(virt_to_phys((void *) AC97C_CONFIG), + 0x14, AU1000_MODULE_NAME)) { + err("AC'97 ports in use"); + goto err_codec; + } + // Allocate the DMA Channels + if ((s->dma_dac.dmanr = request_au1000_dma(DMA_ID_AC97C_TX, + "audio DAC", + dac_dma_interrupt, + SA_INTERRUPT, s)) < 0) { + err("Can't get DAC DMA"); + goto err_dma1; + } + if ((s->dma_adc.dmanr = request_au1000_dma(DMA_ID_AC97C_RX, + "audio ADC", + adc_dma_interrupt, + SA_INTERRUPT, s)) < 0) { + err("Can't get ADC DMA"); + goto err_dma2; + } + + info("DAC: DMA%d/IRQ%d, ADC: DMA%d/IRQ%d", + s->dma_dac.dmanr, get_dma_done_irq(s->dma_dac.dmanr), + s->dma_adc.dmanr, get_dma_done_irq(s->dma_adc.dmanr)); + +#ifdef USE_COHERENT_DMA + // enable DMA coherency in read/write DMA channels + set_dma_mode(s->dma_dac.dmanr, + get_dma_mode(s->dma_dac.dmanr) & ~DMA_NC); + set_dma_mode(s->dma_adc.dmanr, + get_dma_mode(s->dma_adc.dmanr) & ~DMA_NC); +#else + // disable DMA coherency in read/write DMA channels + set_dma_mode(s->dma_dac.dmanr, + get_dma_mode(s->dma_dac.dmanr) | DMA_NC); + set_dma_mode(s->dma_adc.dmanr, + get_dma_mode(s->dma_adc.dmanr) | DMA_NC); +#endif + + /* register devices */ + + if ((s->dev_audio = register_sound_dsp(&au1000_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec->dev_mixer = + register_sound_mixer(&au1000_mixer_fops, -1)) < 0) + goto err_dev2; + +#ifdef AU1000_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(AU1000_MODULE_NAME, 0, NULL, + proc_au1000_dump, NULL); +#endif /* AU1000_DEBUG */ + + // configure pins for AC'97 + au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC); + + // Assert reset for 10msec to the AC'97 controller, and enable clock + au_writel(AC97C_RS | AC97C_CE, AC97C_CNTRL); + au1000_delay(10); + au_writel(AC97C_CE, AC97C_CNTRL); + au1000_delay(10); // wait for clock to stabilize + + /* cold reset the AC'97 */ + au_writel(AC97C_RESET, AC97C_CONFIG); + au1000_delay(10); + au_writel(0, AC97C_CONFIG); + /* need to delay around 500msec(bleech) to give + some CODECs enough time to wakeup */ + au1000_delay(500); + + /* warm reset the AC'97 to start the bitclk */ + au_writel(AC97C_SG | AC97C_SYNC, AC97C_CONFIG); + udelay(100); + au_writel(0, AC97C_CONFIG); + + /* codec init */ + if (!ac97_probe_codec(s->codec)) + goto err_dev3; + + s->codec_base_caps = rdcodec(s->codec, AC97_RESET); + s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID); + info("AC'97 Base/Extended ID = %04x/%04x", + s->codec_base_caps, s->codec_ext_caps); + + /* + * On the Pb1000, audio playback is on the AUX_OUT + * channel (which defaults to LNLVL_OUT in AC'97 + * rev 2.2) so make sure this channel is listed + * as supported (soundcard.h calls this channel + * ALTPCM). ac97_codec.c does not handle detection + * of this channel correctly. + */ + s->codec->supported_mixers |= SOUND_MASK_ALTPCM; + /* + * Now set AUX_OUT's default volume. + */ + val = 0x4343; + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_ALTPCM, + (unsigned long) &val); + + if (!(s->codec_ext_caps & AC97_EXTID_VRA)) { + // codec does not support VRA + s->no_vra = 1; + } else if (!vra) { + // Boot option says disable VRA + u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS); + wrcodec(s->codec, AC97_EXTENDED_STATUS, + ac97_extstat & ~AC97_EXTSTAT_VRA); + s->no_vra = 1; + } + if (s->no_vra) + info("no VRA, interpolating and decimating"); + + /* set mic to be the recording source */ + val = SOUND_MASK_MIC; + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC, + (unsigned long) &val); + +#ifdef AU1000_DEBUG + sprintf(proc_str, "driver/%s/%d/ac97", AU1000_MODULE_NAME, + s->codec->id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, s->codec); +#endif + + return 0; + + err_dev3: + unregister_sound_mixer(s->codec->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_au1000_dma(s->dma_adc.dmanr); + err_dma2: + free_au1000_dma(s->dma_dac.dmanr); + err_dma1: + release_region(virt_to_phys((void *) AC97C_CONFIG), 0x14); + err_codec: + ac97_release_codec(s->codec); + return -1; +} + +static void __devinit au1000_remove(void) +{ + struct au1000_state *s = &au1000_state; + + if (!s) + return; +#ifdef AU1000_DEBUG + if (s->ps) + remove_proc_entry(AU1000_MODULE_NAME, NULL); +#endif /* AU1000_DEBUG */ + synchronize_irq(); + free_au1000_dma(s->dma_adc.dmanr); + free_au1000_dma(s->dma_dac.dmanr); + release_region(virt_to_phys((void *) AC97C_CONFIG), 0x14); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec->dev_mixer); + ac97_release_codec(s->codec); +} + +static int __init init_au1000(void) +{ + info("stevel@mvista.com, built " __TIME__ " on " __DATE__); + return au1000_probe(); +} + +static void __exit cleanup_au1000(void) +{ + info("unloading"); + au1000_remove(); +} + +module_init(init_au1000); +module_exit(cleanup_au1000); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +static int __init au1000_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + for(this_opt=strtok(options, ","); + this_opt; this_opt=strtok(NULL, ",")) { + if (!strncmp(this_opt, "vra", 3)) { + vra = 1; + } + } + + return 1; +} + +__setup("au1000_audio=", au1000_setup); + +#endif /* MODULE */ diff -urN linux-2.5.75-bk1/sound/oss/btaudio.c linux-2.5.75-bk2/sound/oss/btaudio.c --- linux-2.5.75-bk1/sound/oss/btaudio.c 2003-07-10 13:06:05.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/btaudio.c 2003-07-12 04:37:48.000000000 -0700 @@ -328,8 +328,8 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; memset(&info,0,sizeof(info)); - strlcpy(info.id,"bt878",sizeof(info.id)); - strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); + strncpy(info.id,"bt878",sizeof(info.id)); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); info.modify_counter = bta->mixcount; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -338,8 +338,8 @@ if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; memset(&info,0,sizeof(info)); - strlcpy(info.id,"bt878",sizeof(info.id)-1); - strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); + strncpy(info.id,"bt878",sizeof(info.id)-1); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -urN linux-2.5.75-bk1/sound/oss/cmpci.c linux-2.5.75-bk2/sound/oss/cmpci.c --- linux-2.5.75-bk1/sound/oss/cmpci.c 2003-07-10 13:10:16.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/cmpci.c 2003-07-12 04:37:48.000000000 -0700 @@ -1272,8 +1272,8 @@ VALIDATE_STATE(s); if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "cmpci", sizeof(info.id)); - strlcpy(info.name, "C-Media PCI", sizeof(info.name)); + strncpy(info.id, "cmpci", sizeof(info.id)); + strncpy(info.name, "C-Media PCI", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -1281,8 +1281,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "cmpci", sizeof(info.id)); - strlcpy(info.name, "C-Media cmpci", sizeof(info.name)); + strncpy(info.id, "cmpci", sizeof(info.id)); + strncpy(info.name, "C-Media cmpci", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -urN linux-2.5.75-bk1/sound/oss/cs46xx.c linux-2.5.75-bk2/sound/oss/cs46xx.c --- linux-2.5.75-bk1/sound/oss/cs46xx.c 2003-07-10 13:04:01.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/cs46xx.c 2003-07-12 04:37:48.000000000 -0700 @@ -311,6 +311,9 @@ /* The cs461x has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; + + /* Keep AC97 sane */ + spinlock_t ac97_lock; /* mixer use count */ atomic_t mixer_use_cnt; @@ -1014,7 +1017,7 @@ } /* - * ganularity is byte boundary, good part. + * granularity is byte boundary, good part. */ if(dmabuf->enable & DAC_RUNNING) { @@ -1195,7 +1198,7 @@ // 2. mark each physical page in range as 'reserved'. for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) - SetPageReserved(map); + cs4x_mem_map_reserve(map); CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: alloc_dmabuf(): allocated %ld (order = %d) bytes at %p\n", PAGE_SIZE << order, order, rawbuf) ); @@ -1232,7 +1235,7 @@ // 2. mark each physical page in range as 'reserved'. for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) - SetPageReserved(map); + cs4x_mem_map_reserve(map); return 0; } @@ -1247,7 +1250,7 @@ mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) - ClearPageReserved(map); + cs4x_mem_map_unreserve(map); free_dmabuf(state->card, dmabuf); } @@ -1256,7 +1259,7 @@ mapend = virt_to_page(dmabuf->tmpbuff + (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) - ClearPageReserved(map); + cs4x_mem_map_unreserve(map); free_dmabuf2(state->card, dmabuf); } @@ -1910,11 +1913,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&card->midi.owait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG "cs46xx: midi timed out??\n"); @@ -2117,7 +2117,7 @@ down(&state->sem); if (!dmabuf->ready && (ret = __prog_dmabuf(state))) - goto out; + goto out2; add_wait_queue(&state->dmabuf.wait, &wait); while (count > 0) { @@ -2187,8 +2187,9 @@ start_adc(state); } out: - up(&state->sem); remove_wait_queue(&state->dmabuf.wait, &wait); +out2: + up(&state->sem); set_current_state(TASK_RUNNING); CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, printk("cs46xx: cs_read()- %d\n",ret) ); @@ -2213,6 +2214,8 @@ state = (struct cs_state *)card->states[1]; if(!state) return -ENODEV; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; dmabuf = &state->dmabuf; if (ppos != &file->f_pos) @@ -2227,11 +2230,6 @@ if (!dmabuf->ready && (ret = __prog_dmabuf(state))) goto out; - if (!access_ok(VERIFY_READ, buffer, count)) - { - ret = -EFAULT; - goto out; - } add_wait_queue(&state->dmabuf.wait, &wait); ret = 0; /* @@ -3007,7 +3005,7 @@ return -ENODEV; case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; + return 0; case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) @@ -3832,11 +3830,12 @@ /* Write AC97 codec registers */ -static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg) +static u16 _cs_ac97_get(struct ac97_codec *dev, u8 reg) { struct cs_card *card = dev->private_data; int count,loopcnt; unsigned int tmp; + u16 ret; /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address @@ -3847,7 +3846,6 @@ * 6. Read ACSTS = Status Register = 464h, check VSTS bit */ - cs461x_peekBA0(card, BA0_ACSDA); /* @@ -3938,7 +3936,19 @@ "cs46xx: cs_ac97_get() reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg, cs461x_peekBA0(card, BA0_ACSDA), cs461x_peekBA0(card, BA0_ACCAD))); - return(cs461x_peekBA0(card, BA0_ACSDA)); + ret = cs461x_peekBA0(card, BA0_ACSDA); + return ret; +} + +static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg) +{ + u16 ret; + struct cs_card *card = dev->private_data; + + spin_lock(&card->ac97_lock); + ret = _cs_ac97_get(dev, reg); + spin_unlock(&card->ac97_lock); + return ret; } static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val) @@ -3947,11 +3957,14 @@ int count; int val2 = 0; + spin_lock(&card->ac97_lock); + if(reg == AC97_CD_VOL) { - val2 = cs_ac97_get(dev, AC97_CD_VOL); + val2 = _cs_ac97_get(dev, AC97_CD_VOL); } + /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 @@ -3999,6 +4012,8 @@ "cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val)); } + spin_unlock(&card->ac97_lock); + /* * Adjust power if the mixer is selected/deselected according * to the CD. @@ -4244,9 +4259,8 @@ "cs46xx: cs_ac97_init()+\n") ); for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -4269,10 +4283,10 @@ eid = cs_ac97_get(codec, AC97_EXTENDED_ID); - if(eid==0xFFFFFF) + if(eid==0xFFFF) { printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97); - kfree(codec); + ac97_release_codec(codec); break; } @@ -4280,7 +4294,7 @@ if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) { printk(KERN_ERR "cs46xx: couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); break; } card->ac97_codec[num_ac97] = codec; @@ -5298,6 +5312,12 @@ .name = "Hercules Game Theatre XP", .amp = amp_hercules, }, + { + .vendor = 0x1681, + .id = 0xa010, + .name = "Hercules Fortissimo II", + .amp = amp_none, + }, /* Not sure if the 570 needs the clkrun hack */ { .vendor = PCI_VENDOR_ID_IBM, @@ -5381,6 +5401,7 @@ card->irq = pci_dev->irq; card->magic = CS_CARD_MAGIC; spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); pci_set_master(pci_dev); @@ -5508,7 +5529,7 @@ for (j = 0; j < NR_AC97; j++) if (card->ac97_codec[j] != NULL) { unregister_sound_mixer(card->ac97_codec[j]->dev_mixer); - kfree (card->ac97_codec[j]); + ac97_release_codec(card->ac97_codec[j]); } mdelay(10 * cs_laptop_wait); continue; @@ -5665,7 +5686,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } unregister_sound_dsp(card->dev_audio); if(card->dev_midi) diff -urN linux-2.5.75-bk1/sound/oss/cs46xx_wrapper-24.h linux-2.5.75-bk2/sound/oss/cs46xx_wrapper-24.h --- linux-2.5.75-bk1/sound/oss/cs46xx_wrapper-24.h 2003-07-10 13:12:09.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/cs46xx_wrapper-24.h 2003-07-12 04:37:48.000000000 -0700 @@ -31,6 +31,9 @@ #define CS_OWNER owner: #define CS_THIS_MODULE THIS_MODULE, void cs46xx_null(struct pci_dev *pcidev) { return; } +#define cs4x_mem_map_reserve(page) SetPageReserved(page) +#define cs4x_mem_map_unreserve(page) ClearPageReserved(page) + #define free_dmabuf(card, dmabuf) \ pci_free_consistent((card)->pci_dev, \ PAGE_SIZE << (dmabuf)->buforder, \ diff -urN linux-2.5.75-bk1/sound/oss/dmasound/dmasound_core.c linux-2.5.75-bk2/sound/oss/dmasound/dmasound_core.c --- linux-2.5.75-bk1/sound/oss/dmasound/dmasound_core.c 2003-07-10 13:05:30.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/dmasound/dmasound_core.c 2003-07-12 04:37:48.000000000 -0700 @@ -351,8 +351,8 @@ case SOUND_MIXER_INFO: { mixer_info info; - strlcpy(info.id, dmasound.mach.name2, sizeof(info.id)); - strlcpy(info.name, dmasound.mach.name2, sizeof(info.name)); + strncpy(info.id, dmasound.mach.name2, sizeof(info.id)); + strncpy(info.name, dmasound.mach.name2, sizeof(info.name)); info.modify_counter = mixer.modify_counter; if (copy_to_user((int *)arg, &info, sizeof(info))) return -EFAULT; diff -urN linux-2.5.75-bk1/sound/oss/emu10k1/efxmgr.c linux-2.5.75-bk2/sound/oss/emu10k1/efxmgr.c --- linux-2.5.75-bk1/sound/oss/emu10k1/efxmgr.c 2003-07-10 13:04:49.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/emu10k1/efxmgr.c 2003-07-12 04:37:48.000000000 -0700 @@ -101,10 +101,10 @@ { extern char volume_params[SOUND_MIXER_NRDEVICES]; - card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + card->ac97->mixer_state[oss_mixer] = (right << 8) | left; if (!card->is_aps) - card->ac97.write_mixer(&card->ac97, oss_mixer, left, right); + card->ac97->write_mixer(card->ac97, oss_mixer, left, right); emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, volume_params[oss_mixer]); @@ -125,7 +125,7 @@ right = (val >> 8) & 0xff; val = 0; } else { - val = card->ac97.mixer_state[oss_channel]; + val = card->ac97->mixer_state[oss_channel]; left = 0; right = 0; } @@ -138,8 +138,8 @@ int oss_channel = VOLCTRL_CHANNEL; int left, right; - left = card->ac97.mixer_state[oss_channel] & 0xff; - right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; + left = card->ac97->mixer_state[oss_channel] & 0xff; + right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff; if ((left += VOLCTRL_STEP_SIZE) > 100) left = 100; @@ -155,8 +155,8 @@ int oss_channel = VOLCTRL_CHANNEL; int left, right; - left = card->ac97.mixer_state[oss_channel] & 0xff; - right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; + left = card->ac97->mixer_state[oss_channel] & 0xff; + right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff; if ((left -= VOLCTRL_STEP_SIZE) < 0) left = 0; diff -urN linux-2.5.75-bk1/sound/oss/emu10k1/hwaccess.h linux-2.5.75-bk2/sound/oss/emu10k1/hwaccess.h --- linux-2.5.75-bk1/sound/oss/emu10k1/hwaccess.h 2003-07-10 13:08:10.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/emu10k1/hwaccess.h 2003-07-12 04:37:48.000000000 -0700 @@ -162,7 +162,7 @@ struct emu10k1_mididevice *seq_mididev; #endif - struct ac97_codec ac97; + struct ac97_codec *ac97; int ac97_supported_mixers; int ac97_stereo_mixers; diff -urN linux-2.5.75-bk1/sound/oss/emu10k1/main.c linux-2.5.75-bk2/sound/oss/emu10k1/main.c --- linux-2.5.75-bk1/sound/oss/emu10k1/main.c 2003-07-10 13:15:00.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/emu10k1/main.c 2003-07-12 04:37:48.000000000 -0700 @@ -223,21 +223,31 @@ { char s[32]; - struct ac97_codec *codec = &card->ac97; - card->ac97.dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1); - if (card->ac97.dev_mixer < 0) { - printk(KERN_ERR "emu10k1: cannot register mixer device\n"); + struct ac97_codec *codec = ac97_alloc_codec(); + + if(codec == NULL) + { + printk(KERN_ERR "emu10k1: cannot allocate mixer\n"); return -EIO; + } + card->ac97 = codec; + +#warning "Initialisation order race. Must register after usable" + + card->ac97->dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1); + if (card->ac97->dev_mixer < 0) { + printk(KERN_ERR "emu10k1: cannot register mixer device\n"); + goto err_codec; } - card->ac97.private_data = card; + card->ac97->private_data = card; if (!card->is_aps) { - card->ac97.id = 0; - card->ac97.codec_read = emu10k1_ac97_read; - card->ac97.codec_write = emu10k1_ac97_write; + card->ac97->id = 0; + card->ac97->codec_read = emu10k1_ac97_read; + card->ac97->codec_write = emu10k1_ac97_write; - if (ac97_probe_codec (&card->ac97) == 0) { + if (ac97_probe_codec (card->ac97) == 0) { printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n"); goto err_out; } @@ -249,7 +259,7 @@ } // Force 5bit: - //card->ac97.bit_resolution=5; + //card->ac97->bit_resolution=5; if (!proc_mkdir ("driver/emu10k1", 0)) { printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n"); @@ -263,14 +273,14 @@ } sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); - if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, card->ac97)) { printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s); goto err_ac97_proc; } /* these will store the original values and never be modified */ - card->ac97_supported_mixers = card->ac97.supported_mixers; - card->ac97_stereo_mixers = card->ac97.stereo_mixers; + card->ac97_supported_mixers = card->ac97->supported_mixers; + card->ac97_stereo_mixers = card->ac97->stereo_mixers; } return 0; @@ -282,7 +292,9 @@ err_emu10k1_proc: remove_proc_entry("driver/emu10k1", NULL); err_out: - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); + err_codec: + ac97_release_codec(card->ac97); return -EIO; } @@ -300,7 +312,8 @@ remove_proc_entry("driver/emu10k1", NULL); } - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); + ac97_release_codec(card->ac97); } static int __devinit emu10k1_midi_init(struct emu10k1_card *card) @@ -694,21 +707,21 @@ mgr->ctrl_gpr[SOUND_MIXER_VOLUME][0] = 8; mgr->ctrl_gpr[SOUND_MIXER_VOLUME][1] = 9; - left = card->ac97.mixer_state[SOUND_MIXER_VOLUME] & 0xff; - right = (card->ac97.mixer_state[SOUND_MIXER_VOLUME] >> 8) & 0xff; + left = card->ac97->mixer_state[SOUND_MIXER_VOLUME] & 0xff; + right = (card->ac97->mixer_state[SOUND_MIXER_VOLUME] >> 8) & 0xff; - emu10k1_set_volume_gpr(card, 8, left, 1 << card->ac97.bit_resolution); - emu10k1_set_volume_gpr(card, 9, right, 1 << card->ac97.bit_resolution); + emu10k1_set_volume_gpr(card, 8, left, 1 << card->ac97->bit_resolution); + emu10k1_set_volume_gpr(card, 9, right, 1 << card->ac97->bit_resolution); //Rear volume mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][0] = 0x19; mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][1] = 0x1a; left = right = 67; - card->ac97.mixer_state[SOUND_MIXER_OGAIN] = (right << 8) | left; + card->ac97->mixer_state[SOUND_MIXER_OGAIN] = (right << 8) | left; - card->ac97.supported_mixers |= SOUND_MASK_OGAIN; - card->ac97.stereo_mixers |= SOUND_MASK_OGAIN; + card->ac97->supported_mixers |= SOUND_MASK_OGAIN; + card->ac97->stereo_mixers |= SOUND_MASK_OGAIN; emu10k1_set_volume_gpr(card, 0x19, left, VOL_5BIT); emu10k1_set_volume_gpr(card, 0x1a, right, VOL_5BIT); @@ -717,8 +730,8 @@ mgr->ctrl_gpr[SOUND_MIXER_PCM][0] = 6; mgr->ctrl_gpr[SOUND_MIXER_PCM][1] = 7; - left = card->ac97.mixer_state[SOUND_MIXER_PCM] & 0xff; - right = (card->ac97.mixer_state[SOUND_MIXER_PCM] >> 8) & 0xff; + left = card->ac97->mixer_state[SOUND_MIXER_PCM] & 0xff; + right = (card->ac97->mixer_state[SOUND_MIXER_PCM] >> 8) & 0xff; emu10k1_set_volume_gpr(card, 6, left, VOL_5BIT); emu10k1_set_volume_gpr(card, 7, right, VOL_5BIT); @@ -728,22 +741,22 @@ mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][1] = 0xf; left = right = 67; - card->ac97.mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; + card->ac97->mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; - card->ac97.supported_mixers |= SOUND_MASK_DIGITAL1; - card->ac97.stereo_mixers |= SOUND_MASK_DIGITAL1; + card->ac97->supported_mixers |= SOUND_MASK_DIGITAL1; + card->ac97->stereo_mixers |= SOUND_MASK_DIGITAL1; emu10k1_set_volume_gpr(card, 0xd, left, VOL_5BIT); emu10k1_set_volume_gpr(card, 0xf, right, VOL_5BIT); //hard wire the ac97's pcm, we'll do that in dsp code instead. - emu10k1_ac97_write(&card->ac97, 0x18, 0x0); + emu10k1_ac97_write(card->ac97, 0x18, 0x0); card->ac97_supported_mixers &= ~SOUND_MASK_PCM; card->ac97_stereo_mixers &= ~SOUND_MASK_PCM; //set Igain to 0dB by default, maybe consider hardwiring it here. - emu10k1_ac97_write(&card->ac97, AC97_RECORD_GAIN, 0x0000); - card->ac97.mixer_state[SOUND_MIXER_IGAIN] = 0x101; + emu10k1_ac97_write(card->ac97, AC97_RECORD_GAIN, 0x0000); + card->ac97->mixer_state[SOUND_MIXER_IGAIN] = 0x101; return 0; } diff -urN linux-2.5.75-bk1/sound/oss/emu10k1/mixer.c linux-2.5.75-bk2/sound/oss/emu10k1/mixer.c --- linux-2.5.75-bk1/sound/oss/emu10k1/mixer.c 2003-07-10 13:12:25.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/emu10k1/mixer.c 2003-07-12 04:37:48.000000000 -0700 @@ -457,29 +457,29 @@ break; if (addr >= 0) { - unsigned int state = card->ac97.mixer_state[id]; + unsigned int state = card->ac97->mixer_state[id]; if (ch == 1) { state >>= 8; - card->ac97.stereo_mixers |= (1 << id); + card->ac97->stereo_mixers |= (1 << id); } - card->ac97.supported_mixers |= (1 << id); + card->ac97->supported_mixers |= (1 << id); if (id == SOUND_MIXER_TREBLE) { - set_treble(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff); + set_treble(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff); } else if (id == SOUND_MIXER_BASS) { - set_bass(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff); + set_bass(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff); } else emu10k1_set_volume_gpr(card, addr, state & 0xff, volume_params[id]); } else { - card->ac97.stereo_mixers &= ~(1 << id); - card->ac97.stereo_mixers |= card->ac97_stereo_mixers; + card->ac97->stereo_mixers &= ~(1 << id); + card->ac97->stereo_mixers |= card->ac97_stereo_mixers; if (ch == 0) { - card->ac97.supported_mixers &= ~(1 << id); - card->ac97.supported_mixers |= card->ac97_supported_mixers; + card->ac97->supported_mixers &= ~(1 << id); + card->ac97->supported_mixers |= card->ac97_supported_mixers; } } break; @@ -500,9 +500,9 @@ case CMD_AC97_BOOST: if(ctl->val[0]) - emu10k1_ac97_write(&card->ac97, 0x18, 0x0); + emu10k1_ac97_write(card->ac97, 0x18, 0x0); else - emu10k1_ac97_write(&card->ac97, 0x18, 0x0808); + emu10k1_ac97_write(card->ac97, 0x18, 0x0808); break; default: ret = -EINVAL; @@ -577,7 +577,7 @@ int val; int scale; - card->ac97.modcnt++; + card->ac97->modcnt++; if (get_user(val, (int *)arg)) return -EFAULT; @@ -589,7 +589,7 @@ if (right > 100) right = 100; if (left > 100) left = 100; - card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + card->ac97->mixer_state[oss_mixer] = (right << 8) | left; if (oss_mixer == SOUND_MIXER_TREBLE) { set_treble(card, left, right); return 0; @@ -599,7 +599,7 @@ } if (oss_mixer == SOUND_MIXER_VOLUME) - scale = 1 << card->ac97.bit_resolution; + scale = 1 << card->ac97->bit_resolution; else scale = volume_params[oss_mixer]; @@ -607,7 +607,7 @@ emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, scale); if (card->ac97_supported_mixers & (1 << oss_mixer)) - card->ac97.write_mixer(&card->ac97, oss_mixer, left, right); + card->ac97->write_mixer(card->ac97, oss_mixer, left, right); return 0; } @@ -623,9 +623,9 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, card->ac97.name, sizeof(info.id)); - strlcpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name)); - info.modify_counter = card->ac97.modcnt; + strncpy(info.id, card->ac97->name, sizeof(info.id)); + strncpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name)); + info.modify_counter = card->ac97->modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -636,7 +636,7 @@ if ((_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES) ret = emu10k1_dsp_mixer(card, oss_mixer, arg); else - ret = card->ac97.mixer_ioctl(&card->ac97, cmd, arg); + ret = card->ac97->mixer_ioctl(card->ac97, cmd, arg); } if (ret < 0) @@ -656,7 +656,7 @@ list_for_each(entry, &emu10k1_devs) { card = list_entry(entry, struct emu10k1_card, list); - if (card->ac97.dev_mixer == minor) + if (card->ac97->dev_mixer == minor) goto match; } diff -urN linux-2.5.75-bk1/sound/oss/es1370.c linux-2.5.75-bk2/sound/oss/es1370.c --- linux-2.5.75-bk1/sound/oss/es1370.c 2003-07-10 13:15:34.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/es1370.c 2003-07-12 04:37:48.000000000 -0700 @@ -889,8 +889,8 @@ } if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "ES1370", sizeof(info.id)); - strlcpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -898,8 +898,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "ES1370", sizeof(info.id)); - strlcpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -2484,12 +2484,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - unlock_kernel(); - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) diff -urN linux-2.5.75-bk1/sound/oss/es1371.c linux-2.5.75-bk2/sound/oss/es1371.c --- linux-2.5.75-bk1/sound/oss/es1371.c 2003-07-10 13:12:51.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/es1371.c 2003-07-12 04:37:48.000000000 -0700 @@ -406,7 +406,7 @@ struct proc_dir_entry *ps; #endif /* ES1371_DEBUG */ - struct ac97_codec codec; + struct ac97_codec *codec; /* wave stuff */ unsigned ctrl; @@ -682,10 +682,10 @@ unsigned long flags; unsigned t, x; + spin_lock_irqsave(&s->lock, flags); for (t = 0; t < POLL_COUNT; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; - spin_lock_irqsave(&s->lock, flags); /* save the current state for later */ x = wait_src_ready(s); @@ -724,11 +724,12 @@ unsigned long flags; unsigned t, x; + spin_lock_irqsave(&s->lock, flags); + /* wait for WIP to go away */ for (t = 0; t < 0x1000; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; - spin_lock_irqsave(&s->lock, flags); /* save the current state for later */ x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)); @@ -756,7 +757,6 @@ /* restore SRC reg */ wait_src_ready(s); outl(x, s->io+ES1371_REG_SRCONV); - spin_unlock_irqrestore(&s->lock, flags); /* wait for WIP again */ for (t = 0; t < 0x1000; t++) @@ -768,6 +768,7 @@ if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) break; + spin_unlock_irqrestore(&s->lock, flags); return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); } @@ -1217,7 +1218,7 @@ if (list == &devs) return -ENODEV; s = list_entry(list, struct es1371_state, devs); - if (s->codec.dev_mixer == minor) + if (s->codec->dev_mixer == minor) break; } VALIDATE_STATE(s); @@ -1236,7 +1237,7 @@ static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct es1371_state *s = (struct es1371_state *)file->private_data; - struct ac97_codec *codec = &s->codec; + struct ac97_codec *codec = s->codec; return mixdev_ioctl(codec, cmd, arg); } @@ -1908,7 +1909,7 @@ return -EINVAL; } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } static int es1371_open(struct inode *inode, struct file *file) @@ -2339,7 +2340,7 @@ return -EINVAL; } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } static int es1371_open_dac(struct inode *inode, struct file *file) @@ -2661,12 +2662,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - unlock_kernel(); - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG PFX "midi timed out??\n"); @@ -2720,7 +2717,7 @@ /* print out CODEC state */ len += sprintf (buf + len, "AC97 CODEC state\n"); for (cnt=0; cnt <= 0x7e; cnt = cnt +2) - len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(&s->codec, cnt)); + len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(s->codec, cnt)); if (fpos >=len){ *start = buf; @@ -2820,6 +2817,11 @@ return -ENOMEM; } memset(s, 0, sizeof(struct es1371_state)); + + s->codec = ac97_alloc_codec(); + if(s->codec == NULL) + goto err_codec; + init_waitqueue_head(&s->dma_adc.wait); init_waitqueue_head(&s->dma_dac1.wait); init_waitqueue_head(&s->dma_dac2.wait); @@ -2835,10 +2837,10 @@ s->vendor = pcidev->vendor; s->device = pcidev->device; pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); - s->codec.private_data = s; - s->codec.id = 0; - s->codec.codec_read = rdcodec; - s->codec.codec_write = wrcodec; + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; printk(KERN_INFO PFX "found chip, vendor id 0x%04x device id 0x%04x revision 0x%02x\n", s->vendor, s->device, s->rev); if (!request_region(s->io, ES1371_EXTENT, "es1371")) { @@ -2855,7 +2857,7 @@ /* register devices */ if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1)))<0) goto err_dev1; - if ((res=(s->codec.dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1))) < 0) + if ((res=(s->codec->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1))) < 0) goto err_dev2; if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1))) < 0) goto err_dev3; @@ -2938,7 +2940,7 @@ /* init the sample rate converter */ src_init(s); /* codec init */ - if (!ac97_probe_codec(&s->codec)) { + if (!ac97_probe_codec(s->codec)) { res = -ENODEV; goto err_gp; } @@ -2947,16 +2949,16 @@ fs = get_fs(); set_fs(KERNEL_DS); val = SOUND_MASK_LINE; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { val = initvol[i].vol; - mixdev_ioctl(&s->codec, initvol[i].mixch, (unsigned long)&val); + mixdev_ioctl(s->codec, initvol[i].mixch, (unsigned long)&val); } /* mute master and PCM when in S/PDIF mode */ if (s->spdif_volume != -1) { val = 0x0000; - s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, (unsigned long)&val); - s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, (unsigned long)&val); + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_VOLUME, (unsigned long)&val); + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_PCM, (unsigned long)&val); } set_fs(fs); /* turn on S/PDIF output driver if requested */ @@ -2984,7 +2986,7 @@ err_dev4: unregister_sound_dsp(s->dev_dac); err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1: @@ -2993,6 +2995,8 @@ err_irq: release_region(s->io, ES1371_EXTENT); err_region: + err_codec: + ac97_release_codec(s->codec); kfree(s); return res; } @@ -3018,9 +3022,10 @@ } release_region(s->io, ES1371_EXTENT); unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); unregister_sound_dsp(s->dev_dac); unregister_sound_midi(s->dev_midi); + ac97_release_codec(s->codec); kfree(s); pci_set_drvdata(dev, NULL); } diff -urN linux-2.5.75-bk1/sound/oss/esssolo1.c linux-2.5.75-bk2/sound/oss/esssolo1.c --- linux-2.5.75-bk1/sound/oss/esssolo1.c 2003-07-10 13:05:26.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/esssolo1.c 2003-07-12 04:37:48.000000000 -0700 @@ -721,8 +721,8 @@ } if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "Solo1", sizeof(info.id)); - strlcpy(info.name, "ESS Solo1", sizeof(info.name)); + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -730,8 +730,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "Solo1", sizeof(info.id)); - strlcpy(info.name, "ESS Solo1", sizeof(info.name)); + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -1972,12 +1972,8 @@ break; if (signal_pending(current)) break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - unlock_kernel(); - return -EBUSY; - } + if (file->f_flags & O_NONBLOCK) + break; tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG "solo1: midi timed out??\n"); diff -urN linux-2.5.75-bk1/sound/oss/forte.c linux-2.5.75-bk2/sound/oss/forte.c --- linux-2.5.75-bk1/sound/oss/forte.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.75-bk2/sound/oss/forte.c 2003-07-12 04:37:48.000000000 -0700 @@ -0,0 +1,2147 @@ +/* + * forte.c - ForteMedia FM801 OSS Driver + * + * Written by Martin K. Petersen + * Copyright (C) 2002 Hewlett-Packard Company + * Portions Copyright (C) 2003 Martin K. Petersen + * + * Latest version: http://mkp.net/forte/ + * + * Based upon the ALSA FM801 driver by Jaroslav Kysela and OSS drivers + * by Thomas Sailer, Alan Cox, Zach Brown, and Jeff Garzik. Thanks + * guys! + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#define DRIVER_NAME "forte" +#define DRIVER_VERSION "$Id: forte.c,v 1.63 2003/03/01 05:32:42 mkp Exp $" +#define PFX DRIVER_NAME ": " + +#undef M_DEBUG + +#ifdef M_DEBUG +#define DPRINTK(args...) printk(KERN_WARNING args) +#else +#define DPRINTK(args...) +#endif + +/* Card capabilities */ +#define FORTE_CAPS (DSP_CAP_MMAP | DSP_CAP_TRIGGER) + +/* Supported audio formats */ +#define FORTE_FMTS (AFMT_U8 | AFMT_S16_LE) + +/* Buffers */ +#define FORTE_MIN_FRAG_SIZE 256 +#define FORTE_MAX_FRAG_SIZE PAGE_SIZE +#define FORTE_DEF_FRAG_SIZE 256 +#define FORTE_MIN_FRAGMENTS 2 +#define FORTE_MAX_FRAGMENTS 256 +#define FORTE_DEF_FRAGMENTS 2 +#define FORTE_MIN_BUF_MSECS 500 +#define FORTE_MAX_BUF_MSECS 1000 + +/* PCI BARs */ +#define FORTE_PCM_VOL 0x00 /* PCM Output Volume */ +#define FORTE_FM_VOL 0x02 /* FM Output Volume */ +#define FORTE_I2S_VOL 0x04 /* I2S Volume */ +#define FORTE_REC_SRC 0x06 /* Record Source */ +#define FORTE_PLY_CTRL 0x08 /* Playback Control */ +#define FORTE_PLY_COUNT 0x0a /* Playback Count */ +#define FORTE_PLY_BUF1 0x0c /* Playback Buffer I */ +#define FORTE_PLY_BUF2 0x10 /* Playback Buffer II */ +#define FORTE_CAP_CTRL 0x14 /* Capture Control */ +#define FORTE_CAP_COUNT 0x16 /* Capture Count */ +#define FORTE_CAP_BUF1 0x18 /* Capture Buffer I */ +#define FORTE_CAP_BUF2 0x1c /* Capture Buffer II */ +#define FORTE_CODEC_CTRL 0x22 /* Codec Control */ +#define FORTE_I2S_MODE 0x24 /* I2S Mode Control */ +#define FORTE_VOLUME 0x26 /* Volume Up/Down/Mute Status */ +#define FORTE_I2C_CTRL 0x29 /* I2C Control */ +#define FORTE_AC97_CMD 0x2a /* AC'97 Command */ +#define FORTE_AC97_DATA 0x2c /* AC'97 Data */ +#define FORTE_MPU401_DATA 0x30 /* MPU401 Data */ +#define FORTE_MPU401_CMD 0x31 /* MPU401 Command */ +#define FORTE_GPIO_CTRL 0x52 /* General Purpose I/O Control */ +#define FORTE_GEN_CTRL 0x54 /* General Control */ +#define FORTE_IRQ_MASK 0x56 /* Interrupt Mask */ +#define FORTE_IRQ_STATUS 0x5a /* Interrupt Status */ +#define FORTE_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ +#define FORTE_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ +#define FORTE_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ +#define FORTE_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ +#define FORTE_POWERDOWN 0x70 /* Blocks Power Down Control */ + +#define FORTE_CAP_OFFSET FORTE_CAP_CTRL - FORTE_PLY_CTRL + +#define FORTE_AC97_ADDR_SHIFT 10 + +/* Playback and record control register bits */ +#define FORTE_BUF1_LAST (1<<1) +#define FORTE_BUF2_LAST (1<<2) +#define FORTE_START (1<<5) +#define FORTE_PAUSE (1<<6) +#define FORTE_IMMED_STOP (1<<7) +#define FORTE_RATE_SHIFT 8 +#define FORTE_RATE_MASK (15 << FORTE_RATE_SHIFT) +#define FORTE_CHANNELS_4 (1<<12) /* Playback only */ +#define FORTE_CHANNELS_6 (2<<12) /* Playback only */ +#define FORTE_CHANNELS_6MS (3<<12) /* Playback only */ +#define FORTE_CHANNELS_MASK (3<<12) +#define FORTE_16BIT (1<<14) +#define FORTE_STEREO (1<<15) + +/* IRQ status bits */ +#define FORTE_IRQ_PLAYBACK (1<<8) +#define FORTE_IRQ_CAPTURE (1<<9) +#define FORTE_IRQ_VOLUME (1<<14) +#define FORTE_IRQ_MPU (1<<15) + +/* CODEC control */ +#define FORTE_CC_CODEC_RESET (1<<5) +#define FORTE_CC_AC97_RESET (1<<6) + +/* AC97 cmd */ +#define FORTE_AC97_WRITE (0<<7) +#define FORTE_AC97_READ (1<<7) +#define FORTE_AC97_DP_INVALID (0<<8) +#define FORTE_AC97_DP_VALID (1<<8) +#define FORTE_AC97_PORT_RDY (0<<9) +#define FORTE_AC97_PORT_BSY (1<<9) + + +struct forte_channel { + const char *name; + + unsigned short ctrl; /* Ctrl BAR contents */ + unsigned long iobase; /* Ctrl BAR address */ + + wait_queue_head_t wait; + + void *buf; /* Buffer */ + dma_addr_t buf_handle; /* Buffer handle */ + + unsigned int record; + unsigned int format; + unsigned int rate; + unsigned int stereo; + + unsigned int frag_sz; /* Current fragment size */ + unsigned int frag_num; /* Current # of fragments */ + unsigned int frag_msecs; /* Milliseconds per frag */ + unsigned int buf_sz; /* Current buffer size */ + + unsigned int hwptr; /* Tail */ + unsigned int swptr; /* Head */ + unsigned int filled_frags; /* Fragments currently full */ + unsigned int next_buf; /* Index of next buffer */ + + unsigned int active; /* Channel currently in use */ + unsigned int mapped; /* mmap */ + + unsigned int buf_pages; /* Real size of buffer */ + unsigned int nr_irqs; /* Number of interrupts */ + unsigned int bytes; /* Total bytes */ + unsigned int residue; /* Partial fragment */ +}; + + +struct forte_chip { + struct pci_dev *pci_dev; + unsigned long iobase; + int irq; + + struct semaphore open_sem; /* Device access */ + spinlock_t lock; /* State */ + + spinlock_t ac97_lock; + struct ac97_codec *ac97; + + int multichannel; + int dsp; /* OSS handle */ + int trigger; /* mmap I/O trigger */ + + struct forte_channel play; + struct forte_channel rec; +}; + + +static int channels[] = { 2, 4, 6, }; +static int rates[] = { 5500, 8000, 9600, 11025, 16000, 19200, + 22050, 32000, 38400, 44100, 48000, }; + +static struct forte_chip *forte; +static int found; + + +/* AC97 Codec -------------------------------------------------------------- */ + + +/** + * forte_ac97_wait: + * @chip: fm801 instance whose AC97 codec to wait on + * + * FIXME: + * Stop busy-waiting + */ + +static inline int +forte_ac97_wait (struct forte_chip *chip) +{ + int i = 10000; + + while ( (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_PORT_BSY) + && i-- ) + cpu_relax(); + + return i == 0; +} + + +/** + * forte_ac97_read: + * @codec: AC97 codec to read from + * @reg: register to read + */ + +u16 +forte_ac97_read (struct ac97_codec *codec, u8 reg) +{ + u16 ret = 0; + struct forte_chip *chip = codec->private_data; + + spin_lock (&chip->ac97_lock); + + /* Knock, knock */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_read: Serial bus busy\n"); + goto out; + } + + /* Send read command */ + outw (reg | (1<<7), chip->iobase + FORTE_AC97_CMD); + + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_read: Bus busy reading reg 0x%x\n", + reg); + goto out; + } + + /* Sanity checking */ + if (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_DP_INVALID) { + printk (KERN_ERR PFX "ac97_read: Invalid data port"); + goto out; + } + + /* Fetch result */ + ret = inw (chip->iobase + FORTE_AC97_DATA); + + out: + spin_unlock (&chip->ac97_lock); + return ret; +} + + +/** + * forte_ac97_write: + * @codec: AC97 codec to send command to + * @reg: register to write + * @val: value to write + */ + +void +forte_ac97_write (struct ac97_codec *codec, u8 reg, u16 val) +{ + struct forte_chip *chip = codec->private_data; + + spin_lock (&chip->ac97_lock); + + /* Knock, knock */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_write: Serial bus busy\n"); + goto out; + } + + outw (val, chip->iobase + FORTE_AC97_DATA); + outb (reg | FORTE_AC97_WRITE, chip->iobase + FORTE_AC97_CMD); + + /* Wait for completion */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_write: Bus busy after write\n"); + goto out; + } + + out: + spin_unlock (&chip->ac97_lock); +} + + +/* Mixer ------------------------------------------------------------------- */ + + +/** + * forte_mixer_open: + * @inode: + * @file: + */ + +static int +forte_mixer_open (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = forte; + file->private_data = chip->ac97; + return 0; +} + + +/** + * forte_mixer_release: + * @inode: + * @file: + */ + +static int +forte_mixer_release (struct inode *inode, struct file *file) +{ + /* We will welease Wodewick */ + return 0; +} + + +/** + * forte_mixer_ioctl: + * @inode: + * @file: + */ + +static int +forte_mixer_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *) file->private_data; + + return codec->mixer_ioctl (codec, cmd, arg); +} + + +static struct file_operations forte_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: forte_mixer_ioctl, + open: forte_mixer_open, + release: forte_mixer_release, +}; + + +/* Channel ----------------------------------------------------------------- */ + +/** + * forte_channel_reset: + * @channel: Channel to reset + * + * Locking: Must be called with lock held. + */ + +static void +forte_channel_reset (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + DPRINTK ("%s: channel = %s\n", __FUNCTION__, channel->name); + + channel->ctrl &= ~FORTE_START; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); + + /* We always play at least two fragments, hence these defaults */ + channel->hwptr = channel->frag_sz; + channel->next_buf = 1; + channel->swptr = 0; + channel->filled_frags = 0; + channel->active = 0; + channel->bytes = 0; + channel->nr_irqs = 0; + channel->mapped = 0; + channel->residue = 0; +} + + +/** + * forte_channel_start: + * @channel: Channel to start (record/playback) + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_start (struct forte_channel *channel) +{ + if (!channel || !channel->iobase || channel->active) + return; + + channel->ctrl &= ~(FORTE_PAUSE | FORTE_BUF1_LAST | FORTE_BUF2_LAST + | FORTE_IMMED_STOP); + channel->ctrl |= FORTE_START; + channel->active = 1; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_stop: + * @channel: Channel to stop + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_stop (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + channel->ctrl &= ~(FORTE_START | FORTE_PAUSE); + channel->ctrl |= FORTE_IMMED_STOP; + + channel->active = 0; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_pause: + * @channel: Channel to pause + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_pause (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + channel->ctrl |= FORTE_PAUSE; + + channel->active = 0; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_rate: + * @channel: Channel whose rate to set. Playback and record are + * independent. + * @rate: Channel rate in Hz + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_rate (struct forte_channel *channel, unsigned int rate) +{ + int new_rate; + + if (!channel || !channel->iobase) + return -EINVAL; + + /* The FM801 only supports a handful of fixed frequencies. + * We find the value closest to what userland requested. + */ + if (rate <= 6250) { rate = 5500; new_rate = 0; } + else if (rate <= 8800) { rate = 8000; new_rate = 1; } + else if (rate <= 10312) { rate = 9600; new_rate = 2; } + else if (rate <= 13512) { rate = 11025; new_rate = 3; } + else if (rate <= 17600) { rate = 16000; new_rate = 4; } + else if (rate <= 20625) { rate = 19200; new_rate = 5; } + else if (rate <= 27025) { rate = 22050; new_rate = 6; } + else if (rate <= 35200) { rate = 32000; new_rate = 7; } + else if (rate <= 41250) { rate = 38400; new_rate = 8; } + else if (rate <= 46050) { rate = 44100; new_rate = 9; } + else { rate = 48000; new_rate = 10; } + + channel->ctrl &= ~FORTE_RATE_MASK; + channel->ctrl |= new_rate << FORTE_RATE_SHIFT; + channel->rate = rate; + + DPRINTK ("%s: %s rate = %d\n", __FUNCTION__, channel->name, rate); + + return rate; +} + + +/** + * forte_channel_format: + * @channel: Channel whose audio format to set + * @format: OSS format ID + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_format (struct forte_channel *channel, int format) +{ + if (!channel || !channel->iobase) + return -EINVAL; + + switch (format) { + + case AFMT_QUERY: + break; + + case AFMT_U8: + channel->ctrl &= ~FORTE_16BIT; + channel->format = AFMT_U8; + break; + + case AFMT_S16_LE: + default: + channel->ctrl |= FORTE_16BIT; + channel->format = AFMT_S16_LE; + break; + } + + DPRINTK ("%s: %s want %d format, got %d\n", __FUNCTION__, channel->name, + format, channel->format); + + return channel->format; +} + + +/** + * forte_channel_stereo: + * @channel: Channel to toggle + * @stereo: 0 for Mono, 1 for Stereo + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_stereo (struct forte_channel *channel, unsigned int stereo) +{ + int ret; + + if (!channel || !channel->iobase) + return -EINVAL; + + DPRINTK ("%s: %s stereo = %d\n", __FUNCTION__, channel->name, stereo); + + switch (stereo) { + + case 0: + channel->ctrl &= ~(FORTE_STEREO | FORTE_CHANNELS_MASK); + channel-> stereo = stereo; + ret = stereo; + break; + + case 1: + channel->ctrl &= ~FORTE_CHANNELS_MASK; + channel->ctrl |= FORTE_STEREO; + channel-> stereo = stereo; + ret = stereo; + break; + + default: + DPRINTK ("Unsupported channel format"); + ret = -EINVAL; + break; + } + + return ret; +} + + +/** + * forte_channel_buffer: + * @channel: Channel whose buffer to set up + * + * Locking: Must be called with lock held. + */ + +static void +forte_channel_buffer (struct forte_channel *channel, int sz, int num) +{ + unsigned int msecs, shift; + + /* Go away, I'm busy */ + if (channel->filled_frags || channel->bytes) + return; + + /* Fragment size must be a power of 2 */ + shift = 0; sz++; + while (sz >>= 1) + shift++; + channel->frag_sz = 1 << shift; + + /* Round fragment size to something reasonable */ + if (channel->frag_sz < FORTE_MIN_FRAG_SIZE) + channel->frag_sz = FORTE_MIN_FRAG_SIZE; + + if (channel->frag_sz > FORTE_MAX_FRAG_SIZE) + channel->frag_sz = FORTE_MAX_FRAG_SIZE; + + /* Find fragment length in milliseconds */ + msecs = channel->frag_sz / + (channel->format == AFMT_S16_LE ? 2 : 1) / + (channel->stereo ? 2 : 1) / + (channel->rate / 1000); + + channel->frag_msecs = msecs; + + /* Pick a suitable number of fragments */ + if (msecs * num < FORTE_MIN_BUF_MSECS) + num = FORTE_MIN_BUF_MSECS / msecs; + + if (msecs * num > FORTE_MAX_BUF_MSECS) + num = FORTE_MAX_BUF_MSECS / msecs; + + /* Fragment number must be a power of 2 */ + shift = 0; + while (num >>= 1) + shift++; + channel->frag_num = 1 << (shift + 1); + + /* Round fragment number to something reasonable */ + if (channel->frag_num < FORTE_MIN_FRAGMENTS) + channel->frag_num = FORTE_MIN_FRAGMENTS; + + if (channel->frag_num > FORTE_MAX_FRAGMENTS) + channel->frag_num = FORTE_MAX_FRAGMENTS; + + channel->buf_sz = channel->frag_sz * channel->frag_num; + + DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d\n", + __FUNCTION__, channel->name, channel->frag_sz, + channel->frag_num, channel->buf_sz); +} + + +/** + * forte_channel_prep: + * @channel: Channel whose buffer to prepare + * + * Locking: Lock held. + */ + +static void +forte_channel_prep (struct forte_channel *channel) +{ + struct page *page; + int i; + + if (channel->buf) + return; + + forte_channel_buffer (channel, channel->frag_sz, channel->frag_num); + channel->buf_pages = channel->buf_sz >> PAGE_SHIFT; + + if (channel->buf_sz % PAGE_SIZE) + channel->buf_pages++; + + DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d, pg = %d\n", + __FUNCTION__, channel->name, channel->frag_sz, + channel->frag_num, channel->buf_sz, channel->buf_pages); + + /* DMA buffer */ + channel->buf = pci_alloc_consistent (forte->pci_dev, + channel->buf_pages * PAGE_SIZE, + &channel->buf_handle); + + if (!channel->buf || !channel->buf_handle) + BUG(); + + page = virt_to_page (channel->buf); + + /* FIXME: can this go away ? */ + for (i = 0 ; i < channel->buf_pages ; i++) + SetPageReserved(page++); + + /* Prep buffer registers */ + outw (channel->frag_sz - 1, channel->iobase + FORTE_PLY_COUNT); + outl (channel->buf_handle, channel->iobase + FORTE_PLY_BUF1); + outl (channel->buf_handle + channel->frag_sz, + channel->iobase + FORTE_PLY_BUF2); + + /* Reset hwptr */ + channel->hwptr = channel->frag_sz; + channel->next_buf = 1; + + DPRINTK ("%s: %s buffer @ %p (%p)\n", __FUNCTION__, channel->name, + channel->buf, channel->buf_handle); +} + + +/** + * forte_channel_drain: + * @chip: + * @channel: + * + * Locking: Don't hold the lock. + */ + +static inline int +forte_channel_drain (struct forte_channel *channel) +{ + DECLARE_WAITQUEUE (wait, current); + unsigned long flags; + + DPRINTK ("%s\n", __FUNCTION__); + + if (channel->mapped) { + spin_lock_irqsave (&forte->lock, flags); + forte_channel_stop (channel); + spin_unlock_irqrestore (&forte->lock, flags); + return 0; + } + + spin_lock_irqsave (&forte->lock, flags); + add_wait_queue (&channel->wait, &wait); + + for (;;) { + if (channel->active == 0 || channel->filled_frags == 1) + break; + + spin_unlock_irqrestore (&forte->lock, flags); + + __set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&forte->lock, flags); + } + + forte_channel_stop (channel); + forte_channel_reset (channel); + set_current_state (TASK_RUNNING); + remove_wait_queue (&channel->wait, &wait); + spin_unlock_irqrestore (&forte->lock, flags); + + return 0; +} + + +/** + * forte_channel_init: + * @chip: Forte chip instance the channel hangs off + * @channel: Channel to initialize + * + * Description: + * Initializes a channel, sets defaults, and allocates + * buffers. + * + * Locking: No lock held. + */ + +static int +forte_channel_init (struct forte_chip *chip, struct forte_channel *channel) +{ + DPRINTK ("%s: chip iobase @ %p\n", __FUNCTION__, (void *)chip->iobase); + + spin_lock_irq (&chip->lock); + memset (channel, 0x0, sizeof (*channel)); + + if (channel == &chip->play) { + channel->name = "PCM_OUT"; + channel->iobase = chip->iobase; + DPRINTK ("%s: PCM-OUT iobase @ %p\n", __FUNCTION__, + (void *) channel->iobase); + } + else if (channel == &chip->rec) { + channel->name = "PCM_IN"; + channel->iobase = chip->iobase + FORTE_CAP_OFFSET; + channel->record = 1; + DPRINTK ("%s: PCM-IN iobase @ %p\n", __FUNCTION__, + (void *) channel->iobase); + } + else + BUG(); + + init_waitqueue_head (&channel->wait); + + /* Defaults: 48kHz, 16-bit, stereo */ + channel->ctrl = inw (channel->iobase + FORTE_PLY_CTRL); + forte_channel_reset (channel); + forte_channel_stereo (channel, 1); + forte_channel_format (channel, AFMT_S16_LE); + forte_channel_rate (channel, 48000); + channel->frag_sz = FORTE_DEF_FRAG_SIZE; + channel->frag_num = FORTE_DEF_FRAGMENTS; + + chip->trigger = 0; + spin_unlock_irq (&chip->lock); + + return 0; +} + + +/** + * forte_channel_free: + * @chip: Chip this channel hangs off + * @channel: Channel to nuke + * + * Description: + * Resets channel and frees buffers. + * + * Locking: Hold your horses. + */ + +static void +forte_channel_free (struct forte_chip *chip, struct forte_channel *channel) +{ + DPRINTK ("%s: %s\n", __FUNCTION__, channel->name); + + if (!channel->buf_handle) + return; + + pci_free_consistent (chip->pci_dev, channel->buf_pages * PAGE_SIZE, + channel->buf, channel->buf_handle); + + memset (channel, 0x0, sizeof (*channel)); +} + + +/* DSP --------------------------------------------------------------------- */ + + +/** + * forte_dsp_ioctl: + */ + +static int +forte_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ival=0, ret, rval=0, rd, wr, count; + struct forte_chip *chip; + struct audio_buf_info abi; + struct count_info cinfo; + + chip = file->private_data; + + if (file->f_mode & FMODE_WRITE) + wr = 1; + else + wr = 0; + + if (file->f_mode & FMODE_READ) + rd = 1; + else + rd = 0; + + switch (cmd) { + + case OSS_GETVERSION: + return put_user (SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_GETCAPS: + DPRINTK ("%s: GETCAPS\n", __FUNCTION__); + + ival = FORTE_CAPS; /* DUPLEX */ + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_GETFMTS: + DPRINTK ("%s: GETFMTS\n", __FUNCTION__); + + ival = FORTE_FMTS; /* U8, 16LE */ + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* U8, 16LE */ + DPRINTK ("%s: SETFMT\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_format (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->rec); + rval = forte_channel_format (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, (int *) arg); + + case SNDCTL_DSP_STEREO: /* 0 - mono, 1 - stereo */ + DPRINTK ("%s: STEREO\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, (int *) arg); + + case SNDCTL_DSP_CHANNELS: /* 1 - mono, 2 - stereo */ + DPRINTK ("%s: CHANNELS\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->rec, ival-1) + 1; + } + + if (wr) { + forte_channel_stop (&chip->play); + rval = forte_channel_stereo (&chip->play, ival-1) + 1; + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, (int *) arg); + + case SNDCTL_DSP_SPEED: + DPRINTK ("%s: SPEED\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_rate (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->play); + rval = forte_channel_rate (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user(rval, (int*) arg); + + case SNDCTL_DSP_GETBLKSIZE: + DPRINTK ("%s: GETBLKSIZE\n", __FUNCTION__); + + spin_lock_irq (&chip->lock); + + if (rd) + ival = chip->rec.frag_sz; + + if (wr) + ival = chip->play.frag_sz; + + spin_unlock_irq (&chip->lock); + + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_RESET: + DPRINTK ("%s: RESET\n", __FUNCTION__); + + spin_lock_irq (&chip->lock); + + if (rd) + forte_channel_reset (&chip->rec); + + if (wr) + forte_channel_reset (&chip->play); + + spin_unlock_irq (&chip->lock); + + return 0; + + case SNDCTL_DSP_SYNC: + DPRINTK ("%s: SYNC\n", __FUNCTION__); + + if (wr) + ret = forte_channel_drain (&chip->play); + + return 0; + + case SNDCTL_DSP_POST: + DPRINTK ("%s: POST\n", __FUNCTION__); + + if (wr) { + spin_lock_irq (&chip->lock); + + if (chip->play.filled_frags) + forte_channel_start (&chip->play); + + spin_unlock_irq (&chip->lock); + } + + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + DPRINTK ("%s: SETFRAGMENT\n", __FUNCTION__); + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_buffer (&chip->rec, ival & 0xffff, + (ival >> 16) & 0xffff); + ival = (chip->rec.frag_num << 16) + chip->rec.frag_sz; + } + + if (wr) { + forte_channel_buffer (&chip->play, ival & 0xffff, + (ival >> 16) & 0xffff); + ival = (chip->play.frag_num << 16) +chip->play.frag_sz; + } + + spin_unlock_irq (&chip->lock); + + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_GETISPACE: + DPRINTK ("%s: GETISPACE\n", __FUNCTION__); + + if (!rd) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + abi.fragstotal = chip->rec.frag_num; + abi.fragsize = chip->rec.frag_sz; + + if (chip->rec.mapped) { + abi.fragments = chip->rec.frag_num - 2; + abi.bytes = abi.fragments * abi.fragsize; + } + else { + abi.fragments = chip->rec.filled_frags; + abi.bytes = abi.fragments * abi.fragsize; + } + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &abi, sizeof (abi)); + + case SNDCTL_DSP_GETIPTR: + DPRINTK ("%s: GETIPTR\n", __FUNCTION__); + + if (!rd) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (chip->rec.active) + cinfo.ptr = chip->rec.hwptr; + else + cinfo.ptr = 0; + + cinfo.bytes = chip->rec.bytes; + cinfo.blocks = chip->rec.nr_irqs; + chip->rec.nr_irqs = 0; + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &cinfo, sizeof (cinfo)); + + case SNDCTL_DSP_GETOSPACE: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + abi.fragstotal = chip->play.frag_num; + abi.fragsize = chip->play.frag_sz; + + if (chip->play.mapped) { + abi.fragments = chip->play.frag_num - 2; + abi.bytes = chip->play.buf_sz; + } + else { + abi.fragments = chip->play.frag_num - + chip->play.filled_frags; + + if (chip->play.residue) + abi.fragments--; + + abi.bytes = abi.fragments * abi.fragsize + + chip->play.residue; + } + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &abi, sizeof (abi)); + + case SNDCTL_DSP_GETOPTR: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (chip->play.active) + cinfo.ptr = chip->play.hwptr; + else + cinfo.ptr = 0; + + cinfo.bytes = chip->play.bytes; + cinfo.blocks = chip->play.nr_irqs; + chip->play.nr_irqs = 0; + + spin_unlock_irq (&chip->lock); + + return copy_to_user ((void *) arg, &cinfo, sizeof (cinfo)); + + case SNDCTL_DSP_GETODELAY: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (!chip->play.active) { + ival = 0; + } + else if (chip->play.mapped) { + count = inw (chip->play.iobase + FORTE_PLY_COUNT) + 1; + ival = chip->play.frag_sz - count; + } + else { + ival = chip->play.filled_frags * chip->play.frag_sz; + + if (chip->play.residue) + ival += chip->play.frag_sz - chip->play.residue; + } + + spin_unlock_irq (&chip->lock); + + return put_user (ival, (int *) arg); + + case SNDCTL_DSP_SETDUPLEX: + DPRINTK ("%s: SETDUPLEX\n", __FUNCTION__); + + return -EINVAL; + + case SNDCTL_DSP_GETTRIGGER: + DPRINTK ("%s: GETTRIGGER\n", __FUNCTION__); + + return put_user (chip->trigger, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + + if (get_user (ival, (int *) arg)) + return -EFAULT; + + DPRINTK ("%s: SETTRIGGER %d\n", __FUNCTION__, ival); + + if (wr) { + spin_lock_irq (&chip->lock); + + if (ival & PCM_ENABLE_OUTPUT) + forte_channel_start (&chip->play); + else { + chip->trigger = 1; + forte_channel_prep (&chip->play); + forte_channel_stop (&chip->play); + } + + spin_unlock_irq (&chip->lock); + } + else if (rd) { + spin_lock_irq (&chip->lock); + + if (ival & PCM_ENABLE_INPUT) + forte_channel_start (&chip->rec); + else { + chip->trigger = 1; + forte_channel_prep (&chip->rec); + forte_channel_stop (&chip->rec); + } + + spin_unlock_irq (&chip->lock); + } + + return 0; + + case SOUND_PCM_READ_RATE: + DPRINTK ("%s: PCM_READ_RATE\n", __FUNCTION__); + return put_user (chip->play.rate, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + DPRINTK ("%s: PCM_READ_CHANNELS\n", __FUNCTION__); + return put_user (chip->play.stereo, (int *) arg); + + case SOUND_PCM_READ_BITS: + DPRINTK ("%s: PCM_READ_BITS\n", __FUNCTION__); + return put_user (chip->play.format, (int *) arg); + + case SNDCTL_DSP_NONBLOCK: + DPRINTK ("%s: DSP_NONBLOCK\n", __FUNCTION__); + file->f_flags |= O_NONBLOCK; + return 0; + + default: + DPRINTK ("Unsupported ioctl: %x (%p)\n", cmd, (void *) arg); + break; + } + + return -EINVAL; +} + + +/** + * forte_dsp_open: + */ + +static int +forte_dsp_open (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = forte; /* FIXME: HACK FROM HELL! */ + + if (file->f_flags & O_NONBLOCK) { + if (down_trylock (&chip->open_sem)) { + DPRINTK ("%s: returning -EAGAIN\n", __FUNCTION__); + return -EAGAIN; + } + } + else { + if (down_interruptible (&chip->open_sem)) { + DPRINTK ("%s: returning -ERESTARTSYS\n", __FUNCTION__); + return -ERESTARTSYS; + } + } + + file->private_data = forte; + + DPRINTK ("%s: dsp opened by %d\n", __FUNCTION__, current->pid); + + if (file->f_mode & FMODE_WRITE) + forte_channel_init (forte, &forte->play); + + if (file->f_mode & FMODE_READ) + forte_channel_init (forte, &forte->rec); + + return 0; +} + + +/** + * forte_dsp_release: + */ + +static int +forte_dsp_release (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = file->private_data; + int ret = 0; + + DPRINTK ("%s: chip @ %p\n", __FUNCTION__, chip); + + if (file->f_mode & FMODE_WRITE) { + forte_channel_drain (&chip->play); + + spin_lock_irq (&chip->lock); + + forte_channel_free (chip, &chip->play); + + spin_unlock_irq (&chip->lock); + } + + if (file->f_mode & FMODE_READ) { + while (chip->rec.filled_frags > 0) + interruptible_sleep_on (&chip->rec.wait); + + spin_lock_irq (&chip->lock); + + forte_channel_stop (&chip->rec); + forte_channel_free (chip, &chip->rec); + + spin_unlock_irq (&chip->lock); + } + + up (&chip->open_sem); + + return ret; +} + + +/** + * forte_dsp_poll: + * + */ + +static unsigned int +forte_dsp_poll (struct file *file, struct poll_table_struct *wait) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int mask = 0; + + chip = file->private_data; + + if (file->f_mode & FMODE_WRITE) { + channel = &chip->play; + + if (channel->active) + poll_wait (file, &channel->wait, wait); + + spin_lock_irq (&chip->lock); + + if (channel->frag_num - channel->filled_frags > 0) + mask |= POLLOUT | POLLWRNORM; + + spin_unlock_irq (&chip->lock); + } + + if (file->f_mode & FMODE_READ) { + channel = &chip->rec; + + if (channel->active) + poll_wait (file, &channel->wait, wait); + + spin_lock_irq (&chip->lock); + + if (channel->filled_frags > 0) + mask |= POLLIN | POLLRDNORM; + + spin_unlock_irq (&chip->lock); + } + + return mask; +} + + +/** + * forte_dsp_mmap: + */ + +static int +forte_dsp_mmap (struct file *file, struct vm_area_struct *vma) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned long size; + int ret; + + chip = file->private_data; + + DPRINTK ("%s: start %lXh, size %ld, pgoff %ld\n", __FUNCTION__, + vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_pgoff); + + spin_lock_irq (&chip->lock); + + if (vma->vm_flags & VM_WRITE && chip->play.active) { + ret = -EBUSY; + goto out; + } + + if (vma->vm_flags & VM_READ && chip->rec.active) { + ret = -EBUSY; + goto out; + } + + if (file->f_mode & FMODE_WRITE) + channel = &chip->play; + else if (file->f_mode & FMODE_READ) + channel = &chip->rec; + else { + ret = -EINVAL; + goto out; + } + + forte_channel_prep (channel); + channel->mapped = 1; + + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + + size = vma->vm_end - vma->vm_start; + + if (size > channel->buf_pages * PAGE_SIZE) { + DPRINTK ("%s: size (%ld) > buf_sz (%d) \n", __FUNCTION__, + size, channel->buf_sz); + ret = -EINVAL; + goto out; + } + + if (remap_page_range (vma, vma->vm_start, virt_to_phys (channel->buf), + size, vma->vm_page_prot)) { + DPRINTK ("%s: remap el a no worko\n", __FUNCTION__); + ret = -EAGAIN; + goto out; + } + + ret = 0; + + out: + spin_unlock_irq (&chip->lock); + return ret; +} + + +/** + * forte_dsp_write: + */ + +static ssize_t +forte_dsp_write (struct file *file, const char *buffer, size_t bytes, + loff_t *ppos) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int i = bytes, sz = 0; + unsigned long flags; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok (VERIFY_READ, buffer, bytes)) + return -EFAULT; + + chip = (struct forte_chip *) file->private_data; + + if (!chip) + BUG(); + + channel = &chip->play; + + if (!channel) + BUG(); + + spin_lock_irqsave (&chip->lock, flags); + + /* Set up buffers with the right fragment size */ + forte_channel_prep (channel); + + while (i) { + /* All fragment buffers in use -> wait */ + if (channel->frag_num - channel->filled_frags == 0) { + DECLARE_WAITQUEUE (wait, current); + + /* For trigger or non-blocking operation, get out */ + if (chip->trigger || file->f_flags & O_NONBLOCK) { + spin_unlock_irqrestore (&chip->lock, flags); + return -EAGAIN; + } + + /* Otherwise wait for buffers */ + add_wait_queue (&channel->wait, &wait); + + for (;;) { + spin_unlock_irqrestore (&chip->lock, flags); + + set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&chip->lock, flags); + + if (channel->frag_num - channel->filled_frags) + break; + } + + remove_wait_queue (&channel->wait, &wait); + set_current_state (TASK_RUNNING); + + if (signal_pending (current)) { + spin_unlock_irqrestore (&chip->lock, flags); + return -ERESTARTSYS; + } + } + + if (channel->residue) + sz = channel->residue; + else if (i > channel->frag_sz) + sz = channel->frag_sz; + else + sz = i; + + spin_unlock_irqrestore (&chip->lock, flags); + + if (copy_from_user ((void *) channel->buf + channel->swptr, buffer, sz)) + return -EFAULT; + + spin_lock_irqsave (&chip->lock, flags); + + /* Advance software pointer */ + buffer += sz; + channel->swptr += sz; + channel->swptr %= channel->buf_sz; + i -= sz; + + /* Only bump filled_frags if a full fragment has been written */ + if (channel->swptr % channel->frag_sz == 0) { + channel->filled_frags++; + channel->residue = 0; + } + else + channel->residue = channel->frag_sz - sz; + + /* If playback isn't active, start it */ + if (channel->active == 0 && chip->trigger == 0) + forte_channel_start (channel); + } + + spin_unlock_irqrestore (&chip->lock, flags); + + return bytes - i; +} + + +/** + * forte_dsp_read: + */ + +static ssize_t +forte_dsp_read (struct file *file, char *buffer, size_t bytes, + loff_t *ppos) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int i = bytes, sz; + unsigned long flags; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok (VERIFY_WRITE, buffer, bytes)) + return -EFAULT; + + chip = (struct forte_chip *) file->private_data; + + if (!chip) + BUG(); + + channel = &chip->rec; + + if (!channel) + BUG(); + + spin_lock_irqsave (&chip->lock, flags); + + /* Set up buffers with the right fragment size */ + forte_channel_prep (channel); + + /* Start recording */ + if (!chip->trigger) + forte_channel_start (channel); + + while (i) { + /* No fragment buffers in use -> wait */ + if (channel->filled_frags == 0) { + DECLARE_WAITQUEUE (wait, current); + + /* For trigger mode operation, get out */ + if (chip->trigger) { + spin_unlock_irqrestore (&chip->lock, flags); + return -EAGAIN; + } + + add_wait_queue (&channel->wait, &wait); + + for (;;) { + if (channel->active == 0) + break; + + if (channel->filled_frags) + break; + + spin_unlock_irqrestore (&chip->lock, flags); + + set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&chip->lock, flags); + } + + set_current_state (TASK_RUNNING); + remove_wait_queue (&channel->wait, &wait); + } + + if (i > channel->frag_sz) + sz = channel->frag_sz; + else + sz = i; + + spin_unlock_irqrestore (&chip->lock, flags); + + if (copy_to_user (buffer, (void *)channel->buf+channel->swptr, sz)) { + DPRINTK ("%s: copy_to_user failed\n", __FUNCTION__); + return -EFAULT; + } + + spin_lock_irqsave (&chip->lock, flags); + + /* Advance software pointer */ + buffer += sz; + if (channel->filled_frags > 0) + channel->filled_frags--; + channel->swptr += channel->frag_sz; + channel->swptr %= channel->buf_sz; + i -= sz; + } + + spin_unlock_irqrestore (&chip->lock, flags); + + return bytes - i; +} + + +static struct file_operations forte_dsp_fops = { + owner: THIS_MODULE, + llseek: &no_llseek, + read: &forte_dsp_read, + write: &forte_dsp_write, + poll: &forte_dsp_poll, + ioctl: &forte_dsp_ioctl, + open: &forte_dsp_open, + release: &forte_dsp_release, + mmap: &forte_dsp_mmap, +}; + + +/* Common ------------------------------------------------------------------ */ + + +/** + * forte_interrupt: + */ + +static irqreturn_t +forte_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct forte_chip *chip = dev_id; + struct forte_channel *channel = NULL; + u16 status, count; + + status = inw (chip->iobase + FORTE_IRQ_STATUS); + + /* If this is not for us, get outta here ASAP */ + if ((status & (FORTE_IRQ_PLAYBACK | FORTE_IRQ_CAPTURE)) == 0) + return IRQ_NONE; + + if (status & FORTE_IRQ_PLAYBACK) { + channel = &chip->play; + + spin_lock (&chip->lock); + + if (channel->frag_sz == 0) + goto pack; + + /* Declare a fragment done */ + if (channel->filled_frags > 0) + channel->filled_frags--; + channel->bytes += channel->frag_sz; + channel->nr_irqs++; + + /* Flip-flop between buffer I and II */ + channel->next_buf ^= 1; + + /* Advance hardware pointer by fragment size and wrap around */ + channel->hwptr += channel->frag_sz; + channel->hwptr %= channel->buf_sz; + + /* Buffer I or buffer II BAR */ + outl (channel->buf_handle + channel->hwptr, + channel->next_buf == 0 ? + channel->iobase + FORTE_PLY_BUF1 : + channel->iobase + FORTE_PLY_BUF2); + + /* If the currently playing fragment is last, schedule pause */ + if (channel->filled_frags == 1) + forte_channel_pause (channel); + + pack: + /* Acknowledge interrupt */ + outw (FORTE_IRQ_PLAYBACK, chip->iobase + FORTE_IRQ_STATUS); + + if (waitqueue_active (&channel->wait)) + wake_up_all (&channel->wait); + + spin_unlock (&chip->lock); + } + + if (status & FORTE_IRQ_CAPTURE) { + channel = &chip->rec; + spin_lock (&chip->lock); + + /* One fragment filled */ + channel->filled_frags++; + + /* Get # of completed bytes */ + count = inw (channel->iobase + FORTE_PLY_COUNT) + 1; + + if (count == 0) { + DPRINTK ("%s: last, filled_frags = %d\n", __FUNCTION__, + channel->filled_frags); + channel->filled_frags = 0; + goto rack; + } + + /* Buffer I or buffer II BAR */ + outl (channel->buf_handle + channel->hwptr, + channel->next_buf == 0 ? + channel->iobase + FORTE_PLY_BUF1 : + channel->iobase + FORTE_PLY_BUF2); + + /* Flip-flop between buffer I and II */ + channel->next_buf ^= 1; + + /* Advance hardware pointer by fragment size and wrap around */ + channel->hwptr += channel->frag_sz; + channel->hwptr %= channel->buf_sz; + + /* Out of buffers */ + if (channel->filled_frags == channel->frag_num - 1) + forte_channel_stop (channel); + rack: + /* Acknowledge interrupt */ + outw (FORTE_IRQ_CAPTURE, chip->iobase + FORTE_IRQ_STATUS); + + spin_unlock (&chip->lock); + + if (waitqueue_active (&channel->wait)) + wake_up_all (&channel->wait); + } + + return IRQ_HANDLED; +} + + +/** + * forte_proc_read: + */ + +static int +forte_proc_read (char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i = 0, p_rate, p_chan, r_rate; + unsigned short p_reg, r_reg; + + i += sprintf (page, "ForteMedia FM801 OSS Lite driver\n%s\n \n", + DRIVER_VERSION); + + if (!forte->iobase) + return i; + + p_rate = p_chan = -1; + p_reg = inw (forte->iobase + FORTE_PLY_CTRL); + p_rate = (p_reg >> 8) & 15; + p_chan = (p_reg >> 12) & 3; + + if (p_rate >= 0 || p_rate <= 10) + p_rate = rates[p_rate]; + + if (p_chan >= 0 || p_chan <= 2) + p_chan = channels[p_chan]; + + r_rate = -1; + r_reg = inw (forte->iobase + FORTE_CAP_CTRL); + r_rate = (r_reg >> 8) & 15; + + if (r_rate >= 0 || r_rate <= 10) + r_rate = rates[r_rate]; + + i += sprintf (page + i, + " Playback Capture\n" + "FIFO empty : %-3s %-3s\n" + "Buf1 Last : %-3s %-3s\n" + "Buf2 Last : %-3s %-3s\n" + "Started : %-3s %-3s\n" + "Paused : %-3s %-3s\n" + "Immed Stop : %-3s %-3s\n" + "Rate : %-5d %-5d\n" + "Channels : %-5d -\n" + "16-bit : %-3s %-3s\n" + "Stereo : %-3s %-3s\n" + " \n" + "Buffer Sz : %-6d %-6d\n" + "Frag Sz : %-6d %-6d\n" + "Frag Num : %-6d %-6d\n" + "Frag msecs : %-6d %-6d\n" + "Used Frags : %-6d %-6d\n" + "Mapped : %-3s %-3s\n", + p_reg & 1<<0 ? "yes" : "no", + r_reg & 1<<0 ? "yes" : "no", + p_reg & 1<<1 ? "yes" : "no", + r_reg & 1<<1 ? "yes" : "no", + p_reg & 1<<2 ? "yes" : "no", + r_reg & 1<<2 ? "yes" : "no", + p_reg & 1<<5 ? "yes" : "no", + r_reg & 1<<5 ? "yes" : "no", + p_reg & 1<<6 ? "yes" : "no", + r_reg & 1<<6 ? "yes" : "no", + p_reg & 1<<7 ? "yes" : "no", + r_reg & 1<<7 ? "yes" : "no", + p_rate, r_rate, + p_chan, + p_reg & 1<<14 ? "yes" : "no", + r_reg & 1<<14 ? "yes" : "no", + p_reg & 1<<15 ? "yes" : "no", + r_reg & 1<<15 ? "yes" : "no", + forte->play.buf_sz, forte->rec.buf_sz, + forte->play.frag_sz, forte->rec.frag_sz, + forte->play.frag_num, forte->rec.frag_num, + forte->play.frag_msecs, forte->rec.frag_msecs, + forte->play.filled_frags, forte->rec.filled_frags, + forte->play.mapped ? "yes" : "no", + forte->rec.mapped ? "yes" : "no" + ); + + return i; +} + + +/** + * forte_proc_init: + * + * Creates driver info entries in /proc + */ + +static int __init +forte_proc_init (void) +{ + if (!proc_mkdir ("driver/forte", 0)) + return -EIO; + + if (!create_proc_read_entry ("driver/forte/chip", 0, 0, forte_proc_read, forte)) { + remove_proc_entry ("driver/forte", NULL); + return -EIO; + } + + if (!create_proc_read_entry("driver/forte/ac97", 0, 0, ac97_read_proc, forte->ac97)) { + remove_proc_entry ("driver/forte/chip", NULL); + remove_proc_entry ("driver/forte", NULL); + return -EIO; + } + + return 0; +} + + +/** + * forte_proc_remove: + * + * Removes driver info entries in /proc + */ + +static void +forte_proc_remove (void) +{ + remove_proc_entry ("driver/forte/ac97", NULL); + remove_proc_entry ("driver/forte/chip", NULL); + remove_proc_entry ("driver/forte", NULL); +} + + +/** + * forte_chip_init: + * @chip: Chip instance to initialize + * + * Description: + * Resets chip, configures codec and registers the driver with + * the sound subsystem. + * + * Press and hold Start for 8 secs, then switch on Run + * and hold for 4 seconds. Let go of Start. Numbers + * assume a properly oiled TWG. + */ + +static int __devinit +forte_chip_init (struct forte_chip *chip) +{ + u8 revision; + u16 cmdw; + struct ac97_codec *codec; + + pci_read_config_byte (chip->pci_dev, PCI_REVISION_ID, &revision); + + if (revision >= 0xB1) { + chip->multichannel = 1; + printk (KERN_INFO PFX "Multi-channel device detected.\n"); + } + + /* Reset chip */ + outw (FORTE_CC_CODEC_RESET | FORTE_CC_AC97_RESET, + chip->iobase + FORTE_CODEC_CTRL); + udelay(100); + outw (0, chip->iobase + FORTE_CODEC_CTRL); + + /* Request read from AC97 */ + outw (FORTE_AC97_READ | (0 << FORTE_AC97_ADDR_SHIFT), + chip->iobase + FORTE_AC97_CMD); + mdelay(750); + + if ((inw (chip->iobase + FORTE_AC97_CMD) & (3<<8)) != (1<<8)) { + printk (KERN_INFO PFX "AC97 codec not responding"); + return -EIO; + } + + /* Init volume */ + outw (0x0808, chip->iobase + FORTE_PCM_VOL); + outw (0x9f1f, chip->iobase + FORTE_FM_VOL); + outw (0x8808, chip->iobase + FORTE_I2S_VOL); + + /* I2S control - I2S mode */ + outw (0x0003, chip->iobase + FORTE_I2S_MODE); + + /* Interrupt setup - unmask PLAYBACK & CAPTURE */ + cmdw = inw (chip->iobase + FORTE_IRQ_MASK); + cmdw &= ~0x0003; + outw (cmdw, chip->iobase + FORTE_IRQ_MASK); + + /* Interrupt clear */ + outw (FORTE_IRQ_PLAYBACK|FORTE_IRQ_CAPTURE, + chip->iobase + FORTE_IRQ_STATUS); + + /* Set up the AC97 codec */ + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + codec->private_data = chip; + codec->codec_read = forte_ac97_read; + codec->codec_write = forte_ac97_write; + codec->id = 0; + + if (ac97_probe_codec (codec) == 0) { + printk (KERN_ERR PFX "codec probe failed\n"); + ac97_release_codec(codec); + return -1; + } + + /* Register mixer */ + if ((codec->dev_mixer = + register_sound_mixer (&forte_mixer_fops, -1)) < 0) { + printk (KERN_ERR PFX "couldn't register mixer!\n"); + ac97_release_codec(codec); + return -1; + } + + chip->ac97 = codec; + + /* Register DSP */ + if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1) ) < 0) { + printk (KERN_ERR PFX "couldn't register dsp!\n"); + return -1; + } + + /* Register with /proc */ + if (forte_proc_init()) { + printk (KERN_ERR PFX "couldn't add entries to /proc!\n"); + return -1; + } + + return 0; +} + + +/** + * forte_probe: + * @pci_dev: PCI struct for probed device + * @pci_id: + * + * Description: + * Allocates chip instance, I/O region, and IRQ + */ +static int __init +forte_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct forte_chip *chip; + int ret = 0; + + /* FIXME: Support more than one chip */ + if (found++) + return -EIO; + + /* Ignition */ + if (pci_enable_device (pci_dev)) + return -EIO; + + pci_set_master (pci_dev); + + /* Allocate chip instance and configure */ + forte = (struct forte_chip *) + kmalloc (sizeof (struct forte_chip), GFP_KERNEL); + chip = forte; + + if (chip == NULL) { + printk (KERN_WARNING PFX "Out of memory"); + return -ENOMEM; + } + + memset (chip, 0, sizeof (struct forte_chip)); + chip->pci_dev = pci_dev; + + init_MUTEX(&chip->open_sem); + spin_lock_init (&chip->lock); + spin_lock_init (&chip->ac97_lock); + + if (! request_region (pci_resource_start (pci_dev, 0), + pci_resource_len (pci_dev, 0), DRIVER_NAME)) { + printk (KERN_WARNING PFX "Unable to reserve I/O space"); + ret = -ENOMEM; + goto error; + } + + chip->iobase = pci_resource_start (pci_dev, 0); + chip->irq = pci_dev->irq; + + if (request_irq (chip->irq, forte_interrupt, SA_SHIRQ, DRIVER_NAME, + chip)) { + printk (KERN_WARNING PFX "Unable to reserve IRQ"); + ret = -EIO; + goto error; + } + + pci_set_drvdata (pci_dev, chip); + + printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%04lX IRQ %u\n", + chip->iobase, pci_resource_end (pci_dev, 0), chip->irq); + + /* Power it up */ + if ((ret = forte_chip_init (chip)) == 0) + return 0; + + error: + if (chip->irq) + free_irq (chip->irq, chip); + + if (chip->iobase) + release_region (pci_resource_start (pci_dev, 0), + pci_resource_len (pci_dev, 0)); + + kfree (chip); + + return ret; +} + + +/** + * forte_remove: + * @pci_dev: PCI device to unclaim + * + */ + +static void +forte_remove (struct pci_dev *pci_dev) +{ + struct forte_chip *chip = pci_get_drvdata (pci_dev); + + if (chip == NULL) + return; + + /* Turn volume down to avoid popping */ + outw (0x1f1f, chip->iobase + FORTE_PCM_VOL); + outw (0x1f1f, chip->iobase + FORTE_FM_VOL); + outw (0x1f1f, chip->iobase + FORTE_I2S_VOL); + + forte_proc_remove(); + free_irq (chip->irq, chip); + release_region (chip->iobase, pci_resource_len (pci_dev, 0)); + + unregister_sound_dsp (chip->dsp); + unregister_sound_mixer (chip->ac97->dev_mixer); + ac97_release_codec(chip->ac97); + kfree (chip); + + printk (KERN_INFO PFX "driver released\n"); +} + + +static struct pci_device_id forte_pci_ids[] __devinitdata = { + { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + + +static struct pci_driver forte_pci_driver = { + name: DRIVER_NAME, + id_table: forte_pci_ids, + probe: forte_probe, + remove: forte_remove, + +}; + + +/** + * forte_init_module: + * + */ + +static int __init +forte_init_module (void) +{ + printk (KERN_INFO PFX DRIVER_VERSION "\n"); + + if (!pci_register_driver (&forte_pci_driver)) { + pci_unregister_driver (&forte_pci_driver); + return -ENODEV; + } + + return 0; +} + + +/** + * forte_cleanup_module: + * + */ + +static void __exit +forte_cleanup_module (void) +{ + pci_unregister_driver (&forte_pci_driver); +} + + +module_init(forte_init_module); +module_exit(forte_cleanup_module); + +MODULE_AUTHOR("Martin K. Petersen "); +MODULE_DESCRIPTION("ForteMedia FM801 OSS Driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE (pci, forte_pci_ids); diff -urN linux-2.5.75-bk1/sound/oss/i810_audio.c linux-2.5.75-bk2/sound/oss/i810_audio.c --- linux-2.5.75-bk1/sound/oss/i810_audio.c 2003-07-10 13:12:55.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/i810_audio.c 2003-07-12 04:37:48.000000000 -0700 @@ -117,6 +117,9 @@ #ifndef PCI_DEVICE_ID_INTEL_ICH4 #define PCI_DEVICE_ID_INTEL_ICH4 0x24c5 #endif +#ifndef PCI_DEVICE_ID_INTEL_ICH5 +#define PCI_DEVICE_ID_INTEL_ICH5 0x24d5 +#endif #ifndef PCI_DEVICE_ID_INTEL_440MX #define PCI_DEVICE_ID_INTEL_440MX 0x7195 #endif @@ -272,6 +275,7 @@ INTELICH2, INTELICH3, INTELICH4, + INTELICH5, SI7012, NVIDIA_NFORCE, AMD768, @@ -285,6 +289,7 @@ "Intel ICH2", "Intel ICH3", "Intel ICH4", + "Intel ICH5", "SiS 7012", "NVIDIA nForce Audio", "AMD 768", @@ -303,7 +308,8 @@ { 1, 0x0000 }, /* INTEL440MX */ { 1, 0x0000 }, /* INTELICH2 */ { 2, 0x0000 }, /* INTELICH3 */ - { 3, 0x0003 }, /* INTELICH4 */ + { 3, 0x0003 }, /* INTELICH4 */ + { 3, 0x0003 }, /* INTELICH5 */ /*@FIXME to be verified*/ { 2, 0x0000 }, /* SI7012 */ /*@FIXME to be verified*/ { 2, 0x0000 }, /* NVIDIA_NFORCE */ /*@FIXME to be verified*/ { 2, 0x0000 }, /* AMD768 */ @@ -323,6 +329,8 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH4}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH5, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH5}, {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012}, {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO, @@ -419,6 +427,9 @@ /* The i810 has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; + + /* Control AC97 access serialization */ + spinlock_t ac97_lock; /* PCI device stuff */ struct pci_dev * pci_dev; @@ -547,80 +558,42 @@ * The DSP sample rate must already be set to a supported * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. */ -static void i810_set_spdif_output(struct i810_state *state, int slots, int rate) +static int i810_set_spdif_output(struct i810_state *state, int slots, int rate) { int vol; int aud_reg; + int r = 0; struct ac97_codec *codec = state->card->ac97_codec[0]; - if(!(state->card->ac97_features & 4)) { -#ifdef DEBUG - printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n"); -#endif + if(!codec->codec_ops->digital) { state->card->ac97_status &= ~SPDIF_ON; } else { if ( slots == -1 ) { /* Turn off S/PDIF */ - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - + codec->codec_ops->digital(codec, 0, 0, 0); /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ if ( !(state->card->ac97_status & VOL_MUTED) ) { aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED)); } state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); - return; + return 0; } vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); state->card->ac97_status = vol & VOL_MUTED; - - /* Set S/PDIF transmitter sample rate */ - aud_reg = i810_ac97_get(codec, AC97_SPDIF_CONTROL); - switch ( rate ) { - case 32000: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; - break; - case 44100: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; - break; - case 48000: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; - break; - default: -#ifdef DEBUG - printk(KERN_WARNING "i810_audio: %d sample rate not supported by S/PDIF.\n", rate); -#endif - /* turn off S/PDIF */ - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - state->card->ac97_status &= ~SPDIF_ON; - return; - } - - i810_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_VRA | AC97_EA_SPDIF; - i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); - state->card->ac97_status |= SPDIF_ON; - - /* Check to make sure the configuration is valid */ - aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); - if ( ! (aud_reg & 0x0400) ) { -#ifdef DEBUG - printk(KERN_WARNING "i810_audio: S/PDIF transmitter configuration not valid (0x%04x).\n", aud_reg); -#endif + r = codec->codec_ops->digital(codec, slots, rate, 0); - /* turn off S/PDIF */ - i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + if(r) + state->card->ac97_status |= SPDIF_ON; + else state->card->ac97_status &= ~SPDIF_ON; - return; - } + /* Mute the analog output */ /* Should this only mute the PCM volume??? */ i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED)); } + return r; } /* i810_set_dac_channels @@ -642,8 +615,9 @@ { int aud_reg; struct ac97_codec *codec = state->card->ac97_codec[0]; - + /* No codec, no setup */ + if(codec == NULL) return; @@ -1835,6 +1809,7 @@ } spin_unlock_irqrestore(&state->card->lock, flags); + synchronize_irq(state->card->pci_dev->irq); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; @@ -2493,11 +2468,9 @@ } if(file->f_mode & FMODE_WRITE) { if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) { - /* free any read channel allocated earlier */ + /* make sure we free the record channel allocated above */ if(file->f_mode & FMODE_READ) - card->free_pcm_channel(card, - dmabuf->read_channel->num); - + card->free_pcm_channel(card,dmabuf->read_channel->num); kfree (card->states[i]); card->states[i] = NULL;; return -EBUSY; @@ -2641,23 +2614,32 @@ static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) { struct i810_card *card = dev->private_data; + u16 ret; + + spin_lock(&card->ac97_lock); if (card->use_mmio) { - return i810_ac97_get_mmio(dev, reg); + ret = i810_ac97_get_mmio(dev, reg); } else { - return i810_ac97_get_io(dev, reg); + ret = i810_ac97_get_io(dev, reg); } + spin_unlock(&card->ac97_lock); + + return ret; } static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) { struct i810_card *card = dev->private_data; + + spin_lock(&card->ac97_lock); if (card->use_mmio) { i810_ac97_set_mmio(dev, reg, data); } else { i810_ac97_set_io(dev, reg, data); } + spin_unlock(&card->ac97_lock); } @@ -2803,7 +2785,7 @@ */ /* see i810_ac97_init for the next 7 lines (jsaw) */ inw(card->ac97base); - if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4) + if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH5) && (card->use_mmio)) { primary_codec_id = (int) readl(card->iobase_mmio + SDM) & 0x3; printk(KERN_INFO "i810_audio: Primary codec has ID %d\n", @@ -2873,7 +2855,7 @@ possible IO channels. Bit 0:1 of SDM then holds the last codec ID spoken to. */ - if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4) + if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH5) && (card->use_mmio)) { ac97_id = (int) readl(card->iobase_mmio + SDM) & 0x3; printk(KERN_INFO "i810_audio: Connection %d with codec id %d\n", @@ -2892,9 +2874,8 @@ printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); } - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -2913,7 +2894,7 @@ if(!i810_ac97_probe_and_powerup(card,codec)) { printk(KERN_ERR "i810_audio: timed out waiting for codec %d analog ready.\n", ac97_id); - kfree(codec); + ac97_release_codec(codec); break; /* it didn't work */ } /* Store state information about S/PDIF transmitter */ @@ -2922,32 +2903,22 @@ /* Don't attempt to get eid until powerup is complete */ eid = i810_ac97_get(codec, AC97_EXTENDED_ID); - if(eid==0xFFFFFF) + if(eid==0xFFFF) { printk(KERN_WARNING "i810_audio: no codec attached ?\n"); - kfree(codec); + ac97_release_codec(codec); break; } /* Check for an AC97 1.0 soft modem (ID1) */ - if(codec->codec_read(codec, AC97_RESET) & 2) + if(codec->modem) { - printk(KERN_WARNING "i810_audio: codec %d is an AC97 1.0 softmodem - skipping.\n", ac97_id); - kfree(codec); + printk(KERN_WARNING "i810_audio: codec %d is a softmodem - skipping.\n", ac97_id); + ac97_release_codec(codec); continue; } - /* Check for an AC97 2.x soft modem */ - - codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); - if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1) - { - printk(KERN_WARNING "i810_audio: codec %d is an AC97 2.x softmodem - skipping.\n", ac97_id); - kfree(codec); - continue; - } - card->ac97_features = eid; /* Now check the codec for useful features to make up for @@ -3027,7 +2998,7 @@ if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { printk(KERN_ERR "i810_audio: couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); break; } @@ -3158,6 +3129,7 @@ card->pm_suspended=0; #endif spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); devs = card; pci_set_master(pci_dev); @@ -3253,7 +3225,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } goto out_iospace; } @@ -3297,7 +3269,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); card->ac97_codec[i] = NULL; } unregister_sound_dsp(card->dev_audio); diff -urN linux-2.5.75-bk1/sound/oss/ite8172.c linux-2.5.75-bk2/sound/oss/ite8172.c --- linux-2.5.75-bk1/sound/oss/ite8172.c 2003-07-10 13:05:40.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/ite8172.c 2003-07-12 04:37:48.000000000 -0700 @@ -42,15 +42,17 @@ * * Memory mapping the audio buffers, and the ioctl controls that go * with it. * * S/PDIF output. + * * I2S support. * 3. The following is not supported: - * * I2S input. * * legacy audio mode. * 4. Support for volume button interrupts is implemented but doesn't * work yet. * * Revision history - * 02.08.2001 0.1 Initial release + * 02.08.2001 Initial release + * 06.22.2001 Added I2S support */ +#include #include #include #include @@ -80,6 +82,19 @@ #undef IT8172_VERBOSE_DEBUG #define DBG(x) {} +#define IT8172_MODULE_NAME "IT8172 audio" +#define PFX IT8172_MODULE_NAME + +#ifdef IT8172_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + + static const unsigned sample_shift[] = { 0, 1, 1, 2 }; @@ -226,75 +241,84 @@ #define POLL_COUNT 0x5000 -#define IT8172_MODULE_NAME "IT8172 audio" -#define PFX IT8172_MODULE_NAME ": " +/* --------------------------------------------------------------------- */ +/* + * Define DIGITAL1 as the I2S channel, since it is not listed in + * soundcard.h. + */ +#define SOUND_MIXER_I2S SOUND_MIXER_DIGITAL1 +#define SOUND_MASK_I2S SOUND_MASK_DIGITAL1 +#define SOUND_MIXER_READ_I2S MIXER_READ(SOUND_MIXER_I2S) +#define SOUND_MIXER_WRITE_I2S MIXER_WRITE(SOUND_MIXER_I2S) /* --------------------------------------------------------------------- */ struct it8172_state { - /* list of it8172 devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; + /* list of it8172 devices */ + struct list_head devs; - /* soundcore stuff */ - int dev_audio; + /* the corresponding pci_dev structure */ + struct pci_dev *dev; - /* hardware resources */ - unsigned long io; - unsigned int irq; - - /* PCI ID's */ - u16 vendor; - u16 device; - u8 rev; /* the chip revision */ - - /* options */ - int spdif_volume; /* S/PDIF output is enabled if != -1 */ + /* soundcore stuff */ + int dev_audio; + /* hardware resources */ + unsigned long io; + unsigned int irq; + + /* PCI ID's */ + u16 vendor; + u16 device; + u8 rev; /* the chip revision */ + + /* options */ + int spdif_volume; /* S/PDIF output is enabled if != -1 */ + int i2s_volume; /* current I2S out volume, in OSS format */ + int i2s_recording;/* 1 = recording from I2S, 0 = not */ + #ifdef IT8172_DEBUG - /* debug /proc entry */ - struct proc_dir_entry *ps; - struct proc_dir_entry *ac97_ps; + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; #endif /* IT8172_DEBUG */ - struct ac97_codec codec; + struct ac97_codec *codec; - unsigned short pcc, capcc; - unsigned dacrate, adcrate; + unsigned short pcc, capcc; + unsigned dacrate, adcrate; - spinlock_t lock; - struct semaphore open_sem; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *rawbuf; - dma_addr_t dmaaddr; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - void* nextIn; - void* nextOut; - int count; - int curBufPtr; - unsigned total_bytes; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned stopped:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + void* nextIn; + void* nextOut; + int count; + int curBufPtr; + unsigned total_bytes; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned stopped:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; }; /* --------------------------------------------------------------------- */ @@ -305,114 +329,114 @@ static inline unsigned ld2(unsigned int x) { - unsigned r = 0; + unsigned r = 0; - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; } /* --------------------------------------------------------------------- */ static void it8172_delay(int msec) { - unsigned long tmo; - signed long tmo2; + unsigned long tmo; + signed long tmo2; - if (in_interrupt()) - return; + if (in_interrupt()) + return; - tmo = jiffies + (msec*HZ)/1000; - for (;;) { - tmo2 = tmo - jiffies; - if (tmo2 <= 0) - break; - schedule_timeout(tmo2); - } + tmo = jiffies + (msec*HZ)/1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } } static unsigned short get_compat_rate(unsigned* rate) { - unsigned rate_out = *rate; - unsigned short sr; + unsigned rate_out = *rate; + unsigned short sr; - if (rate_out >= 46050) { - sr = CC_SR_48000; rate_out = 48000; - } else if (rate_out >= 41250) { - sr = CC_SR_44100; rate_out = 44100; - } else if (rate_out >= 35200) { - sr = CC_SR_38400; rate_out = 38400; - } else if (rate_out >= 27025) { - sr = CC_SR_32000; rate_out = 32000; - } else if (rate_out >= 20625) { - sr = CC_SR_22050; rate_out = 22050; - } else if (rate_out >= 17600) { - sr = CC_SR_19200; rate_out = 19200; - } else if (rate_out >= 13513) { - sr = CC_SR_16000; rate_out = 16000; - } else if (rate_out >= 10313) { - sr = CC_SR_11025; rate_out = 11025; - } else if (rate_out >= 8800) { - sr = CC_SR_9600; rate_out = 9600; - } else if (rate_out >= 6750) { - sr = CC_SR_8000; rate_out = 8000; - } else { - sr = CC_SR_5500; rate_out = 5500; - } + if (rate_out >= 46050) { + sr = CC_SR_48000; rate_out = 48000; + } else if (rate_out >= 41250) { + sr = CC_SR_44100; rate_out = 44100; + } else if (rate_out >= 35200) { + sr = CC_SR_38400; rate_out = 38400; + } else if (rate_out >= 27025) { + sr = CC_SR_32000; rate_out = 32000; + } else if (rate_out >= 20625) { + sr = CC_SR_22050; rate_out = 22050; + } else if (rate_out >= 17600) { + sr = CC_SR_19200; rate_out = 19200; + } else if (rate_out >= 13513) { + sr = CC_SR_16000; rate_out = 16000; + } else if (rate_out >= 10313) { + sr = CC_SR_11025; rate_out = 11025; + } else if (rate_out >= 8800) { + sr = CC_SR_9600; rate_out = 9600; + } else if (rate_out >= 6750) { + sr = CC_SR_8000; rate_out = 8000; + } else { + sr = CC_SR_5500; rate_out = 5500; + } - *rate = rate_out; - return sr; + *rate = rate_out; + return sr; } static void set_adc_rate(struct it8172_state *s, unsigned rate) { - unsigned long flags; - unsigned short sr; + unsigned long flags; + unsigned short sr; - sr = get_compat_rate(&rate); + sr = get_compat_rate(&rate); - spin_lock_irqsave(&s->lock, flags); - s->capcc &= ~CC_SR_MASK; - s->capcc |= sr; - outw(s->capcc, s->io+IT_AC_CAPCC); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + s->capcc &= ~CC_SR_MASK; + s->capcc |= sr; + outw(s->capcc, s->io+IT_AC_CAPCC); + spin_unlock_irqrestore(&s->lock, flags); - s->adcrate = rate; + s->adcrate = rate; } static void set_dac_rate(struct it8172_state *s, unsigned rate) { - unsigned long flags; - unsigned short sr; + unsigned long flags; + unsigned short sr; - sr = get_compat_rate(&rate); + sr = get_compat_rate(&rate); - spin_lock_irqsave(&s->lock, flags); - s->pcc &= ~CC_SR_MASK; - s->pcc |= sr; - outw(s->pcc, s->io+IT_AC_PCC); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + s->pcc &= ~CC_SR_MASK; + s->pcc |= sr; + outw(s->pcc, s->io+IT_AC_PCC); + spin_unlock_irqrestore(&s->lock, flags); - s->dacrate = rate; + s->dacrate = rate; } @@ -420,89 +444,88 @@ static u16 rdcodec(struct ac97_codec *codec, u8 addr) { - struct it8172_state *s = (struct it8172_state *)codec->private_data; - unsigned long flags; - unsigned short circp, data; - int i; - - spin_lock_irqsave(&s->lock, flags); - - for (i = 0; i < POLL_COUNT; i++) - if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) - break; - if (i == POLL_COUNT) - printk(KERN_INFO PFX "rdcodec: codec ready poll expired!\n"); - - circp = addr & CIRCP_CIA_MASK; - circp |= (codec->id << CIRCP_CID_BIT); - circp |= CIRCP_RWC; // read command - outw(circp, s->io+IT_AC_CIRCP); - - /* now wait for the data */ - for (i = 0; i < POLL_COUNT; i++) - if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF) - break; - if (i == POLL_COUNT) - printk(KERN_INFO PFX "rdcodec: read poll expired!\n"); + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp, data; + int i; + + spin_lock_irqsave(&s->lock, flags); - data = inw(s->io+IT_AC_CIRDP); - spin_unlock_irqrestore(&s->lock, flags); + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + err("rdcodec: codec ready poll expired!"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp |= CIRCP_RWC; // read command + outw(circp, s->io+IT_AC_CIRCP); + + /* now wait for the data */ + for (i = 0; i < POLL_COUNT; i++) + if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF) + break; + if (i == POLL_COUNT) + err("rdcodec: read poll expired!"); - return data; + data = inw(s->io+IT_AC_CIRDP); + spin_unlock_irqrestore(&s->lock, flags); + + return data; } static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) { - struct it8172_state *s = (struct it8172_state *)codec->private_data; - unsigned long flags; - unsigned short circp; - int i; - - spin_lock_irqsave(&s->lock, flags); - - for (i = 0; i < POLL_COUNT; i++) - if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) - break; - if (i == POLL_COUNT) - printk(KERN_INFO PFX "wrcodec: codec ready poll expired!\n"); - - circp = addr & CIRCP_CIA_MASK; - circp |= (codec->id << CIRCP_CID_BIT); - circp &= ~CIRCP_RWC; // write command + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + err("wrcodec: codec ready poll expired!"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp &= ~CIRCP_RWC; // write command - outw(data, s->io+IT_AC_CIRDP); // send data first - outw(circp, s->io+IT_AC_CIRCP); + outw(data, s->io+IT_AC_CIRDP); // send data first + outw(circp, s->io+IT_AC_CIRCP); - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static void waitcodec(struct ac97_codec *codec) { - unsigned short temp; + unsigned short temp; + + /* codec_wait is used to wait for a ready state after + an AC97_RESET. */ + it8172_delay(10); - /* codec_wait is used to wait for a ready state after - an AC97_RESET. */ - it8172_delay(10); - - temp = rdcodec(codec, 0x26); - - // If power down, power up - if (temp & 0x3f00) { - // Power on - wrcodec(codec, 0x26, 0); - it8172_delay(100); - // Reread temp = rdcodec(codec, 0x26); - } + + // If power down, power up + if (temp & 0x3f00) { + // Power on + wrcodec(codec, 0x26, 0); + it8172_delay(100); + // Reread + temp = rdcodec(codec, 0x26); + } - // Check if Codec REF,ANL,DAC,ADC ready***/ - if ((temp & 0x3f0f) != 0x000f) { - printk(KERN_INFO PFX "codec reg 26 status (0x%x) not ready!!\n", - temp); - return; - } + // Check if Codec REF,ANL,DAC,ADC ready***/ + if ((temp & 0x3f0f) != 0x000f) { + err("codec reg 26 status (0x%x) not ready!!", temp); + return; + } } @@ -510,120 +533,120 @@ static inline void stop_adc(struct it8172_state *s) { - struct dmabuf* db = &s->dma_adc; - unsigned long flags; - unsigned char imc; + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; - if (db->stopped) - return; + if (db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); - s->capcc |= CC_CSP; - outw(s->capcc, s->io+IT_AC_CAPCC); + s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CSP; + outw(s->capcc, s->io+IT_AC_CAPCC); - // disable capture interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc | IMC_CCIM, s->io+IT_AC_IMC); + // disable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_CCIM, s->io+IT_AC_IMC); - db->stopped = 1; + db->stopped = 1; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static inline void stop_dac(struct it8172_state *s) { - struct dmabuf* db = &s->dma_dac; - unsigned long flags; - unsigned char imc; + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; - if (db->stopped) - return; + if (db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); - s->pcc |= CC_CSP; - outw(s->pcc, s->io+IT_AC_PCC); + s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CSP; + outw(s->pcc, s->io+IT_AC_PCC); - // disable playback interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc | IMC_PCIM, s->io+IT_AC_IMC); + // disable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_PCIM, s->io+IT_AC_IMC); - db->stopped = 1; + db->stopped = 1; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static void start_dac(struct it8172_state *s) { - struct dmabuf* db = &s->dma_dac; - unsigned long flags; - unsigned char imc; - unsigned long buf1, buf2; + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; - if (!db->stopped) - return; + if (!db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize - buf1 = virt_to_bus(db->nextOut); - buf2 = buf1 + db->fragsize; - if (buf2 >= db->dmaaddr + db->dmasize) - buf2 -= db->dmasize; - - outl(buf1, s->io+IT_AC_PCB1STA); - outl(buf2, s->io+IT_AC_PCB2STA); - db->curBufPtr = IT_AC_PCB1STA; - - // enable playback interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC); + // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize + buf1 = virt_to_bus(db->nextOut); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_PCB1STA); + outl(buf2, s->io+IT_AC_PCB2STA); + db->curBufPtr = IT_AC_PCB1STA; + + // enable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC); - s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); - s->pcc |= CC_CA; - outw(s->pcc, s->io+IT_AC_PCC); + s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CA; + outw(s->pcc, s->io+IT_AC_PCC); - db->stopped = 0; + db->stopped = 0; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } static void start_adc(struct it8172_state *s) { - struct dmabuf* db = &s->dma_adc; - unsigned long flags; - unsigned char imc; - unsigned long buf1, buf2; + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; - if (!db->stopped) - return; + if (!db->stopped) + return; - spin_lock_irqsave(&s->lock, flags); - - // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize - buf1 = virt_to_bus(db->nextIn); - buf2 = buf1 + db->fragsize; - if (buf2 >= db->dmaaddr + db->dmasize) - buf2 -= db->dmasize; - - outl(buf1, s->io+IT_AC_CAPB1STA); - outl(buf2, s->io+IT_AC_CAPB2STA); - db->curBufPtr = IT_AC_CAPB1STA; + spin_lock_irqsave(&s->lock, flags); - // enable capture interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC); + // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize + buf1 = virt_to_bus(db->nextIn); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_CAPB1STA); + outl(buf2, s->io+IT_AC_CAPB2STA); + db->curBufPtr = IT_AC_CAPB1STA; + + // enable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC); - s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); - s->capcc |= CC_CA; - outw(s->capcc, s->io+IT_AC_CAPCC); + s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CA; + outw(s->capcc, s->io+IT_AC_CAPCC); - db->stopped = 0; + db->stopped = 0; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); } /* --------------------------------------------------------------------- */ @@ -633,94 +656,103 @@ static inline void dealloc_dmabuf(struct it8172_state *s, struct dmabuf *db) { - struct page *page, *pend; + struct page *page, *pend; - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - ClearPageReserved(page); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, - db->rawbuf, db->dmaaddr); - } - db->rawbuf = db->nextIn = db->nextOut = NULL; - db->mapped = db->ready = 0; + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, + db->rawbuf, db->dmaaddr); + } + db->rawbuf = db->nextIn = db->nextOut = NULL; + db->mapped = db->ready = 0; } static int prog_dmabuf(struct it8172_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) { - int order; - unsigned bytepersec; - unsigned bufs; - struct page *page, *pend; - - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = pci_alloc_consistent(s->dev, - PAGE_SIZE << order, - &db->dmaaddr))) - break; - if (!db->rawbuf) - return -ENOMEM; - db->buforder = order; - /* now mark the pages as reserved; - otherwise remap_page_range doesn't do what we want */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - SetPageReserved(page); - } - - db->count = 0; - db->nextIn = db->nextOut = db->rawbuf; - - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? - db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = + pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; + otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + db->count = 0; + db->nextIn = db->nextOut = db->rawbuf; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? + db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, db->dmasize); - - // set data length register - outw(db->fragsize, s->io+reg+2); - db->ready = 1; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, bufs); + +#ifdef IT8172_VERBOSE_DEBUG + dbg("rate=%d, fragsize=%d, numfrag=%d, dmasize=%d", + rate, db->fragsize, db->numfrag, db->dmasize); +#endif - return 0; + // set data length register + outw(db->fragsize, s->io+reg+2); + db->ready = 1; + + return 0; } static inline int prog_dmabuf_adc(struct it8172_state *s) { - stop_adc(s); - return prog_dmabuf(s, &s->dma_adc, s->adcrate, - (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT, - IT_AC_CAPCC); + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, + (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_CAPCC); } static inline int prog_dmabuf_dac(struct it8172_state *s) { - stop_dac(s); - return prog_dmabuf(s, &s->dma_dac, s->dacrate, - (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT, - IT_AC_PCC); + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac, s->dacrate, + (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_PCC); } @@ -728,918 +760,1127 @@ static irqreturn_t it8172_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct it8172_state *s = (struct it8172_state *)dev_id; - struct dmabuf* dac = &s->dma_dac; - struct dmabuf* adc = &s->dma_adc; - unsigned char isc, vs; - unsigned short vol, mute; - unsigned long newptr; - - spin_lock(&s->lock); - - isc = inb(s->io+IT_AC_ISC); - - /* fastpath out, to ease interrupt sharing */ - if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI))) { - spin_unlock(&s->lock); - return IRQ_NONE; - } - - /* clear audio interrupts first */ - outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC); - - /* handle volume button events */ - if (isc & ISC_VCI) { - vs = inb(s->io+IT_AC_VS); - outb(0, s->io+IT_AC_VS); - vol = inw(s->io+IT_AC_PCMOV); - mute = vol & PCMOV_PCMOM; - vol &= PCMOV_PCMLCG_MASK; - if ((vs & VS_VUP) && vol > 0) - vol--; - if ((vs & VS_VDP) && vol < 0x1f) - vol++; - vol |= (vol << PCMOV_PCMRCG_BIT); - if (vs & VS_VMP) - vol |= (mute ^ PCMOV_PCMOM); - outw(vol, s->io+IT_AC_PCMOV); - } - - /* update capture pointers */ - if (isc & ISC_CCI) { - if (adc->count > adc->dmasize - adc->fragsize) { - // Overrun. Stop ADC and log the error - stop_adc(s); - adc->error++; - printk(KERN_INFO PFX "adc overrun\n"); - } else { - newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize; - if (newptr >= adc->dmaaddr + adc->dmasize) - newptr -= adc->dmasize; + struct it8172_state *s = (struct it8172_state *)dev_id; + struct dmabuf* dac = &s->dma_dac; + struct dmabuf* adc = &s->dma_adc; + unsigned char isc, vs; + unsigned short vol, mute; + unsigned long newptr; + + spin_lock(&s->lock); + + isc = inb(s->io+IT_AC_ISC); + + /* fastpath out, to ease interrupt sharing */ + if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI))) { + spin_unlock(&s->lock); + return IRQ_NONE; + } + + /* clear audio interrupts first */ + outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC); + + /* handle volume button events (ignore if S/PDIF enabled) */ + if ((isc & ISC_VCI) && s->spdif_volume == -1) { + vs = inb(s->io+IT_AC_VS); + outb(0, s->io+IT_AC_VS); + vol = inw(s->io+IT_AC_PCMOV); + mute = vol & PCMOV_PCMOM; + vol &= PCMOV_PCMLCG_MASK; + if ((vs & VS_VUP) && vol > 0) + vol--; + if ((vs & VS_VDP) && vol < 0x1f) + vol++; + vol |= (vol << PCMOV_PCMRCG_BIT); + if (vs & VS_VMP) + vol |= (mute ^ PCMOV_PCMOM); + outw(vol, s->io+IT_AC_PCMOV); + } + + /* update capture pointers */ + if (isc & ISC_CCI) { + if (adc->count > adc->dmasize - adc->fragsize) { + // Overrun. Stop ADC and log the error + stop_adc(s); + adc->error++; + dbg("adc overrun"); + } else { + newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize; + if (newptr >= adc->dmaaddr + adc->dmasize) + newptr -= adc->dmasize; - outl(newptr, s->io+adc->curBufPtr); - adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ? - IT_AC_CAPB2STA : IT_AC_CAPB1STA; + outl(newptr, s->io+adc->curBufPtr); + adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ? + IT_AC_CAPB2STA : IT_AC_CAPB1STA; - adc->nextIn += adc->fragsize; - if (adc->nextIn >= adc->rawbuf + adc->dmasize) - adc->nextIn -= adc->dmasize; + adc->nextIn += adc->fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; - adc->count += adc->fragsize; - adc->total_bytes += adc->fragsize; + adc->count += adc->fragsize; + adc->total_bytes += adc->fragsize; - /* wake up anybody listening */ - if (waitqueue_active(&adc->wait)) - wake_up_interruptible(&adc->wait); - } - } - - /* update playback pointers */ - if (isc & ISC_PCI) { - newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize; - if (newptr >= dac->dmaaddr + dac->dmasize) - newptr -= dac->dmasize; + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) + wake_up_interruptible(&adc->wait); + } + } + + /* update playback pointers */ + if (isc & ISC_PCI) { + newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize; + if (newptr >= dac->dmaaddr + dac->dmasize) + newptr -= dac->dmasize; - outl(newptr, s->io+dac->curBufPtr); - dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ? - IT_AC_PCB2STA : IT_AC_PCB1STA; + outl(newptr, s->io+dac->curBufPtr); + dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ? + IT_AC_PCB2STA : IT_AC_PCB1STA; - dac->nextOut += dac->fragsize; - if (dac->nextOut >= dac->rawbuf + dac->dmasize) - dac->nextOut -= dac->dmasize; + dac->nextOut += dac->fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; - dac->count -= dac->fragsize; - dac->total_bytes += dac->fragsize; + dac->count -= dac->fragsize; + dac->total_bytes += dac->fragsize; - /* wake up anybody listening */ - if (waitqueue_active(&dac->wait)) - wake_up_interruptible(&dac->wait); + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up_interruptible(&dac->wait); - if (dac->count <= 0) - stop_dac(s); - } + if (dac->count <= 0) + stop_dac(s); + } - spin_unlock(&s->lock); - return IRQ_HANDLED; + spin_unlock(&s->lock); + return IRQ_HANDLED; } /* --------------------------------------------------------------------- */ +static loff_t it8172_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + static int it8172_open_mixdev(struct inode *inode, struct file *file) { - int minor = minor(inode->i_rdev); - struct list_head *list; - struct it8172_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct it8172_state, devs); - if (s->codec.dev_mixer == minor) - break; - } - file->private_data = s; - return 0; + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct it8172_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (s->codec->dev_mixer == minor) + break; + } + file->private_data = s; + return 0; } static int it8172_release_mixdev(struct inode *inode, struct file *file) { - return 0; + return 0; +} + + +static u16 +cvt_ossvol(unsigned int gain) +{ + u16 ret; + + if (gain == 0) + return 0; + + if (gain > 100) + gain = 100; + + ret = (100 - gain + 32) / 4; + ret = ret > 31 ? 31 : ret; + return ret; } static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) { - return codec->mixer_ioctl(codec, cmd, arg); + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned int left, right; + unsigned long flags; + int val; + u16 vol; + + /* + * When we are in S/PDIF mode, we want to disable any analog output so + * we filter the master/PCM channel volume ioctls. + * + * Also filter I2S channel, which AC'97 knows nothing about. + */ + + switch (cmd) { + case SOUND_MIXER_WRITE_VOLUME: + // if not in S/PDIF mode, pass to AC'97 + if (s->spdif_volume == -1) + break; + return 0; + case SOUND_MIXER_WRITE_PCM: + // if not in S/PDIF mode, pass to AC'97 + if (s->spdif_volume == -1) + break; + if (get_user(val, (int *)arg)) + return -EFAULT; + right = ((val >> 8) & 0xff); + left = (val & 0xff); + if (right > 100) + right = 100; + if (left > 100) + left = 100; + s->spdif_volume = (right << 8) | left; + vol = cvt_ossvol(left); + vol |= (cvt_ossvol(right) << PCMOV_PCMRCG_BIT); + if (vol == 0) + vol = PCMOV_PCMOM; // mute + spin_lock_irqsave(&s->lock, flags); + outw(vol, s->io+IT_AC_PCMOV); + spin_unlock_irqrestore(&s->lock, flags); + return put_user(s->spdif_volume, (int *)arg); + case SOUND_MIXER_READ_PCM: + // if not in S/PDIF mode, pass to AC'97 + if (s->spdif_volume == -1) + break; + return put_user(s->spdif_volume, (int *)arg); + case SOUND_MIXER_WRITE_I2S: + if (get_user(val, (int *)arg)) + return -EFAULT; + right = ((val >> 8) & 0xff); + left = (val & 0xff); + if (right > 100) + right = 100; + if (left > 100) + left = 100; + s->i2s_volume = (right << 8) | left; + vol = cvt_ossvol(left); + vol |= (cvt_ossvol(right) << I2SV_I2SRCG_BIT); + if (vol == 0) + vol = I2SV_I2SOM; // mute + outw(vol, s->io+IT_AC_I2SV); + return put_user(s->i2s_volume, (int *)arg); + case SOUND_MIXER_READ_I2S: + return put_user(s->i2s_volume, (int *)arg); + case SOUND_MIXER_WRITE_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val & SOUND_MASK_I2S) { + s->i2s_recording = 1; + outb(DRSS_I2S, s->io+IT_AC_DRSS); + return 0; + } else { + s->i2s_recording = 0; + outb(DRSS_AC97_PRIM, s->io+IT_AC_DRSS); + // now let AC'97 select record source + break; + } + case SOUND_MIXER_READ_RECSRC: + if (s->i2s_recording) + return put_user(SOUND_MASK_I2S, (int *)arg); + else + // let AC'97 report recording source + break; + } + + return codec->mixer_ioctl(codec, cmd, arg); } static int it8172_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct ac97_codec *codec = &s->codec; + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct ac97_codec *codec = s->codec; - return mixdev_ioctl(codec, cmd, arg); + return mixdev_ioctl(codec, cmd, arg); } static /*const*/ struct file_operations it8172_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = it8172_ioctl_mixdev, - .open = it8172_open_mixdev, - .release = it8172_release_mixdev, + owner: THIS_MODULE, + llseek: it8172_llseek, + ioctl: it8172_ioctl_mixdev, + open: it8172_open_mixdev, + release: it8172_release_mixdev, }; /* --------------------------------------------------------------------- */ static int drain_dac(struct it8172_state *s, int nonblock) { - unsigned long flags; - int count, tmo; + unsigned long flags; + int count, tmo; - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; + if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped) + return 0; - for (;;) { - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + //if (nonblock) + //return -EBUSY; + tmo = 1000 * count / s->dacrate; + tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; + it8172_delay(tmo); + } if (signal_pending(current)) - break; - if (nonblock) - return -EBUSY; - tmo = 1000 * count / s->dacrate; - tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; - it8172_delay(tmo); - } - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; + return -ERESTARTSYS; + return 0; } /* --------------------------------------------------------------------- */ + +/* + * Copy audio data to/from user buffer from/to dma buffer, taking care + * that we wrap when reading/writing the dma buffer. Returns actual byte + * count written to or read from the dma buffer. + */ +static int copy_dmabuf_user(struct dmabuf *db, char* userbuf, + int count, int to_user) +{ + char* bufptr = to_user ? db->nextOut : db->nextIn; + char* bufend = db->rawbuf + db->dmasize; + + if (bufptr + count > bufend) { + int partial = (int)(bufend - bufptr); + if (to_user) { + if (copy_to_user(userbuf, bufptr, partial)) + return -EFAULT; + if (copy_to_user(userbuf + partial, db->rawbuf, + count - partial)) + return -EFAULT; + } else { + if (copy_from_user(bufptr, userbuf, partial)) + return -EFAULT; + if (copy_from_user(db->rawbuf, + userbuf + partial, + count - partial)) + return -EFAULT; + } + } else { + if (to_user) { + if (copy_to_user(userbuf, bufptr, count)) + return -EFAULT; + } else { + if (copy_from_user(bufptr, userbuf, count)) + return -EFAULT; + } + } + + return count; +} + + static ssize_t it8172_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct dmabuf *db = &s->dma_adc; - ssize_t ret; - unsigned long flags; - int cnt, bufcnt, avail; + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + ssize_t ret; + unsigned long flags; + int cnt, remainder, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; - if (ppos != &file->f_pos) - return -ESPIPE; - if (db->mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - - while (count > 0) { - // wait for samples in capture buffer - do { - spin_lock_irqsave(&s->lock, flags); - if (db->stopped) - start_adc(s); - avail = db->count; - spin_unlock_irqrestore(&s->lock, flags); - if (avail <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - return ret; - } - interruptible_sleep_on(&db->wait); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - return ret; - } - } - } while (avail <= 0); - - cnt = count > avail ? avail : count; - bufcnt = cnt; - if (cnt % db->fragsize) { - // round count up to nearest fragment - int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); - cnt = newcnt; - } - - // copy from nextOut to user - if (copy_to_user(buffer, db->nextOut, bufcnt)) { - if (!ret) - ret = -EFAULT; - return ret; - } + while (count > 0) { + // wait for samples in capture buffer + do { + spin_lock_irqsave(&s->lock, flags); + if (db->stopped) + start_adc(s); + avail = db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + // copy from nextOut to user + if ((cnt = copy_dmabuf_user(db, buffer, count > avail ? + avail : count, 1)) < 0) { + if (!ret) + ret = -EFAULT; + return ret; + } + spin_lock_irqsave(&s->lock, flags); + db->count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + + db->nextOut += cnt; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + + count -= cnt; + buffer += cnt; + ret += cnt; + } // while (count > 0) + + /* + * See if the dma buffer count after this read call is + * aligned on a fragsize boundary. If not, read from + * buffer until we reach a boundary, and let's hope this + * is just the last remainder of an audio record. If not + * it means the user is not reading in fragsize chunks, in + * which case it's his/her fault that there are audio gaps + * in their record. + */ spin_lock_irqsave(&s->lock, flags); - db->count -= cnt; + remainder = db->count % db->fragsize; + if (remainder) { + db->nextOut += remainder; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + db->count -= remainder; + } spin_unlock_irqrestore(&s->lock, flags); - db->nextOut += cnt; - if (db->nextOut >= db->rawbuf + db->dmasize) - db->nextOut -= db->dmasize; - - count -= bufcnt; - buffer += bufcnt; - ret += bufcnt; - } // while (count > 0) - - return ret; + return ret; } static ssize_t it8172_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct dmabuf *db = &s->dma_dac; - ssize_t ret; - unsigned long flags; - int cnt, bufcnt, avail; - - if (ppos != &file->f_pos) - return -ESPIPE; - if (db->mapped) - return -ENXIO; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - - while (count > 0) { - // wait for space in playback buffer - do { - spin_lock_irqsave(&s->lock, flags); - avail = db->dmasize - db->count; - spin_unlock_irqrestore(&s->lock, flags); - if (avail <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - return ret; - } - interruptible_sleep_on(&db->wait); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - return ret; - } - } - } while (avail <= 0); + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + ssize_t ret; + unsigned long flags; + int cnt, remainder, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = db->dmasize - db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); - cnt = count > avail ? avail : count; - // copy to nextIn - if (copy_from_user(db->nextIn, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - return ret; - } - - bufcnt = cnt; - if (cnt % db->fragsize) { - // round count up to nearest fragment, and fill remainder of - // fragment with silence - int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); - memset(db->nextIn + cnt, (s->pcc & CC_DF) ? 0 : 0x80, newcnt - cnt); - cnt = newcnt; - } + // copy to nextIn + if ((cnt = copy_dmabuf_user(db, (char*)buffer, + count > avail ? + avail : count, 0)) < 0) { + if (!ret) + ret = -EFAULT; + return ret; + } - spin_lock_irqsave(&s->lock, flags); - db->count += cnt; - if (db->stopped) - start_dac(s); - spin_unlock_irqrestore(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); + db->count += cnt; + if (db->stopped) + start_dac(s); + spin_unlock_irqrestore(&s->lock, flags); - db->nextIn += cnt; - if (db->nextIn >= db->rawbuf + db->dmasize) - db->nextIn -= db->dmasize; + db->nextIn += cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; - count -= bufcnt; - buffer += bufcnt; - ret += bufcnt; - } // while (count > 0) + count -= cnt; + buffer += cnt; + ret += cnt; + } // while (count > 0) - return ret; + /* + * See if the dma buffer count after this write call is + * aligned on a fragsize boundary. If not, fill buffer + * with silence to the next boundary, and let's hope this + * is just the last remainder of an audio playback. If not + * it means the user is not sending us fragsize chunks, in + * which case it's his/her fault that there are audio gaps + * in their playback. + */ + spin_lock_irqsave(&s->lock, flags); + remainder = db->count % db->fragsize; + if (remainder) { + int fill_cnt = db->fragsize - remainder; + memset(db->nextIn, 0, fill_cnt); + db->nextIn += fill_cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; + db->count += fill_cnt; + } + spin_unlock_irqrestore(&s->lock, flags); + + return ret; } /* No kernel lock - we have our own spinlock */ static unsigned int it8172_poll(struct file *file, struct poll_table_struct *wait) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->dma_dac.wait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->dma_adc.wait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac.dmasize >= - s->dma_dac.count + (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= + s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; } static int it8172_mmap(struct file *file, struct vm_area_struct *vma) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - struct dmabuf *db; - unsigned long size; - - lock_kernel(); - if (vma->vm_flags & VM_WRITE) - db = &s->dma_dac; - else if (vma->vm_flags & VM_READ) - db = &s->dma_adc; - else { - unlock_kernel(); - return -EINVAL; - } - if (vma->vm_pgoff != 0) { - unlock_kernel(); - return -EINVAL; - } - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) { - unlock_kernel(); - return -EINVAL; - } - if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), - size, vma->vm_page_prot)) { + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db; + unsigned long size; + + lock_kernel(); + if (vma->vm_flags & VM_WRITE) + db = &s->dma_dac; + else if (vma->vm_flags & VM_READ) + db = &s->dma_adc; + else { + unlock_kernel(); + return -EINVAL; + } + if (vma->vm_pgoff != 0) { + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + unlock_kernel(); + return -EINVAL; + } + if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), + size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + db->mapped = 1; unlock_kernel(); - return -EAGAIN; - } - db->mapped = 1; - unlock_kernel(); - return 0; + return 0; } #ifdef IT8172_VERBOSE_DEBUG static struct ioctl_str_t { - unsigned int cmd; - const char* str; + unsigned int cmd; + const char* str; } ioctl_str[] = { - {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, - {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, - {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, - {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, - {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, - {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, - {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, - {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, - {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, - {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, - {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, - {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, - {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, - {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, - {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, - {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, - {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, - {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, - {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, - {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, - {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, - {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, - {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, - {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, - {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, - {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, - {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, - {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, - {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, - {OSS_GETVERSION, "OSS_GETVERSION"}, - {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, - {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, - {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, - {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} }; #endif static int it8172_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct it8172_state *s = (struct it8172_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret, diff; + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret, diff; - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); #ifdef IT8172_VERBOSE_DEBUG - for (count=0; countf_mode & FMODE_WRITE) - return drain_dac(s, file->f_flags & O_NONBLOCK); - return 0; + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; - case SNDCTL_DSP_SETDUPLEX: - return 0; + case SNDCTL_DSP_SETDUPLEX: + return 0; - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | - DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->irq); - s->dma_dac.count = s->dma_dac.total_bytes = 0; - s->dma_dac.nextIn = s->dma_dac.nextOut = s->dma_dac.rawbuf; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.count = s->dma_adc.total_bytes = 0; - s->dma_adc.nextIn = s->dma_adc.nextOut = s->dma_adc.rawbuf; - } - return 0; + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = + s->dma_dac.rawbuf; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = + s->dma_adc.rawbuf; + } + return 0; - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - set_adc_rate(s, val); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - set_dac_rate(s, val); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } - return put_user((file->f_mode & FMODE_READ) ? - s->adcrate : s->dacrate, (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if (val) - s->capcc |= CC_SM; - else - s->capcc &= ~CC_SM; - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if (val) - s->pcc |= CC_SM; - else - s->pcc &= ~CC_SM; - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if (val >= 2) { - val = 2; - s->capcc |= CC_SM; + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val) + s->capcc |= CC_SM; + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; } - else - s->capcc &= ~CC_SM; - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - switch (val) { - case 1: - s->pcc &= ~CC_SM; - break; - case 2: - s->pcc |= CC_SM; - break; - default: - // FIX! support multichannel??? - val = 2; - s->pcc |= CC_SM; - break; + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val) + s->pcc |= CC_SM; + else + s->pcc &= ~CC_SM; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; } - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } - return put_user(val, (int *)arg); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val >= 2) { + val = 2; + s->capcc |= CC_SM; + } + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + switch (val) { + case 1: + s->pcc &= ~CC_SM; + break; + case 2: + s->pcc |= CC_SM; + break; + default: + // FIX! support multichannel??? + val = 2; + s->pcc |= CC_SM; + break; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *)arg); - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if (val == AFMT_S16_LE) - s->capcc |= CC_DF; - else { - val = AFMT_U8; - s->capcc &= ~CC_DF; - } - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if (val == AFMT_S16_LE) - s->pcc |= CC_DF; - else { - val = AFMT_U8; - s->pcc &= ~CC_DF; + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val == AFMT_S16_LE) + s->capcc |= CC_DF; + else { + val = AFMT_U8; + s->capcc &= ~CC_DF; + } + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val == AFMT_S16_LE) + s->pcc |= CC_DF; + else { + val = AFMT_U8; + s->pcc &= ~CC_DF; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + if (file->f_mode & FMODE_READ) + val = (s->capcc & CC_DF) ? + AFMT_S16_LE : AFMT_U8; + else + val = (s->pcc & CC_DF) ? + AFMT_S16_LE : AFMT_U8; } - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } else { - if (file->f_mode & FMODE_READ) - val = (s->capcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; - else - val = (s->pcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; - } - return put_user(val, (int *)arg); + return put_user(val, (int *)arg); - case SNDCTL_DSP_POST: - return 0; + case SNDCTL_DSP_POST: + return 0; - case SNDCTL_DSP_GETTRIGGER: - val = 0; - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) - val |= PCM_ENABLE_OUTPUT; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, (int *)arg); + case SNDCTL_DSP_GETTRIGGER: + val = 0; + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) + val |= PCM_ENABLE_OUTPUT; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) - start_adc(s); - else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) - start_dac(s); - else - stop_dac(s); - } - return 0; + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) + start_adc(s); + else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) + start_dac(s); + else + stop_dac(s); + } + return 0; - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - abinfo.fragsize = s->dma_dac.fragsize; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - if (!s->dma_dac.stopped) - count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac.dmasize - count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - abinfo.fragsize = s->dma_adc.fragsize; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_adc.count; - if (!s->dma_adc.stopped) - count += (s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL)); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - abinfo.bytes = count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - + inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? + -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + if (!s->dma_adc.stopped) + count += (s->dma_adc.fragsize - + inw(s->io+IT_AC_CAPCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? + -EFAULT : 0; - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - if (!s->dma_dac.stopped) - count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, (int *)arg); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (!s->dma_adc.stopped) { - diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL); - count += diff; - cinfo.bytes += diff; - cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - s->dma_adc.dmaaddr; - } else - cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - s->dma_adc.dmaaddr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - if (copy_to_user((void *)arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cinfo.bytes = s->dma_dac.total_bytes; - count = s->dma_dac.count; - if (!s->dma_dac.stopped) { - diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL); - count -= diff; - cinfo.bytes += diff; - cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - s->dma_dac.dmaaddr; - } else - cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - s->dma_dac.dmaaddr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac.fragshift; - if (copy_to_user((void *)arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) - return put_user(s->dma_dac.fragsize, (int *)arg); - else - return put_user(s->dma_adc.fragsize, (int *)arg); + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - + inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (!s->dma_adc.stopped) { + diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL); + count += diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - + s->dma_adc.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - + s->dma_adc.dmaaddr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (!s->dma_dac.stopped) { + diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL); + count -= diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - + s->dma_dac.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - + s->dma_dac.dmaaddr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragsize, (int *)arg); + else + return put_user(s->dma_adc.fragsize, (int *)arg); - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.subdivision = val; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.subdivision = val; - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.subdivision = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.subdivision = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? - s->adcrate : s->dacrate, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - if (file->f_mode & FMODE_READ) - return put_user((s->capcc & CC_SM) ? 2 : 1, (int *)arg); - else - return put_user((s->pcc & CC_SM) ? 2 : 1, (int *)arg); + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_SM) ? 2 : 1, + (int *)arg); + else + return put_user((s->pcc & CC_SM) ? 2 : 1, + (int *)arg); - case SOUND_PCM_READ_BITS: - if (file->f_mode & FMODE_READ) - return put_user((s->capcc & CC_DF) ? 16 : 8, (int *)arg); - else - return put_user((s->pcc & CC_DF) ? 16 : 8, (int *)arg); + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_DF) ? 16 : 8, + (int *)arg); + else + return put_user((s->pcc & CC_DF) ? 16 : 8, + (int *)arg); - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - } + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } static int it8172_open(struct inode *inode, struct file *file) { - int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct it8172_state *s; - int ret; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct it8172_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - up(&s->open_sem); - return -EBUSY; + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct it8172_state *s; + int ret; + +#ifdef IT8172_VERBOSE_DEBUG + if (file->f_flags & O_NONBLOCK) + dbg(__FUNCTION__ ": non-blocking"); + else + dbg(__FUNCTION__ ": blocking"); +#endif + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - up(&s->open_sem); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; + file->private_data = s; + /* wait for device to become free */ down(&s->open_sem); - } + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } - spin_lock_irqsave(&s->lock, flags); + spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = - s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; - s->capcc &= ~(CC_SM | CC_DF); - set_adc_rate(s, 8000); - if ((minor & 0xf) == SND_DEV_DSP16) - s->capcc |= CC_DF; - outw(s->capcc, s->io+IT_AC_CAPCC); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = - s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; - s->pcc &= ~(CC_SM | CC_DF); - set_dac_rate(s, 8000); - if ((minor & 0xf) == SND_DEV_DSP16) - s->pcc |= CC_DF; - outw(s->pcc, s->io+IT_AC_PCC); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - - spin_unlock_irqrestore(&s->lock, flags); - - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&s->open_sem); - return 0; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; + s->capcc &= ~(CC_SM | CC_DF); + set_adc_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->capcc |= CC_DF; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) { + spin_unlock_irqrestore(&s->lock, flags); + return ret; + } + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; + s->pcc &= ~(CC_SM | CC_DF); + set_dac_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->pcc |= CC_DF; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) { + spin_unlock_irqrestore(&s->lock, flags); + return ret; + } + } + + spin_unlock_irqrestore(&s->lock, flags); + + s->open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); + up(&s->open_sem); + return 0; } static int it8172_release(struct inode *inode, struct file *file) { - struct it8172_state *s = (struct it8172_state *)file->private_data; + struct it8172_state *s = (struct it8172_state *)file->private_data; - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - down(&s->open_sem); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - dealloc_dmabuf(s, &s->dma_dac); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - } - s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - up(&s->open_sem); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; +#ifdef IT8172_VERBOSE_DEBUG + dbg(__FUNCTION__); +#endif + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; } static /*const*/ struct file_operations it8172_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = it8172_read, - .write = it8172_write, - .poll = it8172_poll, - .ioctl = it8172_ioctl, - .mmap = it8172_mmap, - .open = it8172_open, - .release = it8172_release, + owner: THIS_MODULE, + llseek: it8172_llseek, + read: it8172_read, + write: it8172_write, + poll: it8172_poll, + ioctl: it8172_ioctl, + mmap: it8172_mmap, + open: it8172_open, + release: it8172_release, }; @@ -1657,51 +1898,51 @@ static int proc_it8172_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) { - struct it8172_state *s; - int cnt, len = 0; - - if (list_empty(&devs)) - return 0; - s = list_entry(devs.next, struct it8172_state, devs); - - /* print out header */ - len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n"); - - // print out digital controller state - len += sprintf (buf + len, "IT8172 Audio Controller registers\n"); - len += sprintf (buf + len, "---------------------------------\n"); - cnt=0; - while (cnt < 0x72) { - if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA || - cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA || - cnt == IT_AC_PFDP) { - len+= sprintf (buf + len, "reg %02x = %08x\n", - cnt, inl(s->io+cnt)); - cnt += 4; - } else { - len+= sprintf (buf + len, "reg %02x = %04x\n", - cnt, inw(s->io+cnt)); - cnt += 2; - } - } - - /* print out CODEC state */ - len += sprintf (buf + len, "\nAC97 CODEC registers\n"); - len += sprintf (buf + len, "----------------------\n"); - for (cnt=0; cnt <= 0x7e; cnt = cnt +2) - len+= sprintf (buf + len, "reg %02x = %04x\n", - cnt, rdcodec(&s->codec, cnt)); + struct it8172_state *s; + int cnt, len = 0; - if (fpos >=len){ - *start = buf; + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct it8172_state, devs); + + /* print out header */ + len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf (buf + len, "IT8172 Audio Controller registers\n"); + len += sprintf (buf + len, "---------------------------------\n"); + cnt=0; + while (cnt < 0x72) { + if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA || + cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA || + cnt == IT_AC_PFDP) { + len+= sprintf (buf + len, "reg %02x = %08x\n", + cnt, inl(s->io+cnt)); + cnt += 4; + } else { + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, inw(s->io+cnt)); + cnt += 2; + } + } + + /* print out CODEC state */ + len += sprintf (buf + len, "\nAC97 CODEC registers\n"); + len += sprintf (buf + len, "----------------------\n"); + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, rdcodec(s->codec, cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; *eof =1; - return 0; - } - *start = buf + fpos; - if ((len -= fpos) > length) - return length; - *eof =1; - return len; + return len; } #endif /* IT8172_DEBUG */ @@ -1712,244 +1953,307 @@ #define NR_DEVICE 5 static int spdif[NR_DEVICE] = { 0, }; +static int i2s_fmt[NR_DEVICE] = { 0, }; static unsigned int devindex = 0; MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); MODULE_PARM_DESC(spdif, "if 1 the S/PDIF digital output is enabled"); +MODULE_PARM(i2s_fmt, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(i2s_fmt, "the format of I2S"); MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); -MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver"); -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IT8172 Audio Driver"); /* --------------------------------------------------------------------- */ static int __devinit it8172_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { - struct it8172_state *s; - int i, val; - unsigned short pcisr, vol; - unsigned char legacy, imc; - char proc_str[80]; - - if (pcidev->irq == 0) - return -1; - - if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) { - printk(KERN_ERR PFX "alloc of device struct failed\n"); - return -1; - } + struct it8172_state *s; + int i, val; + unsigned short pcisr, vol; + unsigned char legacy, imc; + char proc_str[80]; + + if (pcidev->irq == 0) + return -1; + + if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) { + err("alloc of device struct failed"); + return -1; + } - memset(s, 0, sizeof(struct it8172_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_MUTEX(&s->open_sem); - spin_lock_init(&s->lock); - s->dev = pcidev; - s->io = pci_resource_start(pcidev, 0); - s->irq = pcidev->irq; - s->vendor = pcidev->vendor; - s->device = pcidev->device; - pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); - s->codec.private_data = s; - s->codec.id = 0; - s->codec.codec_read = rdcodec; - s->codec.codec_write = wrcodec; - s->codec.codec_wait = waitcodec; - - if (!request_region(s->io, pci_resource_len(pcidev,0), - IT8172_MODULE_NAME)) { - printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n", - s->io, s->io + pci_resource_len(pcidev,0)-1); - goto err_region; - } - if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT, - IT8172_MODULE_NAME, s)) { - printk(KERN_ERR PFX "irq %u in use\n", s->irq); - goto err_irq; - } - - printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq); - - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0) - goto err_dev1; - if ((s->codec.dev_mixer = - register_sound_mixer(&it8172_mixer_fops, -1)) < 0) - goto err_dev2; + memset(s, 0, sizeof(struct it8172_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + s->vendor = pcidev->vendor; + s->device = pcidev->device; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); + + s->codec = ac97_alloc_codec(); + if(s->codec == NULL) + goto err_codec; + + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; + s->codec->codec_wait = waitcodec; + + if (!request_region(s->io, pci_resource_len(pcidev,0), + IT8172_MODULE_NAME)) { + err("io ports %#lx->%#lx in use", + s->io, s->io + pci_resource_len(pcidev,0)-1); + goto err_region; + } + if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT, + IT8172_MODULE_NAME, s)) { + err("irq %u in use", s->irq); + goto err_irq; + } + + info("IO at %#lx, IRQ %d", s->io, s->irq); + + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec->dev_mixer = + register_sound_mixer(&it8172_mixer_fops, -1)) < 0) + goto err_dev2; #ifdef IT8172_DEBUG - /* initialize the debug proc device */ - s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL, - proc_it8172_dump, NULL); + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL, + proc_it8172_dump, NULL); #endif /* IT8172_DEBUG */ - /* - * Reset the Audio device using the IT8172 PCI Reset register. This - * creates an audible double click on a speaker connected to Line-out. - */ - IT_IO_READ16(IT_PM_PCISR, pcisr); - pcisr |= IT_PM_PCISR_ACSR; - IT_IO_WRITE16(IT_PM_PCISR, pcisr); - /* wait up to 100msec for reset to complete */ - for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) { - it8172_delay(10); - if (i == 10) - break; + /* + * Reset the Audio device using the IT8172 PCI Reset register. This + * creates an audible double click on a speaker connected to Line-out. + */ IT_IO_READ16(IT_PM_PCISR, pcisr); - } - if (i == 10) { - printk(KERN_ERR PFX "chip reset timeout!\n"); - goto err_dev3; - } - - /* enable pci io and bus mastering */ - if (pci_enable_device(pcidev)) - goto err_dev3; - pci_set_master(pcidev); - - /* get out of legacy mode */ - pci_read_config_byte (pcidev, 0x40, &legacy); - pci_write_config_byte (pcidev, 0x40, legacy & ~1); - - s->spdif_volume = -1; - /* check to see if s/pdif mode is being requested */ - if (spdif[devindex]) { - printk(KERN_INFO PFX "enabling S/PDIF output\n"); - s->spdif_volume = 0; - outb(GC_SOE, s->io+IT_AC_GC); - } else { - printk(KERN_INFO PFX "disabling S/PDIF output\n"); - outb(0, s->io+IT_AC_GC); - } - - /* cold reset the AC97 */ - outw(CODECC_CR, s->io+IT_AC_CODECC); - udelay(1000); - outw(0, s->io+IT_AC_CODECC); - /* need to delay around 500msec(bleech) to give - some CODECs enough time to wakeup */ - it8172_delay(500); - - /* AC97 warm reset to start the bitclk */ - outw(CODECC_WR, s->io+IT_AC_CODECC); - udelay(1000); - outw(0, s->io+IT_AC_CODECC); - - /* codec init */ - if (!ac97_probe_codec(&s->codec)) - goto err_dev3; - - /* Enable Volume button interrupts */ - imc = inb(s->io+IT_AC_IMC); - outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC); - - /* Un-mute PCM and FM out on the controller */ - vol = inw(s->io+IT_AC_PCMOV); - outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV); - vol = inw(s->io+IT_AC_FMOV); - outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV); - - /* set channel defaults to 8-bit, mono, 8 Khz */ - s->pcc = 0; - s->capcc = 0; - set_dac_rate(s, 8000); - set_adc_rate(s, 8000); - - /* set mic to be the recording source */ - val = SOUND_MASK_MIC; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - - /* mute master and PCM when in S/PDIF mode */ - if (s->spdif_volume != -1) { - val = 0x0000; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, - (unsigned long)&val); - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, + pcisr |= IT_PM_PCISR_ACSR; + IT_IO_WRITE16(IT_PM_PCISR, pcisr); + /* wait up to 100msec for reset to complete */ + for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) { + it8172_delay(10); + if (i == 10) + break; + IT_IO_READ16(IT_PM_PCISR, pcisr); + } + if (i == 10) { + err("chip reset timeout!"); + goto err_dev3; + } + + /* enable pci io and bus mastering */ + if (pci_enable_device(pcidev)) + goto err_dev3; + pci_set_master(pcidev); + + /* get out of legacy mode */ + pci_read_config_byte (pcidev, 0x40, &legacy); + pci_write_config_byte (pcidev, 0x40, legacy & ~1); + + s->spdif_volume = -1; + /* check to see if s/pdif mode is being requested */ + if (spdif[devindex]) { + info("enabling S/PDIF output"); + s->spdif_volume = 0; + outb(GC_SOE, s->io+IT_AC_GC); + } else { + info("disabling S/PDIF output"); + outb(0, s->io+IT_AC_GC); + } + + /* check to see if I2S format requested */ + if (i2s_fmt[devindex]) { + info("setting I2S format to 0x%02x", i2s_fmt[devindex]); + outb(i2s_fmt[devindex], s->io+IT_AC_I2SMC); + } else { + outb(I2SMC_I2SF_I2S, s->io+IT_AC_I2SMC); + } + + /* cold reset the AC97 */ + outw(CODECC_CR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + /* need to delay around 500msec(bleech) to give + some CODECs enough time to wakeup */ + it8172_delay(500); + + /* AC97 warm reset to start the bitclk */ + outw(CODECC_WR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + + /* codec init */ + if (!ac97_probe_codec(s->codec)) + goto err_dev3; + + /* add I2S as allowable recording source */ + s->codec->record_sources |= SOUND_MASK_I2S; + + /* Enable Volume button interrupts */ + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC); + + /* Un-mute PCM and FM out on the controller */ + vol = inw(s->io+IT_AC_PCMOV); + outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV); + vol = inw(s->io+IT_AC_FMOV); + outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV); + + /* set channel defaults to 8-bit, mono, 8 Khz */ + s->pcc = 0; + s->capcc = 0; + set_dac_rate(s, 8000); + set_adc_rate(s, 8000); + + /* set mic to be the recording source */ + val = SOUND_MASK_MIC; + mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - } + + /* mute AC'97 master and PCM when in S/PDIF mode */ + if (s->spdif_volume != -1) { + val = 0x0000; + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_VOLUME, + (unsigned long)&val); + s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_PCM, + (unsigned long)&val); + } #ifdef IT8172_DEBUG - sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, s->codec.id); - s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, - ac97_read_proc, &s->codec); + sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, + s->codec->id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, s->codec); #endif - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - pcidev->dma_mask = 0xffffffff; - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); err_dev2: - unregister_sound_dsp(s->dev_audio); + unregister_sound_dsp(s->dev_audio); err_dev1: - printk(KERN_ERR PFX "cannot register misc device\n"); - free_irq(s->irq, s); + err("cannot register misc device"); + free_irq(s->irq, s); err_irq: - release_region(s->io, pci_resource_len(pcidev,0)); + release_region(s->io, pci_resource_len(pcidev,0)); err_region: - kfree(s); - return -1; + ac97_release_codec(s->codec); + err_codec: + kfree(s); + return -1; } static void __devinit it8172_remove(struct pci_dev *dev) { - struct it8172_state *s = pci_get_drvdata(dev); + struct it8172_state *s = pci_get_drvdata(dev); - if (!s) - return; - list_del(&s->devs); + if (!s) + return; + list_del(&s->devs); #ifdef IT8172_DEBUG - if (s->ps) - remove_proc_entry(IT8172_MODULE_NAME, NULL); + if (s->ps) + remove_proc_entry(IT8172_MODULE_NAME, NULL); #endif /* IT8172_DEBUG */ - synchronize_irq(s->irq); - free_irq(s->irq, s); - release_region(s->io, pci_resource_len(dev,0)); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); - kfree(s); - pci_set_drvdata(dev, NULL); + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, pci_resource_len(dev,0)); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec->dev_mixer); + ac97_codec_release(s->codec); + kfree(s); + pci_set_drvdata(dev, NULL); } static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID, - PCI_ANY_ID, 0, 0 }, - { 0, } + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID, + PCI_ANY_ID, 0, 0 }, + { 0, } }; MODULE_DEVICE_TABLE(pci, id_table); static struct pci_driver it8172_driver = { - .name = IT8172_MODULE_NAME, - .id_table = id_table, - .probe = it8172_probe, - .remove = it8172_remove, + name: IT8172_MODULE_NAME, + id_table: id_table, + probe: it8172_probe, + remove: it8172_remove }; static int __init init_it8172(void) { - printk("version v0.26 time " __TIME__ " " __DATE__ "\n"); - return pci_module_init(&it8172_driver); + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + info("version v0.5 time " __TIME__ " " __DATE__); + return pci_module_init(&it8172_driver); } static void __exit cleanup_it8172(void) { - printk(KERN_INFO PFX "unloading\n"); - pci_unregister_driver(&it8172_driver); + info("unloading"); + pci_unregister_driver(&it8172_driver); } module_init(init_it8172); module_exit(cleanup_it8172); +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: it8172=[spdif],[i2s:] */ + +static int __init it8172_setup(char *options) +{ + char* this_opt; + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + + if (!options || !*options) + return 0; + + for(this_opt=strtok(options, ","); + this_opt; this_opt=strtok(NULL, ",")) { + if (!strncmp(this_opt, "spdif", 5)) { + spdif[nr_dev] = 1; + } else if (!strncmp(this_opt, "i2s:", 4)) { + if (!strncmp(this_opt+4, "dac", 3)) + i2s_fmt[nr_dev] = I2SMC_I2SF_DAC; + else if (!strncmp(this_opt+4, "adc", 3)) + i2s_fmt[nr_dev] = I2SMC_I2SF_ADC; + else if (!strncmp(this_opt+4, "i2s", 3)) + i2s_fmt[nr_dev] = I2SMC_I2SF_I2S; + } + } + + nr_dev++; + return 1; +} + +__setup("it8172=", it8172_setup); + +#endif /* MODULE */ diff -urN linux-2.5.75-bk1/sound/oss/maestro.c linux-2.5.75-bk2/sound/oss/maestro.c --- linux-2.5.75-bk1/sound/oss/maestro.c 2003-07-10 13:14:10.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/maestro.c 2003-07-12 04:37:48.000000000 -0700 @@ -2024,8 +2024,8 @@ VALIDATE_CARD(card); if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); - strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name, card_names[card->card_type], sizeof(info.name)); info.modify_counter = card->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -2033,8 +2033,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); - strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name, card_names[card->card_type], sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -urN linux-2.5.75-bk1/sound/oss/maestro3.c linux-2.5.75-bk2/sound/oss/maestro3.c --- linux-2.5.75-bk1/sound/oss/maestro3.c 2003-07-10 13:14:15.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/maestro3.c 2003-07-12 04:37:48.000000000 -0700 @@ -2301,9 +2301,8 @@ { struct ac97_codec *codec; - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); codec->private_data = card; codec->codec_read = m3_ac97_read; @@ -2313,13 +2312,13 @@ if (ac97_probe_codec(codec) == 0) { printk(KERN_ERR PFX "codec probe failed\n"); - kfree(codec); + ac97_release_codec(codec); return -1; } if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) { printk(KERN_ERR PFX "couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); return -1; } diff -urN linux-2.5.75-bk1/sound/oss/msnd_pinnacle.c linux-2.5.75-bk2/sound/oss/msnd_pinnacle.c --- linux-2.5.75-bk1/sound/oss/msnd_pinnacle.c 2003-07-10 13:11:33.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/msnd_pinnacle.c 2003-07-12 04:37:48.000000000 -0700 @@ -555,8 +555,8 @@ } #define set_mixer_info() \ - strlcpy(info.id, "MSNDMIXER", sizeof(info.id)); \ - strlcpy(info.name, "MultiSound Mixer", sizeof(info.name)); + strncpy(info.id, "MSNDMIXER", sizeof(info.id)); \ + strncpy(info.name, "MultiSound Mixer", sizeof(info.name)); static int mixer_ioctl(unsigned int cmd, unsigned long arg) { diff -urN linux-2.5.75-bk1/sound/oss/nec_vrc5477.c linux-2.5.75-bk2/sound/oss/nec_vrc5477.c --- linux-2.5.75-bk1/sound/oss/nec_vrc5477.c 2003-07-10 13:12:50.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/nec_vrc5477.c 2003-07-12 04:37:48.000000000 -0700 @@ -194,7 +194,7 @@ struct proc_dir_entry *ac97_ps; #endif /* VRC5477_AC97_DEBUG */ - struct ac97_codec codec; + struct ac97_codec *codec; unsigned dacChannels, adcChannels; unsigned short dacRate, adcRate; @@ -385,7 +385,7 @@ static void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate) { - wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, rate); + wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, rate); s->adcRate = rate; } @@ -393,8 +393,8 @@ static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate) { if(s->extended_status & AC97_EXTSTAT_VRA) { - wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate); - s->dacRate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE); + wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, rate); + s->dacRate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE); } } @@ -865,7 +865,7 @@ if (list == &devs) return -ENODEV; s = list_entry(list, struct vrc5477_ac97_state, devs); - if (s->codec.dev_mixer == minor) + if (s->codec->dev_mixer == minor) break; } file->private_data = s; @@ -889,7 +889,7 @@ { struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; - struct ac97_codec *codec = &s->codec; + struct ac97_codec *codec = s->codec; return mixdev_ioctl(codec, cmd, arg); } @@ -1187,7 +1187,7 @@ #endif count -= copyCount; - totalCopyCount =+ copyCount; + totalCopyCount += copyCount; avail -= copyFragCount; totalCopyFragCount += copyFragCount; @@ -1563,7 +1563,7 @@ return -EINVAL; } - return mixdev_ioctl(&s->codec, cmd, arg); + return mixdev_ioctl(s->codec, cmd, arg); } @@ -1789,7 +1789,7 @@ len += sprintf (buf + len, "----------------------\n"); for (cnt=0; cnt <= 0x7e; cnt = cnt +2) len+= sprintf (buf + len, "reg %02x = %04x\n", - cnt, rdcodec(&s->codec, cnt)); + cnt, rdcodec(s->codec, cnt)); if (fpos >=len){ *start = buf; @@ -1842,12 +1842,14 @@ s->dev = pcidev; s->io = pci_resource_start(pcidev, 0); s->irq = pcidev->irq; + + s->codec = ac97_alloc_codec(); - s->codec.private_data = s; - s->codec.id = 0; - s->codec.codec_read = rdcodec; - s->codec.codec_write = wrcodec; - s->codec.codec_wait = waitcodec; + s->codec->private_data = s; + s->codec->id = 0; + s->codec->codec_read = rdcodec; + s->codec->codec_write = wrcodec; + s->codec->codec_wait = waitcodec; /* setting some other default values such as * adcChannels, adcRate is done in open() so that @@ -1855,7 +1857,7 @@ */ /* test if get response from ac97, if not return */ - if (ac97_codec_not_present(&(s->codec))) { + if (ac97_codec_not_present(s->codec)) { printk(KERN_ERR PFX "no ac97 codec\n"); goto err_region; @@ -1878,7 +1880,7 @@ /* register devices */ if ((s->dev_audio = register_sound_dsp(&vrc5477_ac97_audio_fops, -1)) < 0) goto err_dev1; - if ((s->codec.dev_mixer = + if ((s->codec->dev_mixer = register_sound_mixer(&vrc5477_ac97_mixer_fops, -1)) < 0) goto err_dev2; @@ -1899,22 +1901,22 @@ while (inl(s->io + VRC5477_ACLINK_CTRL) & VRC5477_ACLINK_CTRL_RST_ON); /* codec init */ - if (!ac97_probe_codec(&s->codec)) + if (!ac97_probe_codec(s->codec)) goto err_dev3; #ifdef VRC5477_AC97_DEBUG sprintf(proc_str, "driver/%s/%d/ac97", - VRC5477_AC97_MODULE_NAME, s->codec.id); + VRC5477_AC97_MODULE_NAME, s->codec->id); s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, - ac97_read_proc, &s->codec); + ac97_read_proc, s->codec); /* TODO : why this proc file does not show up? */ #endif /* Try to enable variable rate audio mode. */ - wrcodec(&s->codec, AC97_EXTENDED_STATUS, - rdcodec(&s->codec, AC97_EXTENDED_STATUS) | AC97_EXTSTAT_VRA); + wrcodec(s->codec, AC97_EXTENDED_STATUS, + rdcodec(s->codec, AC97_EXTENDED_STATUS) | AC97_EXTSTAT_VRA); /* Did we enable it? */ - if(rdcodec(&s->codec, AC97_EXTENDED_STATUS) & AC97_EXTSTAT_VRA) + if(rdcodec(s->codec, AC97_EXTENDED_STATUS) & AC97_EXTSTAT_VRA) s->extended_status |= AC97_EXTSTAT_VRA; else { s->dacRate = 48000; @@ -1923,17 +1925,17 @@ } /* let us get the default volumne louder */ - wrcodec(&s->codec, 0x2, 0x1010); /* master volume, middle */ - wrcodec(&s->codec, 0xc, 0x10); /* phone volume, middle */ - // wrcodec(&s->codec, 0xe, 0x10); /* misc volume, middle */ - wrcodec(&s->codec, 0x10, 0x8000); /* line-in 2 line-out disable */ - wrcodec(&s->codec, 0x18, 0x0707); /* PCM out (line out) middle */ + wrcodec(s->codec, 0x2, 0x1010); /* master volume, middle */ + wrcodec(s->codec, 0xc, 0x10); /* phone volume, middle */ + // wrcodec(s->codec, 0xe, 0x10); /* misc volume, middle */ + wrcodec(s->codec, 0x10, 0x8000); /* line-in 2 line-out disable */ + wrcodec(s->codec, 0x18, 0x0707); /* PCM out (line out) middle */ /* by default we select line in the input */ - wrcodec(&s->codec, 0x1a, 0x0404); - wrcodec(&s->codec, 0x1c, 0x0f0f); - wrcodec(&s->codec, 0x1e, 0x07); + wrcodec(s->codec, 0x1a, 0x0404); + wrcodec(s->codec, 0x1c, 0x0f0f); + wrcodec(s->codec, 0x1e, 0x07); /* enable the master interrupt but disable all others */ outl(VRC5477_INT_MASK_NMASK, s->io + VRC5477_INT_MASK); @@ -1949,7 +1951,7 @@ return 0; err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1: @@ -1958,6 +1960,7 @@ err_irq: release_region(s->io, pci_resource_len(pcidev,0)); err_region: + ac97_release_codec(codec); kfree(s); return -1; } @@ -1979,7 +1982,8 @@ free_irq(s->irq, s); release_region(s->io, pci_resource_len(dev,0)); unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_mixer(s->codec->dev_mixer); + ac97_release_codec(s->codec); kfree(s); pci_set_drvdata(dev, NULL); } diff -urN linux-2.5.75-bk1/sound/oss/nm256_audio.c linux-2.5.75-bk2/sound/oss/nm256_audio.c --- linux-2.5.75-bk1/sound/oss/nm256_audio.c 2003-07-10 13:08:52.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/nm256_audio.c 2003-07-12 04:37:48.000000000 -0700 @@ -1105,7 +1105,7 @@ printk (KERN_ERR "NM256: This doesn't look to me like the AC97-compatible version.\n"); printk (KERN_ERR " You can force the driver to load by passing in the module\n"); printk (KERN_ERR " parameter:\n"); - printk (KERN_ERR " force_ac97 = 1\n"); + printk (KERN_ERR " force_load = 1\n"); printk (KERN_ERR "\n"); printk (KERN_ERR " More likely, you should be using the appropriate SB-16 or\n"); printk (KERN_ERR " CS4232 driver instead. (If your BIOS has settings for\n"); diff -urN linux-2.5.75-bk1/sound/oss/sonicvibes.c linux-2.5.75-bk2/sound/oss/sonicvibes.c --- linux-2.5.75-bk1/sound/oss/sonicvibes.c 2003-07-10 13:08:53.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/sonicvibes.c 2003-07-12 04:37:48.000000000 -0700 @@ -1046,8 +1046,8 @@ VALIDATE_STATE(s); if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "SonicVibes", sizeof(info.id)); - strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); info.modify_counter = s->mix.modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -1055,8 +1055,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "SonicVibes", sizeof(info.id)); - strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -urN linux-2.5.75-bk1/sound/oss/soundcard.c linux-2.5.75-bk2/sound/oss/soundcard.c --- linux-2.5.75-bk1/sound/oss/soundcard.c 2003-07-10 13:09:33.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/soundcard.c 2003-07-12 04:37:48.000000000 -0700 @@ -289,8 +289,8 @@ { mixer_info info; - strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); info.modify_counter = mixer_devs[dev]->modify_counter; if (__copy_to_user(arg, &info, sizeof(info))) return -EFAULT; @@ -301,8 +301,8 @@ { _old_mixer_info info; - strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0; diff -urN linux-2.5.75-bk1/sound/oss/swarm_cs4297a.c linux-2.5.75-bk2/sound/oss/swarm_cs4297a.c --- linux-2.5.75-bk1/sound/oss/swarm_cs4297a.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.75-bk2/sound/oss/swarm_cs4297a.c 2003-07-12 04:37:48.000000000 -0700 @@ -0,0 +1,2745 @@ +/******************************************************************************* +* +* "swarm_cs4297a.c" -- Cirrus Logic-Crystal CS4297a linux audio driver. +* +* Copyright (C) 2001 Broadcom Corporation. +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- adapted from drivers by Thomas Sailer, +* -- but don't bug him; Problems should go to: +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* -- adapted from cs4281 PCI driver for cs4297a on +* BCM1250 Synchronous Serial interface +* (kwalker@broadcom.com) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Module command line parameters: +* none +* +* Supported devices: +* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible +* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible +* /dev/midi simple MIDI UART interface, no ioctl +* +* Modification History +* 08/20/00 trw - silence and no stopping DAC until release +* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop. +* 09/18/00 trw - added 16bit only record with conversion +* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous +* capture/playback rates) +* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin +* libOSSm.so) +* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal) +* 11/03/00 trw - fixed interrupt loss/stutter, added debug. +* 11/10/00 bkz - added __devinit to cs4297a_hw_init() +* 11/10/00 trw - fixed SMP and capture spinlock hang. +* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm. +* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix. +* 12/08/00 trw - added PM support. +* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 +* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident. +* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup. +* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +* +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct cs4297a_state; +EXPORT_NO_SYMBOLS; + +static void stop_dac(struct cs4297a_state *s); +static void stop_adc(struct cs4297a_state *s); +static void start_dac(struct cs4297a_state *s); +static void start_adc(struct cs4297a_state *s); +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +// --------------------------------------------------------------------- + +#define CS4297a_MAGIC 0xf00beef1 + +// buffer order determines the size of the dma buffer for the driver. +// under Linux, a smaller buffer allows more responsiveness from many of the +// applications (e.g. games). A larger buffer allows some of the apps (esound) +// to not underrun the dma buffer as easily. As default, use 32k (order=3) +// rather than 64k as some of the games work more responsively. +// log base 2( buff sz = 32k). + +//static unsigned long defaultorder = 3; +//MODULE_PARM(defaultorder, "i"); + +// +// Turn on/off debugging compilation by commenting out "#define CSDEBUG" +// +#define CSDEBUG 0 +#if CSDEBUG +#define CSDEBUG_INTERFACE 1 +#else +#undef CSDEBUG_INTERFACE +#endif +// +// cs_debugmask areas +// +#define CS_INIT 0x00000001 // initialization and probe functions +#define CS_ERROR 0x00000002 // tmp debugging bit placeholder +#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other) +#define CS_FUNCTION 0x00000008 // enter/leave functions +#define CS_WAVE_WRITE 0x00000010 // write information for wave +#define CS_WAVE_READ 0x00000020 // read information for wave +#define CS_AC97 0x00000040 // AC97 register access +#define CS_DESCR 0x00000080 // descriptor management +#define CS_OPEN 0x00000400 // all open functions in the driver +#define CS_RELEASE 0x00000800 // all release functions in the driver +#define CS_PARMS 0x00001000 // functional and operational parameters +#define CS_IOCTL 0x00002000 // ioctl (non-mixer) +#define CS_TMP 0x10000000 // tmp debug mask bit + +// +// CSDEBUG is usual mode is set to 1, then use the +// cs_debuglevel and cs_debugmask to turn on or off debugging. +// Debug level of 1 has been defined to be kernel errors and info +// that should be printed on any released driver. +// +#if CSDEBUG +#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;} +#else +#define CS_DBGOUT(mask,level,x) +#endif + +#if CSDEBUG +static unsigned long cs_debuglevel = 4; // levels range from 1-9 +static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/; +MODULE_PARM(cs_debuglevel, "i"); +MODULE_PARM(cs_debugmask, "i"); +#endif +#define CS_TRUE 1 +#define CS_FALSE 0 + +#define CS_TYPE_ADC 0 +#define CS_TYPE_DAC 1 + +#define SER_BASE (A_SER_BASE_1 + KSEG1) +#define SS_CSR(t) (SER_BASE+t) +#define SS_TXTBL(t) (SER_BASE+R_SER_TX_TABLE_BASE+(t*8)) +#define SS_RXTBL(t) (SER_BASE+R_SER_RX_TABLE_BASE+(t*8)) + +#define FRAME_BYTES 32 +#define FRAME_SAMPLE_BYTES 4 + +/* Should this be variable? */ +#define SAMPLE_BUF_SIZE (16*1024) +#define SAMPLE_FRAME_COUNT (SAMPLE_BUF_SIZE / FRAME_SAMPLE_BYTES) +/* The driver can explode/shrink the frames to/from a smaller sample + buffer */ +#define DMA_BLOAT_FACTOR 1 +#define DMA_DESCR (SAMPLE_FRAME_COUNT / DMA_BLOAT_FACTOR) +#define DMA_BUF_SIZE (DMA_DESCR * FRAME_BYTES) + +/* Use the maxmium count (255 == 5.1 ms between interrupts) */ +#define DMA_INT_CNT ((1 << S_DMA_INT_PKTCNT) - 1) + +/* Figure this out: how many TX DMAs ahead to schedule a reg access */ +#define REG_LATENCY 150 + +#define FRAME_TX_US 20 + +#define SERDMA_NEXTBUF(d,f) (((d)->f+1) % (d)->ringsz) + +static const char invalid_magic[] = + KERN_CRIT "cs4297a: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CS4297a_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +struct list_head cs4297a_devs = { &cs4297a_devs, &cs4297a_devs }; + +typedef struct serdma_descr_s { + u64 descr_a; + u64 descr_b; +} serdma_descr_t; + +typedef unsigned long paddr_t; + +typedef struct serdma_s { + unsigned ringsz; + serdma_descr_t *descrtab; + serdma_descr_t *descrtab_end; + paddr_t descrtab_phys; + + serdma_descr_t *descr_add; + serdma_descr_t *descr_rem; + + u64 *dma_buf; // buffer for DMA contents (frames) + paddr_t dma_buf_phys; + u16 *sample_buf; // tmp buffer for sample conversions + u16 *sb_swptr; + u16 *sb_hwptr; + u16 *sb_end; + + dma_addr_t dmaaddr; +// unsigned buforder; // Log base 2 of 'dma_buf' size in bytes.. + unsigned numfrag; // # of 'fragments' in the buffer. + unsigned fragshift; // Log base 2 of fragment size. + unsigned hwptr, swptr; + unsigned total_bytes; // # bytes process since open. + unsigned blocks; // last returned blocks value GETOPTR + unsigned wakeup; // interrupt occurred on block + int count; + unsigned underrun; // underrun flag + unsigned error; // over/underrun + wait_queue_head_t wait; + wait_queue_head_t reg_wait; + // redundant, but makes calculations easier + unsigned fragsize; // 2**fragshift.. + unsigned sbufsz; // 2**buforder. + unsigned fragsamples; + // OSS stuff + unsigned mapped:1; // Buffer mapped in cs4297a_mmap()? + unsigned ready:1; // prog_dmabuf_dac()/adc() successful? + unsigned endcleared:1; + unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +} serdma_t; + +struct cs4297a_state { + // magic + unsigned int magic; + + struct list_head list; + + // soundcore stuff + int dev_audio; + int dev_mixer; + + // hardware resources + unsigned int irq; + + struct { + unsigned int rx_ovrrn; /* FIFO */ + unsigned int rx_overflow; /* staging buffer */ + unsigned int tx_underrun; + unsigned int rx_bad; + unsigned int rx_good; + } stats; + + // mixer registers + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + // wave stuff + struct properties { + unsigned fmt; + unsigned fmt_original; // original requested format + unsigned channels; + unsigned rate; + } prop_dac, prop_adc; + unsigned conversion:1; // conversion from 16 to 8 bit in progress + unsigned ena; + spinlock_t lock; + struct semaphore open_sem; + struct semaphore open_sem_adc; + struct semaphore open_sem_dac; + mode_t open_mode; + wait_queue_head_t open_wait; + wait_queue_head_t open_wait_adc; + wait_queue_head_t open_wait_dac; + + dma_addr_t dmaaddr_sample_buf; + unsigned buforder_sample_buf; // Log base 2 of 'dma_buf' size in bytes.. + + serdma_t dma_dac, dma_adc; + + volatile u16 read_value; + volatile u16 read_reg; + volatile u64 reg_request; +}; + +#if 1 +#define prog_codec(a,b) +#define dealloc_dmabuf(a,b); +#endif + +static int prog_dmabuf_adc(struct cs4297a_state *s) +{ + s->dma_adc.ready = 1; + return 0; +} + + +static int prog_dmabuf_dac(struct cs4297a_state *s) +{ + s->dma_dac.ready = 1; + return 0; +} + +static void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *) buf) + bptr, c, x); + bptr = 0; + len -= x; + } + CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO + "cs4297a: clear_advance(): memset %d at 0x%.8x for %d size \n", + (unsigned)c, (unsigned)((char *) buf) + bptr, len)); + memset(((char *) buf) + bptr, c, len); +} + +#if CSDEBUG + +// DEBUG ROUTINES + +#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) +#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) +#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) +#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) + +static void cs_printioctl(unsigned int x) +{ + unsigned int i; + unsigned char vidx; + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + switch (x) { + case SOUND_MIXER_CS_GETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_GETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); + break; + case SOUND_MIXER_CS_SETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_SETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); + break; + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); + break; + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); + break; + case SNDCTL_DSP_SETDUPLEX: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); + break; + case SNDCTL_DSP_GETCAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); + break; + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); + break; + case SNDCTL_DSP_SPEED: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); + break; + case SNDCTL_DSP_STEREO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); + break; + case SNDCTL_DSP_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); + break; + case SNDCTL_DSP_GETFMTS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); + break; + case SNDCTL_DSP_SETFMT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); + break; + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); + break; + case SNDCTL_DSP_GETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); + break; + case SNDCTL_DSP_SETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); + break; + case SNDCTL_DSP_GETOSPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); + break; + case SNDCTL_DSP_GETISPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); + break; + case SNDCTL_DSP_NONBLOCK: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); + break; + case SNDCTL_DSP_GETODELAY: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); + break; + case SNDCTL_DSP_GETIPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); + break; + case SNDCTL_DSP_GETOPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); + break; + case SNDCTL_DSP_GETBLKSIZE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); + break; + case SNDCTL_DSP_SETFRAGMENT: + CS_DBGOUT(CS_IOCTL, 4, + printk("SNDCTL_DSP_SETFRAGMENT:\n")); + break; + case SNDCTL_DSP_SUBDIVIDE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); + break; + case SOUND_PCM_READ_RATE: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); + break; + case SOUND_PCM_READ_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_READ_CHANNELS:\n")); + break; + case SOUND_PCM_READ_BITS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); + break; + case SOUND_PCM_WRITE_FILTER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_WRITE_FILTER:\n")); + break; + case SNDCTL_DSP_SETSYNCRO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); + break; + case SOUND_PCM_READ_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); + break; + case SOUND_MIXER_PRIVATE1: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); + break; + case SOUND_MIXER_PRIVATE2: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); + break; + case SOUND_MIXER_PRIVATE3: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); + break; + case SOUND_MIXER_PRIVATE4: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); + break; + case SOUND_MIXER_PRIVATE5: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); + break; + case SOUND_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); + break; + case SOUND_OLD_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); + break; + + default: + switch (_IOC_NR(x)) { + case SOUND_MIXER_VOLUME: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_VOLUME:\n")); + break; + case SOUND_MIXER_SPEAKER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SPEAKER:\n")); + break; + case SOUND_MIXER_RECLEV: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECLEV:\n")); + break; + case SOUND_MIXER_MIC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_MIC:\n")); + break; + case SOUND_MIXER_SYNTH: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SYNTH:\n")); + break; + case SOUND_MIXER_RECSRC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECSRC:\n")); + break; + case SOUND_MIXER_DEVMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_DEVMASK:\n")); + break; + case SOUND_MIXER_RECMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECMASK:\n")); + break; + case SOUND_MIXER_STEREODEVS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_STEREODEVS:\n")); + break; + case SOUND_MIXER_CAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n")); + break; + default: + i = _IOC_NR(x); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) { + CS_DBGOUT(CS_IOCTL, 4, printk + ("UNKNOWN IOCTL: 0x%.8x NR=%d\n", + x, i)); + } else { + CS_DBGOUT(CS_IOCTL, 4, printk + ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n", + x, i)); + } + break; + } + } +} +#endif + + +static int ser_init(struct cs4297a_state *s) +{ + int i; + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO "cs4297a: Setting up serial parameters\n")); + + out64(M_SYNCSER_CMD_RX_RESET | M_SYNCSER_CMD_TX_RESET, SS_CSR(R_SER_CMD)); + + out64(M_SYNCSER_MSB_FIRST, SS_CSR(R_SER_MODE)); + out64(32, SS_CSR(R_SER_MINFRM_SZ)); + out64(32, SS_CSR(R_SER_MAXFRM_SZ)); + + out64(1, SS_CSR(R_SER_TX_RD_THRSH)); + out64(4, SS_CSR(R_SER_TX_WR_THRSH)); + out64(8, SS_CSR(R_SER_RX_RD_THRSH)); + + /* This looks good from experimentation */ + out64((M_SYNCSER_TXSYNC_INT | V_SYNCSER_TXSYNC_DLY(0) | M_SYNCSER_TXCLK_EXT | + M_SYNCSER_RXSYNC_INT | V_SYNCSER_RXSYNC_DLY(1) | M_SYNCSER_RXCLK_EXT | M_SYNCSER_RXSYNC_EDGE), + SS_CSR(R_SER_LINE_MODE)); + + /* This looks good from experimentation */ + out64(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE, + SS_TXTBL(0)); + out64(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_TXTBL(1)); + out64(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_TXTBL(2)); + out64(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | + M_SYNCSER_SEQ_STROBE | M_SYNCSER_SEQ_LAST, SS_TXTBL(3)); + + out64(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE, + SS_RXTBL(0)); + out64(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_RXTBL(1)); + out64(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, + SS_RXTBL(2)); + out64(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE | + M_SYNCSER_SEQ_LAST, SS_RXTBL(3)); + + for (i=4; i<16; i++) { + /* Just in case... */ + out64(M_SYNCSER_SEQ_LAST, SS_TXTBL(i)); + out64(M_SYNCSER_SEQ_LAST, SS_RXTBL(i)); + } + + return 0; +} + +static int init_serdma(serdma_t *dma) +{ + CS_DBGOUT(CS_INIT, 2, + printk(KERN_ERR "cs4297a: desc - %d sbufsize - %d dbufsize - %d\n", + DMA_DESCR, SAMPLE_BUF_SIZE, DMA_BUF_SIZE)); + + /* Descriptors */ + dma->ringsz = DMA_DESCR; + dma->descrtab = kmalloc(dma->ringsz * sizeof(serdma_descr_t), GFP_KERNEL); + if (!dma->descrtab) { + printk(KERN_ERR "cs4297a: kmalloc descrtab failed\n"); + return -1; + } + memset(dma->descrtab, 0, dma->ringsz * sizeof(serdma_descr_t)); + dma->descrtab_end = dma->descrtab + dma->ringsz; + /* XXX bloddy mess, use proper DMA API here ... */ + dma->descrtab_phys = PHYSADDR((int)dma->descrtab); + dma->descr_add = dma->descr_rem = dma->descrtab; + + /* Frame buffer area */ + dma->dma_buf = kmalloc(DMA_BUF_SIZE, GFP_KERNEL); + if (!dma->dma_buf) { + printk(KERN_ERR "cs4297a: kmalloc dma_buf failed\n"); + kfree(dma->descrtab); + return -1; + } + memset(dma->dma_buf, 0, DMA_BUF_SIZE); + dma->dma_buf_phys = PHYSADDR((int)dma->dma_buf); + + /* Samples buffer area */ + dma->sbufsz = SAMPLE_BUF_SIZE; + dma->sample_buf = kmalloc(dma->sbufsz, GFP_KERNEL); + if (!dma->sample_buf) { + printk(KERN_ERR "cs4297a: kmalloc sample_buf failed\n"); + kfree(dma->descrtab); + kfree(dma->dma_buf); + return -1; + } + dma->sb_swptr = dma->sb_hwptr = dma->sample_buf; + dma->sb_end = (u16 *)((void *)dma->sample_buf + dma->sbufsz); + dma->fragsize = dma->sbufsz >> 1; + + CS_DBGOUT(CS_INIT, 4, + printk(KERN_ERR "cs4297a: descrtab - %08x dma_buf - %x sample_buf - %x\n", + (int)dma->descrtab, (int)dma->dma_buf, + (int)dma->sample_buf)); + + return 0; +} + +static int dma_init(struct cs4297a_state *s) +{ + int i; + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO "cs4297a: Setting up DMA\n")); + + if (init_serdma(&s->dma_adc) || + init_serdma(&s->dma_dac)) + return -1; + + if (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))|| + in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) { + panic("DMA state corrupted?!"); + } + + /* Initialize now - the descr/buffer pairings will never + change... */ + for (i=0; idma_dac.descrtab[i].descr_a = M_DMA_SERRX_SOP | V_DMA_DSCRA_A_SIZE(1) | + (s->dma_dac.dma_buf_phys + i*FRAME_BYTES); + s->dma_dac.descrtab[i].descr_b = V_DMA_DSCRB_PKT_SIZE(FRAME_BYTES); + s->dma_adc.descrtab[i].descr_a = V_DMA_DSCRA_A_SIZE(1) | + (s->dma_adc.dma_buf_phys + i*FRAME_BYTES); + s->dma_adc.descrtab[i].descr_b = 0; + } + + out64((M_DMA_EOP_INT_EN | V_DMA_INT_PKTCNT(DMA_INT_CNT) | + V_DMA_RINGSZ(DMA_DESCR) | M_DMA_TDX_EN), + SS_CSR(R_SER_DMA_CONFIG0_RX)); + out64(M_DMA_L2CA, SS_CSR(R_SER_DMA_CONFIG1_RX)); + out64(s->dma_adc.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_RX)); + + out64(V_DMA_RINGSZ(DMA_DESCR), SS_CSR(R_SER_DMA_CONFIG0_TX)); + out64(M_DMA_L2CA | M_DMA_NO_DSCR_UPDT, SS_CSR(R_SER_DMA_CONFIG1_TX)); + out64(s->dma_dac.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_TX)); + + /* Prep the receive DMA descriptor ring */ + out64(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + + out64(M_SYNCSER_DMA_RX_EN | M_SYNCSER_DMA_TX_EN, SS_CSR(R_SER_DMA_ENABLE)); + + out64((M_SYNCSER_RX_SYNC_ERR | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_EOP_COUNT), + SS_CSR(R_SER_INT_MASK)); + + /* Enable the rx/tx; let the codec warm up to the sync and + start sending good frames before the receive FIFO is + enabled */ + out64(M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD)); + udelay(1000); + out64(M_SYNCSER_CMD_RX_EN | M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD)); + + /* XXXKW is this magic? (the "1" part) */ + while ((in64(SS_CSR(R_SER_STATUS)) & 0xf1) != 1) + ; + + CS_DBGOUT(CS_INIT, 4, + printk(KERN_INFO "cs4297a: status: %08x\n", + (unsigned int)(in64(SS_CSR(R_SER_STATUS)) & 0xffffffff))); + + return 0; +} + +static int serdma_reg_access(struct cs4297a_state *s, u64 data) +{ + serdma_t *d = &s->dma_dac; + u64 *data_p; + unsigned swptr; + int flags; + serdma_descr_t *descr; + + if (s->reg_request) { + printk(KERN_ERR "cs4297a: attempt to issue multiple reg_access\n"); + return -1; + } + + if (s->ena & FMODE_WRITE) { + /* Since a writer has the DSP open, we have to mux the + request in */ + s->reg_request = data; + interruptible_sleep_on(&s->dma_dac.reg_wait); + /* XXXKW how can I deal with the starvation case where + the opener isn't writing? */ + } else { + /* Be safe when changing ring pointers */ + spin_lock_irqsave(&s->lock, flags); + if (d->hwptr != d->swptr) { + printk(KERN_ERR "cs4297a: reg access found bookkeeping error (hw/sw = %d/%d\n", + d->hwptr, d->swptr); + spin_unlock_irqrestore(&s->lock, flags); + return -1; + } + swptr = d->swptr; + d->hwptr = d->swptr = (d->swptr + 1) % d->ringsz; + spin_unlock_irqrestore(&s->lock, flags); + + descr = &d->descrtab[swptr]; + data_p = &d->dma_buf[swptr * 4]; + *data_p = data; + out64(1, SS_CSR(R_SER_DMA_DSCR_COUNT_TX)); + CS_DBGOUT(CS_DESCR, 4, + printk(KERN_INFO "cs4297a: add_tx %p (%x -> %x)\n", + data_p, swptr, d->hwptr)); + } + + CS_DBGOUT(CS_FUNCTION, 6, + printk(KERN_INFO "cs4297a: serdma_reg_access()-\n")); + + return 0; +} + +//**************************************************************************** +// "cs4297a_read_ac97" -- Reads an AC97 register +//**************************************************************************** +static int cs4297a_read_ac97(struct cs4297a_state *s, u32 offset, + u32 * value) +{ + CS_DBGOUT(CS_AC97, 1, + printk(KERN_INFO "cs4297a: read reg %2x\n", offset)); + if (serdma_reg_access(s, (0xCLL << 60) | (1LL << 47) | ((u64)(offset & 0x7F) << 40))) + return -1; + + interruptible_sleep_on(&s->dma_adc.reg_wait); + *value = s->read_value; + CS_DBGOUT(CS_AC97, 2, + printk(KERN_INFO "cs4297a: rdr reg %x -> %x\n", s->read_reg, s->read_value)); + + return 0; +} + + +//**************************************************************************** +// "cs4297a_write_ac97()"-- writes an AC97 register +//**************************************************************************** +static int cs4297a_write_ac97(struct cs4297a_state *s, u32 offset, + u32 value) +{ + CS_DBGOUT(CS_AC97, 1, + printk(KERN_INFO "cs4297a: write reg %2x -> %04x\n", offset, value)); + return (serdma_reg_access(s, (0xELL << 60) | ((u64)(offset & 0x7F) << 40) | ((value & 0xffff) << 12))); +} + +static void stop_dac(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4297a: stop_dac():\n")); + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; +#if 0 + /* XXXKW what do I really want here? My theory for now is + that I just flip the "ena" bit, and the interrupt handler + will stop processing the xmit channel */ + out64((s->ena & FMODE_READ) ? M_SYNCSER_DMA_RX_EN : 0, + SS_CSR(R_SER_DMA_ENABLE)); +#endif + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void start_dac(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4297a: start_dac()+\n")); + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || + (s->dma_dac.count > 0 + && s->dma_dac.ready))) { + s->ena |= FMODE_WRITE; + /* XXXKW what do I really want here? My theory for + now is that I just flip the "ena" bit, and the + interrupt handler will start processing the xmit + channel */ + + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO + "cs4297a: start_dac(): start dma\n")); + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4297a: start_dac()-\n")); +} + + +static void stop_adc(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4297a: stop_adc()+\n")); + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + + if (s->conversion == 1) { + s->conversion = 0; + s->prop_adc.fmt = s->prop_adc.fmt_original; + } + /* Nothing to do really, I need to keep the DMA going + XXXKW when do I get here, and is there more I should do? */ + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4297a: stop_adc()-\n")); +} + + +static void start_adc(struct cs4297a_state *s) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: start_adc()+\n")); + + if (!(s->ena & FMODE_READ) && + (s->dma_adc.mapped || s->dma_adc.count <= + (signed) (s->dma_adc.sbufsz - 2 * s->dma_adc.fragsize)) + && s->dma_adc.ready) { + if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) { + // + // now only use 16 bit capture, due to truncation issue + // in the chip, noticable distortion occurs. + // allocate buffer and then convert from 16 bit to + // 8 bit for the user buffer. + // + s->prop_adc.fmt_original = s->prop_adc.fmt; + if (s->prop_adc.fmt & AFMT_S8) { + s->prop_adc.fmt &= ~AFMT_S8; + s->prop_adc.fmt |= AFMT_S16_LE; + } + if (s->prop_adc.fmt & AFMT_U8) { + s->prop_adc.fmt &= ~AFMT_U8; + s->prop_adc.fmt |= AFMT_U16_LE; + } + // + // prog_dmabuf_adc performs a stop_adc() but that is + // ok since we really haven't started the DMA yet. + // + prog_codec(s, CS_TYPE_ADC); + + prog_dmabuf_adc(s); + s->conversion = 1; + } + spin_lock_irqsave(&s->lock, flags); + s->ena |= FMODE_READ; + /* Nothing to do really, I am probably already + DMAing... XXXKW when do I get here, and is there + more I should do? */ + spin_unlock_irqrestore(&s->lock, flags); + + CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO + "cs4297a: start_adc(): start adc\n")); + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: start_adc()-\n")); + +} + + +// call with spinlock held! +static void cs4297a_update_ptr(struct cs4297a_state *s, int intflag) +{ + int good_diff, diff, diff2; + u64 *data_p, data; + u32 *s_ptr; + unsigned hwptr; + u32 status; + serdma_t *d; + serdma_descr_t *descr; + + // update ADC pointer + status = intflag ? in64(SS_CSR(R_SER_STATUS)) : 0; + + if ((s->ena & FMODE_READ) || (status & (M_SYNCSER_RX_EOP_COUNT))) { + d = &s->dma_adc; + hwptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - + d->descrtab_phys) / sizeof(serdma_descr_t)); + + if (s->ena & FMODE_READ) { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: upd_rcv sw->hw->hw %x/%x/%x (int-%d)n", + d->swptr, d->hwptr, hwptr, intflag)); + /* Number of DMA buffers available for software: */ + diff2 = diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz; + d->hwptr = hwptr; + good_diff = 0; + s_ptr = (u32 *)&(d->dma_buf[d->swptr*4]); + descr = &d->descrtab[d->swptr]; + while (diff2--) { + u64 data = *(u64 *)s_ptr; + u64 descr_a; + u16 left, right; + descr_a = descr->descr_a; + descr->descr_a &= ~M_DMA_SERRX_SOP; + if ((descr_a & M_DMA_DSCRA_A_ADDR) != PHYSADDR((int)s_ptr)) { + printk(KERN_ERR "cs4297a: RX Bad address (read)\n"); + } + if (((data & 0x9800000000000000) != 0x9800000000000000) || + (!(descr_a & M_DMA_SERRX_SOP)) || + (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) { + s->stats.rx_bad++; + printk(KERN_DEBUG "cs4297a: RX Bad attributes (read)\n"); + continue; + } + s->stats.rx_good++; + if ((data >> 61) == 7) { + s->read_value = (data >> 12) & 0xffff; + s->read_reg = (data >> 40) & 0x7f; + wake_up(&d->reg_wait); + } + if (d->count && (d->sb_hwptr == d->sb_swptr)) { + s->stats.rx_overflow++; + printk(KERN_DEBUG "cs4297a: RX overflow\n"); + continue; + } + good_diff++; + left = ((s_ptr[1] & 0xff) << 8) | ((s_ptr[2] >> 24) & 0xff); + right = (s_ptr[2] >> 4) & 0xffff; + *d->sb_hwptr++ = left; + *d->sb_hwptr++ = right; + if (d->sb_hwptr == d->sb_end) + d->sb_hwptr = d->sample_buf; + descr++; + if (descr == d->descrtab_end) { + descr = d->descrtab; + s_ptr = (u32 *)s->dma_adc.dma_buf; + } else { + s_ptr += 8; + } + } + d->total_bytes += good_diff * FRAME_SAMPLE_BYTES; + d->count += good_diff * FRAME_SAMPLE_BYTES; + if (d->count > d->sbufsz) { + printk(KERN_ERR "cs4297a: bogus receive overflow!!\n"); + } + d->swptr = (d->swptr + diff) % d->ringsz; + out64(diff, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + if (d->mapped) { + if (d->count >= (signed) d->fragsize) + wake_up(&d->wait); + } else { + if (d->count > 0) { + CS_DBGOUT(CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4297a: update count -> %d\n", d->count)); + wake_up(&d->wait); + } + } + } else { + /* Receive is going even if no one is + listening (for register accesses and to + avoid FIFO overrun) */ + diff2 = diff = (hwptr + d->ringsz - d->hwptr) % d->ringsz; + if (!diff) { + printk(KERN_ERR "cs4297a: RX full or empty?\n"); + } + + descr = &d->descrtab[d->swptr]; + data_p = &d->dma_buf[d->swptr*4]; + + /* Force this to happen at least once; I got + here because of an interrupt, so there must + be a buffer to process. */ + do { + data = *data_p; + if ((descr->descr_a & M_DMA_DSCRA_A_ADDR) != PHYSADDR((int)data_p)) { + printk(KERN_ERR "cs4297a: RX Bad address %d (%x %x)\n", d->swptr, + (int)(descr->descr_a & M_DMA_DSCRA_A_ADDR), + (int)PHYSADDR((int)data_p)); + } + if (!(data & (1LL << 63)) || + !(descr->descr_a & M_DMA_SERRX_SOP) || + (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) { + s->stats.rx_bad++; + printk(KERN_DEBUG "cs4297a: RX Bad attributes\n"); + } else { + s->stats.rx_good++; + if ((data >> 61) == 7) { + s->read_value = (data >> 12) & 0xffff; + s->read_reg = (data >> 40) & 0x7f; + wake_up(&d->reg_wait); + } + } + descr->descr_a &= ~M_DMA_SERRX_SOP; + descr++; + d->swptr++; + data_p += 4; + if (descr == d->descrtab_end) { + descr = d->descrtab; + d->swptr = 0; + data_p = d->dma_buf; + } + out64(1, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + } while (--diff); + d->hwptr = hwptr; + + CS_DBGOUT(CS_DESCR, 6, + printk(KERN_INFO "cs4297a: hw/sw %x/%x\n", d->hwptr, d->swptr)); + } + + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned)s, d->hwptr, + d->total_bytes, d->count)); + } + + /* XXXKW worry about s->reg_request -- there is a starvation + case if s->ena has FMODE_WRITE on, but the client isn't + doing writes */ + + // update DAC pointer + // + // check for end of buffer, means that we are going to wait for another interrupt + // to allow silence to fill the fifos on the part, to keep pops down to a minimum. + // + if (s->ena & FMODE_WRITE) { + serdma_t *d = &s->dma_dac; + hwptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + d->descrtab_phys) / sizeof(serdma_descr_t)); + diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz; + CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): hw/hw/sw %x/%x/%x diff %d count %d\n", + d->hwptr, hwptr, d->swptr, diff, d->count)); + d->hwptr = hwptr; + /* XXXKW stereo? conversion? Just assume 2 16-bit samples for now */ + d->total_bytes += diff * FRAME_SAMPLE_BYTES; + if (d->mapped) { + d->count += diff * FRAME_SAMPLE_BYTES; + if (d->count >= d->fragsize) { + d->wakeup = 1; + wake_up(&d->wait); + if (d->count > d->sbufsz) + d->count &= d->sbufsz - 1; + } + } else { + d->count -= diff * FRAME_SAMPLE_BYTES; + if (d->count <= 0) { + // + // fill with silence, and do not shut down the DAC. + // Continue to play silence until the _release. + // + CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): memset %d at 0x%.8x for %d size \n", + (unsigned)(s->prop_dac.fmt & + (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + (unsigned)d->dma_buf, + d->ringsz)); + memset(d->dma_buf, 0, d->ringsz * FRAME_BYTES); + if (d->count < 0) { + d->underrun = 1; + s->stats.tx_underrun++; + d->count = 0; + CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): underrun\n")); + } + } else if (d->count <= + (signed) d->fragsize + && !d->endcleared) { + /* XXXKW what is this for? */ + clear_advance(d->dma_buf, + d->sbufsz, + d->swptr, + d->fragsize, + 0); + d->endcleared = 1; + } + if ( (d->count <= (signed) d->sbufsz/2) || intflag) + { + CS_DBGOUT(CS_WAVE_WRITE, 4, + printk(KERN_INFO + "cs4297a: update count -> %d\n", d->count)); + wake_up(&d->wait); + } + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned) s, d->hwptr, + d->total_bytes, d->count)); + } +} + +static int mixer_ioctl(struct cs4297a_state *s, unsigned int cmd, + unsigned long arg) +{ + // Index to mixer_src[] is value of AC97 Input Mux Select Reg. + // Value of array member is recording source Device ID Mask. + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, + SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 + }; + + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + static const unsigned mixreg[] = { + AC97_PCMOUT_VOL, + AC97_AUX_VOL, + AC97_CD_VOL, + AC97_LINEIN_VOL + }; + unsigned char l, r, rl, rr, vidx; + unsigned char attentbl[11] = + { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; + unsigned temp1; + int i, val; + + VALIDATE_STATE(s); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4297a: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n", + (unsigned) s, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif +#if CSDEBUG_INTERFACE + + if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || + (cmd == SOUND_MIXER_CS_SETDBGMASK) || + (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_SETDBGLEVEL)) + { + switch (cmd) { + + case SOUND_MIXER_CS_GETDBGMASK: + return put_user(cs_debugmask, + (unsigned long *) arg); + + case SOUND_MIXER_CS_GETDBGLEVEL: + return put_user(cs_debuglevel, + (unsigned long *) arg); + + case SOUND_MIXER_CS_SETDBGMASK: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + cs_debugmask = val; + return 0; + + case SOUND_MIXER_CS_SETDBGLEVEL: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + cs_debuglevel = val; + return 0; + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4297a: mixer_ioctl(): ERROR unknown debug cmd\n")); + return 0; + } + } +#endif + + if (cmd == SOUND_MIXER_PRIVATE1) { + return -EINVAL; + } + if (cmd == SOUND_MIXER_PRIVATE2) { + // enable/disable/query spatializer + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != -1) { + temp1 = (val & 0x3f) >> 2; + cs4297a_write_ac97(s, AC97_3D_CONTROL, temp1); + cs4297a_read_ac97(s, AC97_GENERAL_PURPOSE, + &temp1); + cs4297a_write_ac97(s, AC97_GENERAL_PURPOSE, + temp1 | 0x2000); + } + cs4297a_read_ac97(s, AC97_3D_CONTROL, &temp1); + return put_user((temp1 << 2) | 3, (int *) arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "CS4297a", sizeof(info.id)); + strncpy(info.name, "Crystal CS4297a", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "CS4297a", sizeof(info.id)); + strncpy(info.name, "Crystal CS4297a", sizeof(info.name)); + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *) arg); + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + // If ioctl has only the SIOC_READ bit(bit 31) + // on, process the only-read commands. + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + cs4297a_read_ac97(s, AC97_RECORD_SELECT, + &temp1); + return put_user(mixer_src[temp1 & 7], (int *) arg); + + case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device + return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_VOLUME | SOUND_MASK_RECLEV, + (int *) arg); + + case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source + return put_user(SOUND_MASK_LINE | SOUND_MASK_VOLUME, + (int *) arg); + + case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo + return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_VOLUME | SOUND_MASK_RECLEV, + (int *) arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx - 1], (int *) arg); + } + } + // If ioctl doesn't have both the SIOC_READ and + // the SIOC_WRITE bit set, return invalid. + if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE)) + return -EINVAL; + + // Increment the count of volume writes. + s->mix.modcnt++; + + // Isolate the command; it must be a write. + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + if (get_user(val, (int *) arg)) + return -EFAULT; + i = hweight32(val); // i = # bits on in val. + if (i != 1) // One & only 1 bit must be on. + return 0; + for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { + if (val == mixer_src[i]) { + temp1 = (i << 8) | i; + cs4297a_write_ac97(s, + AC97_RECORD_SELECT, + temp1); + return 0; + } + } + return 0; + + case SOUND_MIXER_VOLUME: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; // Max soundcard.h vol is 100. + if (l < 6) { + rl = 63; + l = 0; + } else + rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten. + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; // Max right volume is 100, too + if (r < 6) { + rr = 63; + r = 0; + } else + rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation. + + if ((rl > 60) && (rr > 60)) // If both l & r are 'low', + temp1 = 0x8000; // turn on the mute bit. + else + temp1 = 0; + + temp1 |= (rl << 8) | rr; + + cs4297a_write_ac97(s, AC97_MASTER_VOL_STEREO, temp1); + cs4297a_write_ac97(s, AC97_PHONE_VOL, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int *) arg); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 3) { + rl = 0; + l = 0; + } else { + rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15. + l = (rl * 13 + 5) / 2; + } + + if (rl < 3) { + temp1 = 0x8000; + rl = 0; + } else + temp1 = 0; + rl = 15 - rl; // Convert volume to attenuation. + temp1 |= rl << 1; + cs4297a_write_ac97(s, AC97_PCBEEP_VOL, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[6] = l << 8; +#else + s->mix.vol[6] = val; +#endif + return put_user(s->mix.vol[6], (int *) arg); + + case SOUND_MIXER_RECLEV: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15. + rr = (r * 2 - 5) / 13; + if (rl < 3 && rr < 3) + temp1 = 0x8000; + else + temp1 = 0; + + temp1 = temp1 | (rl << 8) | rr; + cs4297a_write_ac97(s, AC97_RECORD_GAIN, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int *) arg); + + case SOUND_MIXER_MIC: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 0; + } else { + rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31. + l = (rl * 16 + 4) / 5; + } + cs4297a_read_ac97(s, AC97_MIC_VOL, &temp1); + temp1 &= 0x40; // Isolate 20db gain bit. + if (rl < 3) { + temp1 |= 0x8000; + rl = 0; + } + rl = 31 - rl; // Convert volume to attenuation. + temp1 |= rl; + cs4297a_write_ac97(s, AC97_MIC_VOL, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[5] = val << 8; +#else + s->mix.vol[5] = val; +#endif + return put_user(s->mix.vol[5], (int *) arg); + + + case SOUND_MIXER_SYNTH: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (get_user(val, (int *) arg)) + return -EFAULT; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63. + rr = (r * 2 - 11) / 3; + if (rl < 3) // If l is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + + rl = 63 - rl; // Convert vol to attenuation. +// writel(temp1 | rl, s->pBA0 + FMLVC); + if (rr < 3) // If rr is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + rr = 63 - rr; // Convert vol to attenuation. +// writel(temp1 | rr, s->pBA0 + FMRVC); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[4] = (r << 8) | l; +#else + s->mix.vol[4] = val; +#endif + return put_user(s->mix.vol[4], (int *) arg); + + + default: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: mixer_ioctl(): default\n")); + + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 31; + } else + rl = (attentbl[(l * 10) / 100]) >> 1; + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (r < 1) { + r = 0; + rr = 31; + } else + rr = (attentbl[(r * 10) / 100]) >> 1; + if ((rl > 30) && (rr > 30)) + temp1 = 0x8000; + else + temp1 = 0; + temp1 = temp1 | (rl << 8) | rr; + cs4297a_write_ac97(s, mixreg[vidx - 1], temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[vidx - 1] = val; +#endif + return put_user(s->mix.vol[vidx - 1], (int *) arg); + } +} + + +// --------------------------------------------------------------------- + +static loff_t cs4297a_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + +// --------------------------------------------------------------------- + +static int cs4297a_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct cs4297a_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n")); + + list_for_each(entry, &cs4297a_devs) + { + s = list_entry(entry, struct cs4297a_state, list); + if(s->dev_mixer == minor) + break; + } + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n")); + + return 0; +} + + +static int cs4297a_release_mixdev(struct inode *inode, struct file *file) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + + +static int cs4297a_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cs4297a_state *) file->private_data, cmd, + arg); +} + + +// ****************************************************************************************** +// Mixer file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4297a_mixer_fops = { + llseek:cs4297a_llseek, + ioctl:cs4297a_ioctl_mixdev, + open:cs4297a_open_mixdev, + release:cs4297a_release_mixdev, +}; + +// --------------------------------------------------------------------- + + +static int drain_adc(struct cs4297a_state *s, int nonblock) +{ + /* This routine serves no purpose currently - any samples + sitting in the receive queue will just be processed by the + background consumer. This would be different if DMA + actually stopped when there were no clients. */ + return 0; +} + +static int drain_dac(struct cs4297a_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned hwptr; + unsigned tmo; + int count; + + if (s->dma_dac.mapped) + return 0; + if (nonblock) + return -EBUSY; + add_wait_queue(&s->dma_dac.wait, &wait); + while ((count = in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) || + (s->dma_dac.count > 0)) { + if (!signal_pending(current)) { + set_current_state(TASK_INTERRUPTIBLE); + /* XXXKW is this calculation working? */ + tmo = ((count * FRAME_TX_US) * HZ) / 1000000; + schedule_timeout(tmo + 1); + } else { + /* XXXKW do I care if there is a signal pending? */ + } + } + spin_lock_irqsave(&s->lock, flags); + /* Reset the bookkeeping */ + hwptr = (int)(((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t)); + s->dma_dac.hwptr = s->dma_dac.swptr = hwptr; + spin_unlock_irqrestore(&s->lock, flags); + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return 0; +} + + +// --------------------------------------------------------------------- + +static ssize_t cs4297a_read(struct file *file, char *buffer, size_t count, + loff_t * ppos) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + ssize_t ret; + unsigned long flags; + int cnt, count_fr, cnt_by; + unsigned copied = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4297a: cs4297a_read()+ %d \n", count)); + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +// +// "count" is the amount of bytes to read (from app), is decremented each loop +// by the amount of bytes that have been returned to the user buffer. +// "cnt" is the running total of each read from the buffer (changes each loop) +// "buffer" points to the app's buffer +// "ret" keeps a running total of the amount of bytes that have been copied +// to the user buffer. +// "copied" is the total bytes copied into the user buffer for each loop. +// + while (count > 0) { + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + "_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n", + count, s->dma_adc.count, + s->dma_adc.swptr, s->dma_adc.hwptr)); + spin_lock_irqsave(&s->lock, flags); + + /* cnt will be the number of available samples (16-bit + stereo); it starts out as the maxmimum consequetive + samples */ + cnt = (s->dma_adc.sb_end - s->dma_adc.sb_swptr) / 2; + count_fr = s->dma_adc.count / FRAME_SAMPLE_BYTES; + + // dma_adc.count is the current total bytes that have not been read. + // if the amount of unread bytes from the current sw pointer to the + // end of the buffer is greater than the current total bytes that + // have not been read, then set the "cnt" (unread bytes) to the + // amount of unread bytes. + + if (count_fr < cnt) + cnt = count_fr; + cnt_by = cnt * FRAME_SAMPLE_BYTES; + spin_unlock_irqrestore(&s->lock, flags); + // + // if we are converting from 8/16 then we need to copy + // twice the number of 16 bit bytes then 8 bit bytes. + // + if (s->conversion) { + if (cnt_by > (count * 2)) { + cnt = (count * 2) / FRAME_SAMPLE_BYTES; + cnt_by = count * 2; + } + } else { + if (cnt_by > count) { + cnt = count / FRAME_SAMPLE_BYTES; + cnt_by = count; + } + } + // + // "cnt" NOW is the smaller of the amount that will be read, + // and the amount that is requested in this read (or partial). + // if there are no bytes in the buffer to read, then start the + // ADC and wait for the interrupt handler to wake us up. + // + if (cnt <= 0) { + + // start up the dma engine and then continue back to the top of + // the loop when wake up occurs. + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + // there are bytes in the buffer to read. + // copy from the hw buffer over to the user buffer. + // user buffer is designated by "buffer" + // virtual address to copy from is dma_buf+swptr + // the "cnt" is the number of bytes to read. + + CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO + "_read() copy_to cnt=%d count=%d ", cnt_by, count)); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .sbufsz=%d .count=%d buffer=0x%.8x ret=%d\n", + s->dma_adc.sbufsz, s->dma_adc.count, + (unsigned) buffer, ret)); + + if (copy_to_user (buffer, ((void *)s->dma_adc.sb_swptr), cnt_by)) + return ret ? ret : -EFAULT; + copied = cnt_by; + + /* Return the descriptors */ + spin_lock_irqsave(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: upd_rcv sw->hw %x/%x\n", s->dma_adc.swptr, s->dma_adc.hwptr)); + s->dma_adc.count -= cnt_by; + s->dma_adc.sb_swptr += cnt * 2; + if (s->dma_adc.sb_swptr == s->dma_adc.sb_end) + s->dma_adc.sb_swptr = s->dma_adc.sample_buf; + spin_unlock_irqrestore(&s->lock, flags); + count -= copied; + buffer += copied; + ret += copied; + start_adc(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4297a: cs4297a_read()- %d\n", ret)); + return ret; +} + + +static ssize_t cs4297a_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr, hwptr; + int cnt; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4297a: cs4297a_write()+ count=%d\n", + count)); + VALIDATE_STATE(s); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + serdma_t *d = &s->dma_dac; + int copy_cnt; + u32 *s_tmpl; + u32 *t_tmpl; + u32 left, right; + /* XXXKW check system endian here ... */ + int swap = (s->prop_dac.fmt == AFMT_S16_LE) || (s->prop_dac.fmt == AFMT_U16_LE); + + /* XXXXXX this is broken for BLOAT_FACTOR */ + spin_lock_irqsave(&s->lock, flags); + if (d->count < 0) { + d->count = 0; + d->swptr = d->hwptr; + } + if (d->underrun) { + d->underrun = 0; + hwptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + d->descrtab_phys) / sizeof(serdma_descr_t)); + d->swptr = d->hwptr = hwptr; + } + swptr = d->swptr; + cnt = d->sbufsz - (swptr * FRAME_SAMPLE_BYTES); + /* Will this write fill up the buffer? */ + if (d->count + cnt > d->sbufsz) + cnt = d->sbufsz - d->count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&d->wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(d->sample_buf, buffer, cnt)) + return ret ? ret : -EFAULT; + + copy_cnt = cnt; + s_tmpl = (u32 *)d->sample_buf; + t_tmpl = (u32 *)(d->dma_buf + (swptr * 4)); + + /* XXXKW assuming 16-bit stereo! */ + do { + t_tmpl[0] = 0x98000000; + left = s_tmpl[0] >> 16; + if (left & 0x8000) + left |= 0xf0000; + right = s_tmpl[0] & 0xffff; + if (right & 0x8000) + right |= 0xf0000; + if (swap) { + t_tmpl[1] = left & 0xff; + t_tmpl[2] = ((left & 0xff00) << 16) | ((right & 0xff) << 12) | + ((right & 0xff00) >> 4); + } else { + t_tmpl[1] = left >> 8; + t_tmpl[2] = ((left & 0xff) << 24) | (right << 4); + } + s_tmpl++; + t_tmpl += 8; + copy_cnt -= 4; + } while (copy_cnt); + + /* Mux in any pending read/write accesses */ + if (s->reg_request) { + *(u64 *)(d->dma_buf + (swptr * 4)) |= s->reg_request; + s->reg_request = 0; + wake_up(&s->dma_dac.reg_wait); + } + + CS_DBGOUT(CS_WAVE_WRITE, 4, + printk(KERN_INFO + "cs4297a: copy in %d to swptr %x\n", cnt, swptr)); + + swptr = (swptr + (cnt/FRAME_SAMPLE_BYTES)) % d->ringsz; + out64(cnt/FRAME_SAMPLE_BYTES, SS_CSR(R_SER_DMA_DSCR_COUNT_TX)); + spin_lock_irqsave(&s->lock, flags); + d->swptr = swptr; + d->count += cnt; + d->endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4297a: cs4297a_write()- %d\n", ret)); + return ret; +} + + +static unsigned int cs4297a_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4297a: cs4297a_poll()+\n")); + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4297a: cs4297a_poll() wait on FMODE_WRITE\n")); + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4297a: cs4297a_poll() wait on FMODE_READ\n")); + if(!s->dma_dac.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed) s->dma_dac.fragsize) { + if (s->dma_dac.wakeup) + mask |= POLLOUT | POLLWRNORM; + else + mask = 0; + s->dma_dac.wakeup = 0; + } + } else { + if ((signed) (s->dma_dac.sbufsz/2) >= s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } else if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4297a: cs4297a_poll()- 0x%.8x\n", + mask)); + return mask; +} + + +static int cs4297a_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* XXXKW currently no mmap support */ + return -EINVAL; + return 0; +} + + +static int cs4297a_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + CS_DBGOUT(CS_FUNCTION|CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): file=0x%.8x cmd=0x%.8x\n", + (unsigned) file, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): SOUND_VERSION=0x%.8x\n", + SOUND_VERSION)); + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SYNC\n")); + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, + 0 /*file->f_flags & O_NONBLOCK */ + ); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, + (int *) arg); + + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_RESET\n")); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = + s->dma_dac.blocks = s->dma_dac.wakeup = 0; + s->dma_dac.swptr = s->dma_dac.hwptr = + (int)(((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - + s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t)); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = + s->dma_adc.blocks = s->dma_dac.wakeup = 0; + s->dma_adc.swptr = s->dma_adc.hwptr = + (int)(((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - + s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t)); + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SPEED val=%d -> 48000\n", val)); + val = 48000; + return put_user(val, (int *) arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_STEREO val=%d\n", val)); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + s->prop_adc.channels = val ? 2 : 1; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + s->prop_dac.channels = val ? 2 : 1; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_CHANNELS val=%d\n", + val)); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + s->prop_adc.channels = 2; + else + s->prop_adc.channels = 1; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + s->prop_dac.channels = 2; + else + s->prop_dac.channels = 1; + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.channels; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.channels; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETFMTS: // Returns a mask + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_GETFMT val=0x%.8x\n", + AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8)); + return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8, (int *) arg); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SETFMT val=0x%.8x\n", + val)); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_adc.fmt = val; + s->prop_adc.fmt_original = s->prop_adc.fmt; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_dac.fmt = val; + s->prop_dac.fmt_original = s->prop_dac.fmt; + } + } else { + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.fmt_original; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.fmt_original; + } + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_SETFMT return val=0x%.8x\n", + val)); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): DSP_POST\n")); + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready + && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready + && (ret = prog_dmabuf_dac(s))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + abinfo.fragsize = s->dma_dac.fragsize; + if (s->dma_dac.mapped) + abinfo.bytes = s->dma_dac.sbufsz; + else + abinfo.bytes = + s->dma_dac.sbufsz - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO + "cs4297a: cs4297a_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n", + abinfo.fragsize,abinfo.bytes,abinfo.fragstotal, + abinfo.fragments)); + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + if (s->conversion) { + abinfo.fragsize = s->dma_adc.fragsize / 2; + abinfo.bytes = s->dma_adc.count / 2; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> (s->dma_adc.fragshift - 1); + } else { + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> s->dma_adc.fragshift; + } + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if(!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_adc.total_bytes; + if (s->dma_adc.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_adc.fragshift) - + s->dma_adc.blocks; + s->dma_adc.blocks = + cinfo.bytes >> s->dma_adc.fragshift; + } else { + if (s->conversion) { + cinfo.blocks = + s->dma_adc.count / + 2 >> (s->dma_adc.fragshift - 1); + } else + cinfo.blocks = + s->dma_adc.count >> s->dma_adc. + fragshift; + } + if (s->conversion) + cinfo.ptr = s->dma_adc.hwptr / 2; + else + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4297a_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_dac.total_bytes; + if (s->dma_dac.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_dac.fragshift) - + s->dma_dac.blocks; + s->dma_dac.blocks = + cinfo.bytes >> s->dma_dac.fragshift; + } else { + cinfo.blocks = + s->dma_dac.count >> s->dma_dac.fragshift; + } + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *) arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + if (s->conversion) + return put_user(s->dma_adc.fragsize / 2, + (int *) arg); + else + return put_user(s->dma_adc.fragsize, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + return 0; // Say OK, but do nothing. + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) + || (file->f_mode & FMODE_WRITE + && s->dma_dac.subdivision)) return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + else if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.rate, (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.rate, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.channels, (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.channels, (int *) arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return + put_user( + (s->prop_adc. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return + put_user( + (s->prop_dac. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + (int *) arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return mixer_ioctl(s, cmd, arg); +} + + +static int cs4297a_release(struct inode *inode, struct file *file) +{ + struct cs4297a_state *s = + (struct cs4297a_state *) file->private_data; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO + "cs4297a: cs4297a_release(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", + (unsigned) inode, (unsigned) file, file->f_mode)); + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_dac); + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + s->open_mode &= ~FMODE_WRITE; + up(&s->open_sem_dac); + wake_up(&s->open_wait_dac); + MOD_DEC_USE_COUNT; + } + if (file->f_mode & FMODE_READ) { + drain_adc(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_adc); + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + s->open_mode &= ~FMODE_READ; + up(&s->open_sem_adc); + wake_up(&s->open_wait_adc); + MOD_DEC_USE_COUNT; + } + return 0; +} + +static int cs4297a_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct cs4297a_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4297a: cs4297a_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", + (unsigned) inode, (unsigned) file, file->f_mode)); + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4297a: status = %08x\n", (int)in64(SS_CSR(R_SER_STATUS_DEBUG)))); + + list_for_each(entry, &cs4297a_devs) + { + s = list_entry(entry, struct cs4297a_state, list); + + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs4297a_devs) + return -ENODEV; + if (!s) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4297a: cs4297a_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + + // wait for device to become free + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO + "cs4297a: cs4297a_open(): Error - must open READ and/or WRITE\n")); + return -ENODEV; + } + if (file->f_mode & FMODE_WRITE) { + if (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)) != 0) { + printk(KERN_ERR "cs4297a: TX pipe needs to drain\n"); + while (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) + ; + } + + down(&s->open_sem_dac); + while (s->open_mode & FMODE_WRITE) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_dac); + return -EBUSY; + } + up(&s->open_sem_dac); + interruptible_sleep_on(&s->open_wait_dac); + + if (signal_pending(current)) { + printk("open - sig pending\n"); + return -ERESTARTSYS; + } + down(&s->open_sem_dac); + } + } + if (file->f_mode & FMODE_READ) { + down(&s->open_sem_adc); + while (s->open_mode & FMODE_READ) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_adc); + return -EBUSY; + } + up(&s->open_sem_adc); + interruptible_sleep_on(&s->open_wait_adc); + + if (signal_pending(current)) { + printk("open - sig pending\n"); + return -ERESTARTSYS; + } + down(&s->open_sem_adc); + } + } + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + if (file->f_mode & FMODE_READ) { + s->prop_adc.fmt = AFMT_S16_BE; + s->prop_adc.fmt_original = s->prop_adc.fmt; + s->prop_adc.channels = 2; + s->prop_adc.rate = 48000; + s->conversion = 0; + s->ena &= ~FMODE_READ; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = 0; + up(&s->open_sem_adc); + MOD_INC_USE_COUNT; + + if (prog_dmabuf_adc(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4297a: adc Program dmabufs failed.\n")); + cs4297a_release(inode, file); + return -ENOMEM; + } + } + if (file->f_mode & FMODE_WRITE) { + s->prop_dac.fmt = AFMT_S16_BE; + s->prop_dac.fmt_original = s->prop_dac.fmt; + s->prop_dac.channels = 2; + s->prop_dac.rate = 48000; + s->conversion = 0; + s->ena &= ~FMODE_WRITE; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = 0; + up(&s->open_sem_dac); + MOD_INC_USE_COUNT; + + if (prog_dmabuf_dac(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4297a: dac Program dmabufs failed.\n")); + cs4297a_release(inode, file); + return -ENOMEM; + } + } + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, + printk(KERN_INFO "cs4297a: cs4297a_open()- 0\n")); + return 0; +} + + +// ****************************************************************************************** +// Wave (audio) file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4297a_audio_fops = { + llseek:cs4297a_llseek, + read:cs4297a_read, + write:cs4297a_write, + poll:cs4297a_poll, + ioctl:cs4297a_ioctl, + mmap:cs4297a_mmap, + open:cs4297a_open, + release:cs4297a_release, +}; + +static irqreturn_t cs4297a_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs4297a_state *s = (struct cs4297a_state *) dev_id; + u32 status; + + status = in64(SS_CSR(R_SER_STATUS_DEBUG)); + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4297a: cs4297a_interrupt() HISR=0x%.8x\n", status)); + +#if 0 + /* XXXKW what check *should* be done here? */ + if (!(status & (M_SYNCSER_RX_EOP_COUNT | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_SYNC_ERR))) { + status = in64(SS_CSR(R_SER_STATUS)); + printk(KERN_ERR "cs4297a: unexpected interrupt (status %08x)\n", status); + return IRQ_HANDLED; + } +#endif + + if (status & M_SYNCSER_RX_SYNC_ERR) { + status = in64(SS_CSR(R_SER_STATUS)); + printk(KERN_ERR "cs4297a: rx sync error (status %08x)\n", status); + return IRQ_HANDLED; + } + + if (status & M_SYNCSER_RX_OVERRUN) { + int newptr, i; + s->stats.rx_ovrrn++; + printk(KERN_ERR "cs4297a: receive FIFO overrun\n"); + + /* Fix things up: get the receive descriptor pool + clean and give them back to the hardware */ + while (in64(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))) + ; + newptr = (unsigned) (((in64(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - + s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t)); + for (i=0; idma_adc.descrtab[i].descr_a &= ~M_DMA_SERRX_SOP; + } + s->dma_adc.swptr = s->dma_adc.hwptr = newptr; + s->dma_adc.count = 0; + s->dma_adc.sb_swptr = s->dma_adc.sb_hwptr = s->dma_adc.sample_buf; + out64(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); + } + + spin_lock(&s->lock); + cs4297a_update_ptr(s,CS_TRUE); + spin_unlock(&s->lock); + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4297a: cs4297a_interrupt()-\n")); + return IRQ_HANDLED; +} + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + + {SOUND_MIXER_WRITE_VOLUME, 0x4040}, + {SOUND_MIXER_WRITE_PCM, 0x4040}, + {SOUND_MIXER_WRITE_SYNTH, 0x4040}, + {SOUND_MIXER_WRITE_CD, 0x4040}, + {SOUND_MIXER_WRITE_LINE, 0x4040}, + {SOUND_MIXER_WRITE_LINE1, 0x4040}, + {SOUND_MIXER_WRITE_RECLEV, 0x0000}, + {SOUND_MIXER_WRITE_SPEAKER, 0x4040}, + {SOUND_MIXER_WRITE_MIC, 0x0000} +}; + +static int __init cs4297a_init(void) +{ + struct cs4297a_state *s; + u64 cfg; + u32 pwr, id; + mm_segment_t fs; + int rval, mdio_val; + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4297a: cs4297a_init_module()+ \n")); + + mdio_val = in64(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) & + (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT); + + /* Check syscfg for synchronous serial on port 1 */ + cfg = in64(KSEG1 + A_SCD_SYSTEM_CFG); + if (!(cfg & M_SYS_SER1_ENABLE)) { + out64(cfg | M_SYS_SER1_ENABLE, KSEG1+A_SCD_SYSTEM_CFG); + cfg = in64(KSEG1 + A_SCD_SYSTEM_CFG); + if (!(cfg & M_SYS_SER1_ENABLE)) { + printk(KERN_INFO "cs4297a: serial port 1 not configured for synchronous operation\n"); + return -1; + } + + printk(KERN_INFO "cs4297a: serial port 1 switching to synchronous operation\n"); + + /* Force the codec (on SWARM) to reset by clearing + GENO, preserving MDIO (no effect on CSWARM) */ + out64(mdio_val, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO)); + udelay(10); + } + + /* Now set GENO */ + out64(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO)); + /* Give the codec some time to finish resetting (start the bit clock) */ + udelay(100); + + if (!(s = kmalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4297a: probe() no memory for state struct.\n")); + return -1; + } + memset(s, 0, sizeof(struct cs4297a_state)); + s->magic = CS4297a_MAGIC; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->dma_adc.reg_wait); + init_waitqueue_head(&s->dma_dac.reg_wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->open_wait_adc); + init_waitqueue_head(&s->open_wait_dac); + init_MUTEX(&s->open_sem_adc); + init_MUTEX(&s->open_sem_dac); + spin_lock_init(&s->lock); + + s->irq = K_INT_SER_1; + + if (request_irq + (s->irq, cs4297a_interrupt, 0, "Crystal CS4297a", s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, + printk(KERN_ERR "cs4297a: irq %u in use\n", s->irq)); + goto err_irq; + } + if ((s->dev_audio = register_sound_dsp(&cs4297a_audio_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4297a: probe() register_sound_dsp() failed.\n")); + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&cs4297a_mixer_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4297a: probe() register_sound_mixer() failed.\n")); + goto err_dev2; + } + + if (ser_init(s) || dma_init(s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4297a: ser_init failed.\n")); + goto err_dev3; + } + + do { + udelay(4000); + rval = cs4297a_read_ac97(s, AC97_POWER_CONTROL, &pwr); + } while (!rval && (pwr != 0xf)); + + if (!rval) { + fs = get_fs(); + set_fs(KERNEL_DS); +#if 0 + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val); + for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val); + } +// cs4297a_write_ac97(s, 0x18, 0x0808); +#else + // cs4297a_write_ac97(s, 0x5e, 0x180); + cs4297a_write_ac97(s, 0x02, 0x0808); + cs4297a_write_ac97(s, 0x18, 0x0808); +#endif + set_fs(fs); + + list_add(&s->list, &cs4297a_devs); + + cs4297a_read_ac97(s, AC97_VENDOR_ID1, &id); + + printk(KERN_INFO "cs4297a: initialized (vendor id = %x)\n", id); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: cs4297a_init_module()-\n")); + + return 0; + } + + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_irq(s->irq, s); + err_irq: + kfree(s); + + printk(KERN_INFO "cs4297a: initialization failed\n"); + + return -1; +} + +static void __exit cs4297a_cleanup(void) +{ + /* + XXXKW + disable_irq, free_irq + drain DMA queue + disable DMA + disable TX/RX + free memory + */ + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4297a: cleanup_cs4297a() finished\n")); +} + +// --------------------------------------------------------------------- + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Kip Walker, kwalker@broadcom.com"); +MODULE_DESCRIPTION("Cirrus Logic CS4297a Driver for Broadcom SWARM board"); + +// --------------------------------------------------------------------- + +module_init(cs4297a_init); +module_exit(cs4297a_cleanup); diff -urN linux-2.5.75-bk1/sound/oss/trident.c linux-2.5.75-bk2/sound/oss/trident.c --- linux-2.5.75-bk1/sound/oss/trident.c 2003-07-10 13:12:56.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/trident.c 2003-07-12 04:37:48.000000000 -0700 @@ -37,6 +37,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * History + * v0.14.10h + * Sept 10 2002 Pascal Schmidt + * added support for ALi 5451 joystick port + * v0.14.10g + * Sept 05 2002 Alan Cox + * adapt to new pci joystick attachment interface * v0.14.10f * July 24 2002 Muli Ben-Yehuda * patch from Eric Lemar (via Ian Soboroff): in suspend and resume, @@ -212,7 +218,7 @@ #include "trident.h" -#define DRIVER_VERSION "0.14.10f" +#define DRIVER_VERSION "0.14.10h-2.5" /* magic numbers to protect our data structures */ #define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ @@ -538,8 +544,8 @@ outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); - pr_debug("trident: Enable Loop Interrupts, globctl = 0x%08X\n", - inl(TRID_REG(card, T4D_LFO_GC_CIR))); + TRDBG("trident: Enable Loop Interrupts, globctl = 0x%08X\n", + inl(TRID_REG(card, T4D_LFO_GC_CIR))); return (TRUE); } @@ -552,8 +558,8 @@ global_control &= ~(ENDLP_IE | MIDLP_IE); outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); - pr_debug("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", - global_control); + TRDBG("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", + global_control); return (TRUE); } @@ -570,8 +576,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); + TRDBG("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); #endif /* DEBUG */ } @@ -590,8 +596,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); + TRDBG("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); #endif /* DEBUG */ } @@ -609,8 +615,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_START_B? "START_B":"START_A",reg,addr); + TRDBG("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_START_B? "START_B":"START_A",reg,addr); #endif /* DEBUG */ } @@ -628,8 +634,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, addr)); - pr_debug("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr); + TRDBG("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr); #endif /* DEBUG */ } @@ -647,8 +653,8 @@ #ifdef DEBUG if (reg & mask) - pr_debug("trident: channel %d has interrupt, %s = 0x%08x\n", - channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg); + TRDBG("trident: channel %d has interrupt, %s = 0x%08x\n", + channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg); #endif /* DEBUG */ return (reg & mask) ? TRUE : FALSE; } @@ -665,8 +671,8 @@ #ifdef DEBUG reg = inl(TRID_REG(card, T4D_AINT_B)); - pr_debug("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", - channel, reg); + TRDBG("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", + channel, reg); #endif /* DEBUG */ } @@ -914,7 +920,7 @@ trident_write_voice_regs(state); - pr_debug("trident: called trident_set_dac_rate : rate = %d\n", rate); + TRDBG("trident: called trident_set_dac_rate : rate = %d\n", rate); return rate; } @@ -934,7 +940,7 @@ trident_write_voice_regs(state); - pr_debug("trident: called trident_set_adc_rate : rate = %d\n", rate); + TRDBG("trident: called trident_set_adc_rate : rate = %d\n", rate); return rate; } @@ -978,9 +984,9 @@ /* stereo */ channel->control |= CHANNEL_STEREO; - pr_debug("trident: trident_play_setup, LBA = 0x%08x, " - "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", - channel->lba, channel->delta, channel->eso, channel->control); + TRDBG("trident: trident_play_setup, LBA = 0x%08x, " + "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); trident_write_voice_regs(state); } @@ -1064,9 +1070,9 @@ /* stereo */ channel->control |= CHANNEL_STEREO; - pr_debug("trident: trident_rec_setup, LBA = 0x%08x, " - "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", - channel->lba, channel->delta, channel->eso, channel->control); + TRDBG("trident: trident_rec_setup, LBA = 0x%08x, " + "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); trident_write_voice_regs(state); } @@ -1101,8 +1107,8 @@ } - pr_debug("trident: trident_get_dma_addr: chip reported channel: %d, " - "cso = 0x%04x\n", dmabuf->channel->num, cso); + TRDBG("trident: trident_get_dma_addr: chip reported channel: %d, " + "cso = 0x%04x\n", dmabuf->channel->num, cso); /* ESO and CSO are in units of Samples, convert to byte offset */ cso <<= sample_shift[dmabuf->fmt]; @@ -1211,8 +1217,8 @@ &dmabuf->dma_handle))) return -ENOMEM; - pr_debug("trident: allocated %ld (order = %d) bytes at %p\n", - PAGE_SIZE << order, order, rawbuf); + TRDBG("trident: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); dmabuf->ready = dmabuf->mapped = 0; dmabuf->rawbuf = rawbuf; @@ -1349,11 +1355,10 @@ /* set the ready flag for the dma buffer */ dmabuf->ready = 1; - pr_debug("trident: prog_dmabuf(%d), sample rate = %d, " - "format = %d, numfrag = %d, fragsize = %d " - "dmasize = %d\n", dmabuf->channel->num, dmabuf->rate, - dmabuf->fmt, dmabuf->numfrag, dmabuf->fragsize, - dmabuf->dmasize); + TRDBG("trident: prog_dmabuf(%d), sample rate = %d, format = %d, numfrag = %d, " + "fragsize = %d dmasize = %d\n", + dmabuf->channel->num, dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); } unlock_set_fmt(state); return 0; @@ -1705,7 +1710,7 @@ /* FIXED: read interrupt status only once */ irq_status=inl(TRID_REG(card, T4D_AINT_A) ); - pr_debug("cyber_address_interrupt: irq_status 0x%X\n",irq_status); + TRDBG("cyber_address_interrupt: irq_status 0x%X\n",irq_status); for (i = 0; i < NR_HW_CH; i++) { channel = 31 - i; @@ -1713,7 +1718,7 @@ /* clear bit by writing a 1, zeroes are ignored */ outl( (1 << channel), TRID_REG(card, T4D_AINT_A)); - pr_debug("cyber_interrupt: channel %d\n", channel); + TRDBG("cyber_interrupt: channel %d\n", channel); if ((state = card->states[i]) != NULL) { trident_update_ptr(state); @@ -1736,7 +1741,7 @@ spin_lock(&card->lock); event = inl(TRID_REG(card, T4D_MISCINT)); - pr_debug("trident: trident_interrupt called, MISCINT = 0x%08x\n", event); + TRDBG("trident: trident_interrupt called, MISCINT = 0x%08x\n", event); if (event & ADDRESS_IRQ) { card->address_interrupt(card); @@ -1775,7 +1780,7 @@ unsigned swptr; int cnt; - pr_debug("trident: trident_read called, count = %d\n", count); + TRDBG("trident: trident_read called, count = %d\n", count); VALIDATE_STATE(state); if (ppos != &file->f_pos) @@ -1829,7 +1834,7 @@ 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)) { - pr_debug(KERN_ERR "trident: recording schedule timeout, " + TRDBG(KERN_ERR "trident: recording schedule timeout, " "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, dmabuf->hwptr, dmabuf->swptr); @@ -1887,7 +1892,7 @@ unsigned int state_cnt; unsigned int copy_count; - pr_debug("trident: trident_write called, count = %d\n", count); + TRDBG("trident: trident_write called, count = %d\n", count); VALIDATE_STATE(state); if (ppos != &file->f_pos) @@ -1956,7 +1961,7 @@ which results in a (potential) buffer underrun. And worse, there is NOTHING we can do to prevent it. */ if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { - pr_debug(KERN_ERR "trident: playback schedule timeout, " + TRDBG(KERN_ERR "trident: playback schedule timeout, " "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, dmabuf->hwptr, dmabuf->swptr); @@ -2139,7 +2144,7 @@ VALIDATE_STATE(state); mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) || ((file->f_mode & FMODE_READ) && dmabuf->mapped); - pr_debug("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", + TRDBG("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", _IOC_NR(cmd), arg ? *(int *)arg : 0); switch (cmd) @@ -2704,7 +2709,7 @@ state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&card->open_sem); - pr_debug("trident: open virtual channel %d, hard channel %d\n", + TRDBG("trident: open virtual channel %d, hard channel %d\n", state->virt, dmabuf->channel->num); return 0; @@ -2726,7 +2731,7 @@ drain_dac(state, file->f_flags & O_NONBLOCK); } - pr_debug("trident: closing virtual channel %d, hard channel %d\n", + TRDBG("trident: closing virtual channel %d, hard channel %d\n", state->virt, dmabuf->channel->num); /* stop DMA state machine and free DMA buffers/channels */ @@ -2925,11 +2930,10 @@ } if(!block) { - pr_debug("accesscodecsemaphore: try unlock\n"); + TRDBG("accesscodecsemaphore: try unlock\n"); block = 1; goto unlock; } - printk(KERN_ERR "accesscodecsemaphore: fail\n"); return 0; } @@ -2954,8 +2958,6 @@ break; udelay(50); } - - printk(KERN_NOTICE "waitforstimertick :BIT_CLK is dead\n"); return 0; } @@ -2967,6 +2969,7 @@ unsigned long aud_reg; u32 data; u16 wcontrol; + unsigned long flags; if(!card) BUG(); @@ -2979,6 +2982,8 @@ if (secondary) mask |= ALI_AC97_SECONDARY; + spin_lock_irqsave(&card->lock, flags); + if (!acquirecodecaccess(card)) printk(KERN_ERR "access codec fail\n"); @@ -2990,7 +2995,7 @@ data = (mask | (reg & AC97_REG_ADDR)); if(!waitforstimertick(card)) { - printk(KERN_ERR "BIT_CLOCK is dead\n"); + printk(KERN_ERR "ali_ac97_read: BIT_CLOCK is dead\n"); goto releasecodec; } @@ -3004,7 +3009,7 @@ if(ncount <=0) break; if(ncount--==1) { - pr_debug("ali_ac97_read :try clear busy flag\n"); + TRDBG("ali_ac97_read :try clear busy flag\n"); aud_reg = inl(TRID_REG(card, ALI_AC97_WRITE)); outl((aud_reg & 0xffff7fff), TRID_REG(card, ALI_AC97_WRITE)); } @@ -3017,7 +3022,8 @@ releasecodec: releasecodecaccess(card); - printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "ali_ac97_read: AC97 CODEC read timed out.\n"); return 0; } @@ -3029,6 +3035,7 @@ unsigned int ncount; u32 data; u16 wcontrol; + unsigned long flags; data = ((u32) val) << 16; @@ -3042,8 +3049,9 @@ if (card->revision == ALI_5451_V02) mask |= ALI_AC97_WRITE_MIXER_REGISTER; + spin_lock_irqsave(&card->lock, flags); if (!acquirecodecaccess(card)) - printk(KERN_ERR "access codec fail\n"); + printk(KERN_ERR "ali_ac97_write: access codec fail\n"); wcontrol = inw(TRID_REG(card, ALI_AC97_WRITE)); wcontrol &= 0xff00; @@ -3063,7 +3071,7 @@ if(ncount <= 0) break; if(ncount-- == 1) { - pr_debug("ali_ac97_set :try clear busy flag!!\n"); + TRDBG("ali_ac97_set :try clear busy flag!!\n"); outw(wcontrol & 0x7fff, TRID_REG(card, ALI_AC97_WRITE)); } udelay(10); @@ -3071,6 +3079,7 @@ releasecodec: releasecodecaccess(card); + spin_unlock_irqrestore(&card->lock, flags); return; } @@ -3099,6 +3108,9 @@ if(!card->mixer_regs_ready) return ali_ac97_get(card, codec->id, reg); + /* + * FIXME: need to stop this caching some registers + */ if(codec->id) id = 1; else @@ -3366,15 +3378,17 @@ pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x80; - pci_write_config_byte(pci_dev, 0x59, ~temp); + pci_read_config_byte(pci_dev, 0x59, &temp); + temp &= ~0x80; + pci_write_config_byte(pci_dev, 0x59, temp); pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x20; - pci_write_config_byte(pci_dev, 0xB8, ~temp); + pci_read_config_byte(pci_dev, 0xB8, &temp); + temp &= ~0x20; + pci_write_config_byte(pci_dev, 0xB8, temp); return 0; } @@ -3388,13 +3402,15 @@ pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x80; + pci_read_config_byte(pci_dev, 0x59, &temp); + temp |= 0x80; pci_write_config_byte(pci_dev, 0x59, temp); pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev); if (pci_dev == NULL) return -1; - temp = 0x20; + pci_read_config_byte(pci_dev, (int)0xB8, &temp); + temp |= 0x20; pci_write_config_byte(pci_dev, (int)0xB8,(u8) temp); if (chan_nums == 6) { dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000; @@ -3934,10 +3950,7 @@ return 0; udelay(5000); } - /* This is non fatal if you have a non PM capable codec.. */ - printk(KERN_ERR "ALi 5451 did not come out of reset " - "- continuing anyway.\n"); return 0; } @@ -4010,9 +4023,8 @@ } for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -4033,7 +4045,7 @@ if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { printk(KERN_ERR "trident: couldn't register mixer!\n"); - kfree(codec); + ac97_release_codec(codec); break; } @@ -4258,7 +4270,7 @@ for (i = 0; i < NR_AC97; i++) { if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } } goto out_unregister_sound_dsp; @@ -4358,7 +4370,7 @@ for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree (card->ac97_codec[i]); + ac97_release_codec(card->ac97_codec[i]); } unregister_sound_dsp(card->dev_audio); diff -urN linux-2.5.75-bk1/sound/oss/trident.h linux-2.5.75-bk2/sound/oss/trident.h --- linux-2.5.75-bk1/sound/oss/trident.h 2003-07-10 13:04:00.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/trident.h 2003-07-12 04:37:48.000000000 -0700 @@ -360,4 +360,16 @@ return r; } +#ifdef DEBUG + +#define TRDBG(msg, args...) do { \ + printk(KERN_DEBUG msg , ##args ); \ +} while (0) + +#else /* !defined(DEBUG) */ + +#define TRDBG(msg, args...) do { } while (0) + +#endif /* DEBUG */ + #endif /* __TRID4DWAVE_H */ diff -urN linux-2.5.75-bk1/sound/oss/via82cxxx_audio.c linux-2.5.75-bk2/sound/oss/via82cxxx_audio.c --- linux-2.5.75-bk1/sound/oss/via82cxxx_audio.c 2003-07-10 13:06:55.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/via82cxxx_audio.c 2003-07-12 04:37:48.000000000 -0700 @@ -2,17 +2,20 @@ * Support for VIA 82Cxxx Audio Codecs * Copyright 1999,2000 Jeff Garzik * + * Updated to support the VIA 8233/8235 audio subsystem + * Alan Cox (C) Copyright 2002, 2003 Red Hat Inc + * * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2. * See the "COPYING" file distributed with this software for more info. + * NO WARRANTY * * For a list of known bugs (errata) and documentation, * see via-audio.pdf in linux/Documentation/DocBook. * If this documentation does not exist, run "make pdfdocs". - * */ -#define VIA_VERSION "1.9.1" +#define VIA_VERSION "1.9.1-ac3-2.5" #include @@ -81,10 +84,13 @@ #define VIA_DEFAULT_FRAG_TIME 20 #define VIA_DEFAULT_BUFFER_TIME 500 +/* the hardware has a 256 fragment limit */ +#define VIA_MIN_FRAG_NUMBER 2 +#define VIA_MAX_FRAG_NUMBER 128 + #define VIA_MAX_FRAG_SIZE PAGE_SIZE -#define VIA_MIN_FRAG_SIZE 64 +#define VIA_MIN_FRAG_SIZE (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE / VIA_MAX_FRAG_NUMBER) -#define VIA_MIN_FRAG_NUMBER 2 /* 82C686 function 5 (audio codec) PCI configuration registers */ #define VIA_ACLINK_STATUS 0x40 @@ -116,7 +122,10 @@ #define VIA_PCM_STATUS 0x00 #define VIA_PCM_CONTROL 0x01 #define VIA_PCM_TYPE 0x02 +#define VIA_PCM_LEFTVOL 0x02 +#define VIA_PCM_RIGHTVOL 0x03 #define VIA_PCM_TABLE_ADDR 0x04 +#define VIA_PCM_STOPRATE 0x08 /* 8233+ */ #define VIA_PCM_BLOCK_COUNT 0x0C /* XXX unused DMA channel for FM PCM data */ @@ -125,6 +134,12 @@ #define VIA_BASE0_FM_OUT_CHAN_CTRL 0x21 #define VIA_BASE0_FM_OUT_CHAN_TYPE 0x22 +/* Six channel audio output on 8233 */ +#define VIA_BASE0_MULTI_OUT_CHAN 0x40 +#define VIA_BASE0_MULTI_OUT_CHAN_STATUS 0x40 +#define VIA_BASE0_MULTI_OUT_CHAN_CTRL 0x41 +#define VIA_BASE0_MULTI_OUT_CHAN_TYPE 0x42 + #define VIA_BASE0_AC97_CTRL 0x80 #define VIA_BASE0_SGD_STATUS_SHADOW 0x84 #define VIA_BASE0_GPI_INT_ENABLE 0x8C @@ -133,6 +148,12 @@ #define VIA_INTR_FM ((1<<2) | (1<<6) | (1<<10)) #define VIA_INTR_MASK (VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM) +/* Newer VIA we need to monitor the low 3 bits of each channel. This + mask covers the channels we don't yet use as well + */ + +#define VIA_NEW_INTR_MASK 0x77077777UL + /* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */ #define VIA_IRQ_ON_FLAG (1<<0) /* int on each flagged scatter block */ #define VIA_IRQ_ON_EOL (1<<1) /* int at end of scatter list */ @@ -250,11 +271,15 @@ unsigned is_record : 1; unsigned is_mapped : 1; unsigned is_enabled : 1; + unsigned is_multi: 1; /* 8233 6 channel */ u8 pcm_fmt; /* VIA_PCM_FMT_xxx */ + u8 channels; /* Channel count */ unsigned rate; /* sample rate */ unsigned int frag_size; unsigned int frag_number; + + unsigned char intmask; volatile struct via_sgd_table *sgtable; dma_addr_t sgt_handle; @@ -273,19 +298,31 @@ struct pci_dev *pdev; long baseaddr; - struct ac97_codec ac97; + struct ac97_codec *ac97; + spinlock_t ac97_lock; spinlock_t lock; int card_num; /* unique card number, from 0 */ int dev_dsp; /* /dev/dsp index from register_sound_dsp() */ unsigned rev_h : 1; + unsigned legacy: 1; /* Has legacy ports */ + unsigned intmask: 1; /* Needs int bits */ + unsigned sixchannel: 1; /* 8233/35 with 6 channel support */ + unsigned volume: 1; int locked_rate : 1; + + int mixer_vol; /* 8233/35 volume - not yet implemented */ struct semaphore syscall_sem; struct semaphore open_sem; + /* The 8233/8235 have 4 DX audio channels, two record and + one six channel out. We bind ch_in to DX 1, ch_out to multichannel + and ch_fm to DX 2. DX 3 and REC0/REC1 are unused at the + moment */ + struct via_channel ch_in; struct via_channel ch_out; struct via_channel ch_fm; @@ -352,17 +389,19 @@ static struct pci_device_id via_pci_tbl[] __initdata = { { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, - PCI_ANY_ID, PCI_ANY_ID, }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, { 0, } }; MODULE_DEVICE_TABLE(pci,via_pci_tbl); static struct pci_driver via_driver = { - .name = VIA_MODULE_NAME, - .id_table = via_pci_tbl, - .probe = via_init_one, - .remove = __devexit_p(via_remove_one), + name: VIA_MODULE_NAME, + id_table: via_pci_tbl, + probe: via_init_one, + remove: __devexit_p(via_remove_one), }; @@ -426,7 +465,13 @@ static inline void sg_begin (struct via_channel *chan) { - outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL); + DPRINTK("Start with intmask %d\n", chan->intmask); + DPRINTK("About to start from %d to %d\n", + inl(chan->iobase + VIA_PCM_BLOCK_COUNT), + inb(chan->iobase + VIA_PCM_STOPRATE + 3)); + outb (VIA_SGD_START|chan->intmask, chan->iobase + VIA_PCM_CONTROL); + DPRINTK("Status is now %02X\n", inb(chan->iobase + VIA_PCM_STATUS)); + DPRINTK("Control is now %02X\n", inb(chan->iobase + VIA_PCM_CONTROL)); } @@ -442,6 +487,10 @@ return 0; } +static int via_sg_offset(struct via_channel *chan) +{ + return inl (chan->iobase + VIA_PCM_BLOCK_COUNT) & 0x00FFFFFF; +} /**************************************************************** * @@ -505,6 +554,8 @@ via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + if(card->sixchannel) + via_chan_stop (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN); /* * clear any existing stops / flags (sanity check mainly) @@ -512,6 +563,8 @@ via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + if(card->sixchannel) + via_chan_status_clear (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN); /* * clear any enabled interrupt bits @@ -531,6 +584,14 @@ if (tmp != new_tmp) outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); + if(card->sixchannel) + { + tmp = inb (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE); + } + udelay(10); /* @@ -561,6 +622,9 @@ { struct via_info *card = ac97->private_data; int rate_reg; + u32 dacp; + u32 mast_vol, phone_vol, mono_vol, pcm_vol; + u32 mute_vol = 0x8000; /* The mute volume? -- Seems to work! */ DPRINTK ("ENTER, rate = %d\n", rate); @@ -577,16 +641,32 @@ rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE : AC97_PCM_FRONT_DAC_RATE; - via_ac97_write_reg (ac97, AC97_POWER_CONTROL, - (via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) | - 0x0200); + /* Save current state */ + dacp=via_ac97_read_reg(ac97, AC97_POWER_CONTROL); + mast_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_STEREO); + mono_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_MONO); + phone_vol = via_ac97_read_reg(ac97, AC97_HEADPHONE_VOL); + pcm_vol = via_ac97_read_reg(ac97, AC97_PCMOUT_VOL); + /* Mute - largely reduces popping */ + via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mute_vol); + via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mute_vol); + via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, mute_vol); + via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, mute_vol); + /* Power down the DAC */ + via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp|0x0200); + /* Set new rate */ via_ac97_write_reg (ac97, rate_reg, rate); - via_ac97_write_reg (ac97, AC97_POWER_CONTROL, - via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200); - - udelay (10); + /* Power DAC back up */ + via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp); + udelay (200); /* reduces popping */ + + /* Restore volumes */ + via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mast_vol); + via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mono_vol); + via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, phone_vol); + via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, pcm_vol); /* the hardware might return a value different than what we * passed to it, so read the rate value back from hardware @@ -626,9 +706,19 @@ { memset (chan, 0, sizeof (*chan)); + if(card->intmask) + chan->intmask = 0x23; /* Turn on the IRQ bits */ + if (chan == &card->ch_out) { chan->name = "PCM-OUT"; - chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN; + if(card->sixchannel) + { + chan->iobase = card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN; + chan->is_multi = 1; + DPRINTK("Using multichannel for pcm out\n"); + } + else + chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN; } else if (chan == &card->ch_in) { chan->name = "PCM-IN"; chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN; @@ -659,9 +749,8 @@ * 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. + * Currently the preparations consist of + * setting the PCM channel to a known state. */ @@ -707,6 +796,11 @@ DPRINTK ("ENTER\n"); + + chan->intmask = 0; + if(card->intmask) + chan->intmask = 0x23; /* Turn on the IRQ bits */ + if (chan->sgtable != NULL) { DPRINTK ("EXIT\n"); return 0; @@ -777,6 +871,16 @@ outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); udelay (20); via_ac97_wait_idle (card); + /* load no rate adaption, stereo 16bit, set up ring slots */ + if(card->sixchannel) + { + if(!chan->is_multi) + { + outl (0xFFFFF | (0x3 << 20) | (chan->frag_number << 24), chan->iobase + VIA_PCM_STOPRATE); + udelay (20); + via_ac97_wait_idle (card); + } + } DPRINTK ("inl (0x%lX) = %x\n", chan->iobase + VIA_PCM_TABLE_ADDR, @@ -880,8 +984,11 @@ assert (chan != NULL); if (reset) + { /* reset to 8-bit mono mode */ chan->pcm_fmt = 0; + chan->channels = 1; + } /* enable interrupts on FLAG and EOL */ chan->pcm_fmt |= VIA_CHAN_TYPE_MASK; @@ -892,8 +999,83 @@ /* set interrupt select bits where applicable (PCM in & out channels) */ if (!chan->is_record) chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; + + DPRINTK("SET FMT - %02x %02x\n", chan->intmask , chan->is_multi); + + if(chan->intmask) + { + u32 m; + + /* + * Channel 0x4 is up to 6 x 16bit and has to be + * programmed differently + */ + + if(chan->is_multi) + { + u8 c = 0; + + /* + * Load the type bit for num channels + * and 8/16bit + */ + + if(chan->pcm_fmt & VIA_PCM_FMT_16BIT) + c = 1 << 7; + if(chan->pcm_fmt & VIA_PCM_FMT_STEREO) + c |= (2<<4); + else + c |= (1<<4); + + outb(c, chan->iobase + VIA_PCM_TYPE); + + /* + * Set the channel steering + * Mono + * Channel 0 to slot 3 + * Channel 0 to slot 4 + * Stereo + * Channel 0 to slot 3 + * Channel 1 to slot 4 + */ + + switch(chan->channels) + { + case 1: + outl(0xFF000000 | (1<<0) | (1<<4) , chan->iobase + VIA_PCM_STOPRATE); + break; + case 2: + outl(0xFF000000 | (1<<0) | (2<<4) , chan->iobase + VIA_PCM_STOPRATE); + break; + case 4: + outl(0xFF000000 | (1<<0) | (2<<4) | (3<<8) | (4<<12), chan->iobase + VIA_PCM_STOPRATE); + break; + case 6: + outl(0xFF000000 | (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20), chan->iobase + VIA_PCM_STOPRATE); + break; + } + } + else + { + /* + * New style, turn off channel volume + * control, set bits in the right register + */ + outb(0x0, chan->iobase + VIA_PCM_LEFTVOL); + outb(0x0, chan->iobase + VIA_PCM_RIGHTVOL); + + m = inl(chan->iobase + VIA_PCM_STOPRATE); + m &= ~(3<<20); + if(chan->pcm_fmt & VIA_PCM_FMT_STEREO) + m |= (1 << 20); + if(chan->pcm_fmt & VIA_PCM_FMT_16BIT) + m |= (1 << 21); + outl(m, chan->iobase + VIA_PCM_STOPRATE); + } + } + else + outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); - outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n", chan->pcm_fmt, @@ -948,7 +1130,7 @@ via_chan_clear (card, chan); - val = via_set_rate (&card->ac97, chan, val); + val = via_set_rate (card->ac97, chan, val); DPRINTK ("EXIT, returning %d\n", val); return val; @@ -1034,15 +1216,25 @@ /* mono */ case 1: chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; + chan->channels = 1; via_chan_pcm_fmt (chan, 0); break; /* stereo */ case 2: chan->pcm_fmt |= VIA_PCM_FMT_STEREO; + chan->channels = 2; via_chan_pcm_fmt (chan, 0); break; + case 4: + case 6: + if(chan->is_multi) + { + chan->pcm_fmt |= VIA_PCM_FMT_STEREO; + chan->channels = val; + break; + } /* unknown */ default: printk (KERN_WARNING PFX "unknown number of channels\n"); @@ -1076,9 +1268,8 @@ 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; + chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate * chan->channels + * ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1; shift = 0; while (chan->frag_size) { @@ -1112,6 +1303,8 @@ if (chan->frag_number < VIA_MIN_FRAG_NUMBER) chan->frag_number = VIA_MIN_FRAG_NUMBER; + if (chan->frag_number > VIA_MAX_FRAG_NUMBER) + chan->frag_number = VIA_MAX_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; @@ -1156,7 +1349,7 @@ /** * via_chan_flush_frag - Flush partially-full playback buffer to hardware - * @chan: Channel whose DMA table will be displayed + * @chan: Channel whose DMA table will be flushed * * Flushes partially-full playback buffer to hardware. */ @@ -1194,6 +1387,7 @@ { assert (chan->is_active == sg_active(chan->iobase)); + DPRINTK ("MAYBE START %s\n", chan->name); if (!chan->is_active && chan->is_enabled) { chan->is_active = 1; sg_begin (chan); @@ -1267,6 +1461,8 @@ assert (codec->private_data != NULL); card = codec->private_data; + + spin_lock(&card->ac97_lock); /* Every time we write to register 80 we cause a transaction. The only safe way to clear the valid bit is to write it at @@ -1297,6 +1493,7 @@ if (((data & 0x007F0000) >> 16) == reg) { DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n", data, data & 0x0000FFFF); + spin_unlock(&card->ac97_lock); return data & 0x0000FFFF; } @@ -1304,6 +1501,7 @@ reg, ((data & 0x007F0000) >> 16)); err_out: + spin_unlock(&card->ac97_lock); DPRINTK ("EXIT, returning 0\n"); return 0; } @@ -1335,6 +1533,8 @@ card = codec->private_data; + spin_lock(&card->ac97_lock); + data = (reg << 16) + value; outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); udelay (10); @@ -1349,6 +1549,7 @@ printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value); out: + spin_unlock(&card->ac97_lock); DPRINTK ("EXIT\n"); } @@ -1368,7 +1569,7 @@ assert (pci_get_drvdata (pdev) != NULL); card = pci_get_drvdata (pdev); - if (card->ac97.dev_mixer == minor) + if (card->ac97->dev_mixer == minor) goto match; } } @@ -1377,7 +1578,7 @@ return -ENODEV; match: - file->private_data = &card->ac97; + file->private_data = card->ac97; DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1399,7 +1600,30 @@ rc = via_syscall_down (card, nonblock); if (rc) goto out; - + +#if 0 + /* + * Intercept volume control on 8233 and 8235 + */ + if(card->volume) + { + switch(cmd) + { + case SOUND_MIXER_READ_VOLUME: + return card->mixer_vol; + case SOUND_MIXER_WRITE_VOLUME: + { + int v; + if(get_user(v, (int *)arg)) + { + rc = -EFAULT; + goto out; + } + card->mixer_vol = v; + } + } + } +#endif rc = codec->mixer_ioctl(codec, cmd, arg); up (&card->syscall_sem); @@ -1493,25 +1717,28 @@ udelay (100); } + if(card->legacy) + { #if 0 /* this breaks on K7M */ - /* disable legacy stuff */ - pci_write_config_byte (pdev, 0x42, 0x00); - udelay(10); + /* disable legacy stuff */ + pci_write_config_byte (pdev, 0x42, 0x00); + udelay(10); #endif - /* route FM trap to IRQ, disable FM trap */ - pci_write_config_byte (pdev, 0x48, 0x05); - udelay(10); - + /* route FM trap to IRQ, disable FM trap */ + pci_write_config_byte (pdev, 0x48, 0x05); + udelay(10); + } + /* disable all codec GPI interrupts */ outl (0, pci_resource_start (pdev, 0) + 0x8C); /* WARNING: this line is magic. Remove this * and things break. */ /* enable variable rate */ - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) - via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + via_ac97_write_reg (card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1534,16 +1761,20 @@ assert (card != NULL); - memset (&card->ac97, 0, sizeof (card->ac97)); - card->ac97.private_data = card; - card->ac97.codec_read = via_ac97_read_reg; - card->ac97.codec_write = via_ac97_write_reg; - card->ac97.codec_wait = via_ac97_codec_wait; + card->ac97 = ac97_alloc_codec(); + if(card->ac97 == NULL) + return -ENOMEM; + + card->ac97->private_data = card; + card->ac97->codec_read = via_ac97_read_reg; + card->ac97->codec_write = via_ac97_write_reg; + card->ac97->codec_wait = via_ac97_codec_wait; - card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1); - if (card->ac97.dev_mixer < 0) { + 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"); + ac97_release_codec(card->ac97); return -EIO; } @@ -1552,25 +1783,27 @@ printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); goto err_out; } - - if (ac97_probe_codec (&card->ac97) == 0) { + + mdelay(10); + + if (ac97_probe_codec (card->ac97) == 0) { printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n"); rc = -EIO; goto err_out; } /* enable variable rate */ - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); - via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); + via_ac97_write_reg (card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); /* * If we cannot enable VRA, we have a locked-rate codec. * We try again to enable VRA before assuming so, however. */ - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) { - via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); - tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + via_ac97_write_reg (card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + tmp16 = via_ac97_read_reg (card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) { card->locked_rate = 1; printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); @@ -1581,8 +1814,9 @@ return 0; err_out: - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); DPRINTK ("EXIT, returning %d\n", rc); + ac97_release_codec(card->ac97); return rc; } @@ -1592,9 +1826,10 @@ DPRINTK ("ENTER\n"); assert (card != NULL); - assert (card->ac97.dev_mixer >= 0); + assert (card->ac97->dev_mixer >= 0); - unregister_sound_mixer (card->ac97.dev_mixer); + unregister_sound_mixer (card->ac97->dev_mixer); + ac97_release_codec(card->ac97); DPRINTK ("EXIT\n"); } @@ -1619,11 +1854,11 @@ * Locking: inside card->lock */ -static void via_intr_channel (struct via_channel *chan) +static void via_intr_channel (struct via_info *card, struct via_channel *chan) { u8 status; int n; - + /* check pertinent bits of status register for action bits */ status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED); if (!status) @@ -1642,6 +1877,7 @@ assert (n >= 0); 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 */ @@ -1656,48 +1892,44 @@ /* accounting crap for SNDCTL_DSP_GETxPTR */ chan->n_irqs++; chan->bytes += chan->frag_size; + /* FIXME - signed overflow is undefined */ if (chan->bytes < 0) /* handle overflow of 31-bit value */ chan->bytes = chan->frag_size; - + /* all following checks only occur when not in mmap(2) mode */ + if (!chan->is_mapped) + { + /* 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_frags) < chan->frag_number) + atomic_inc (&chan->n_frags); + assert (atomic_read (&chan->n_frags) <= chan->frag_number); + if (atomic_read (&chan->n_frags) == chan->frag_number) { + chan->is_active = 0; + via_chan_stop (chan->iobase); + } + } /* wake up anyone listening to see when interrupts occur */ - if (waitqueue_active (&chan->wait)) - wake_up_all (&chan->wait); + wake_up_all (&chan->wait); DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n", chan->name, status, (long) inl (chan->iobase + 0x04), atomic_read (&chan->hw_ptr)); - /* all following checks only occur when not in mmap(2) mode */ - if (chan->is_mapped) - return; - - /* 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_frags) < chan->frag_number) - atomic_inc (&chan->n_frags); - assert (atomic_read (&chan->n_frags) <= chan->frag_number); - - if (atomic_read (&chan->n_frags) == chan->frag_number) { - chan->is_active = 0; - via_chan_stop (chan->iobase); - } - - DPRINTK ("%s intr, channel n_frags == %d\n", chan->name, - atomic_read (&chan->n_frags)); + DPRINTK ("%s intr, channel n_frags == %d, missed %d\n", chan->name, + atomic_read (&chan->n_frags), missed); } -static irqreturn_t via_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t via_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct via_info *card = dev_id; u32 status32; - int handled = 0; /* to minimize interrupt sharing costs, we use the SGD status * shadow register to check the status of all inputs and @@ -1708,12 +1940,10 @@ if (!(status32 & VIA_INTR_MASK)) { #ifdef CONFIG_MIDI_VIA82CXXX - if (card->midi_devc) { + if (card->midi_devc) uart401intr(irq, card->midi_devc, regs); - handled = 1; - } #endif - goto out; + return IRQ_HANDLED; } DPRINTK ("intr, status32 == 0x%08X\n", status32); @@ -1722,21 +1952,42 @@ */ spin_lock (&card->lock); - if (status32 & VIA_INTR_OUT) { - handled = 1; - via_intr_channel (&card->ch_out); - } - if (status32 & VIA_INTR_IN) { - handled = 1; - via_intr_channel (&card->ch_in); - } - if (status32 & VIA_INTR_FM) { - handled = 1; - via_intr_channel (&card->ch_fm); - } + if (status32 & VIA_INTR_OUT) + via_intr_channel (card, &card->ch_out); + if (status32 & VIA_INTR_IN) + via_intr_channel (card, &card->ch_in); + if (status32 & VIA_INTR_FM) + via_intr_channel (card, &card->ch_fm); + spin_unlock (&card->lock); -out: - return IRQ_RETVAL(handled); + + return IRQ_HANDLED; +} + +static irqreturn_t via_new_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct via_info *card = dev_id; + u32 status32; + + /* to minimize interrupt sharing costs, we use the SGD status + * shadow register to check the status of all inputs and + * outputs with a single 32-bit bus read. If no interrupt + * conditions are flagged, we exit immediately + */ + status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW); + if (!(status32 & VIA_NEW_INTR_MASK)) + return IRQ_NONE; + /* + * goes away completely on UP + */ + spin_lock (&card->lock); + + via_intr_channel (card, &card->ch_out); + via_intr_channel (card, &card->ch_in); + via_intr_channel (card, &card->ch_fm); + + spin_unlock (&card->lock); + return IRQ_HANDLED; } @@ -1766,18 +2017,32 @@ return -EIO; } - /* make sure FM irq is not routed to us */ - pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); - if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { - tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; - pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); + /* VIA requires this is done */ + pci_write_config_byte(card->pdev, PCI_INTERRUPT_LINE, card->pdev->irq); + + if(card->legacy) + { + /* make sure FM irq is not routed to us */ + pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); + if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { + tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; + pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); + } + if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { + printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", + card->pdev->irq); + DPRINTK ("EXIT, returning -EBUSY\n"); + return -EBUSY; + } } - - if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { - printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", - card->pdev->irq); - DPRINTK ("EXIT, returning -EBUSY\n"); - return -EBUSY; + else + { + if (request_irq (card->pdev->irq, via_new_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { + printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", + card->pdev->irq); + DPRINTK ("EXIT, returning -EBUSY\n"); + return -EBUSY; + } } DPRINTK ("EXIT, returning 0\n"); @@ -1792,15 +2057,15 @@ */ static struct file_operations via_dsp_fops = { - .owner = THIS_MODULE, - .open = via_dsp_open, - .release = via_dsp_release, - .read = via_dsp_read, - .write = via_dsp_write, - .poll = via_dsp_poll, - .llseek = no_llseek, - .ioctl = via_dsp_ioctl, - .mmap = via_dsp_mmap, + owner: THIS_MODULE, + open: via_dsp_open, + release: via_dsp_release, + read: via_dsp_read, + write: via_dsp_write, + poll: via_dsp_poll, + llseek: no_llseek, + ioctl: via_dsp_ioctl, + mmap: via_dsp_mmap, }; @@ -1812,11 +2077,14 @@ assert (card != NULL); - /* turn off legacy features, if not already */ - pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8); - if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE)) { - tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE); - pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); + if(card->legacy) + { + /* turn off legacy features, if not already */ + pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8); + if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE)) { + tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE); + pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); + } } via_stop_everything (card); @@ -1911,10 +2179,10 @@ struct vm_operations_struct via_mm_ops = { - .nopage = via_mm_nopage, + nopage: via_mm_nopage, #ifndef VM_RESERVED - .swapout = via_mm_swapout, + swapout: via_mm_swapout, #endif }; @@ -2129,7 +2397,6 @@ file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); assert (file != NULL); - assert (buffer != NULL); card = file->private_data; assert (card != NULL); @@ -2287,11 +2554,12 @@ 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", + DPRINTK ("regs==S=%02X C=%02X TP=%02X BP=%08X RT=%08X SG=%08X CC=%08X SS=%08X\n", inb (card->baseaddr + 0x00), inb (card->baseaddr + 0x01), inb (card->baseaddr + 0x02), inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x08), inl (card->baseaddr + 0x0C), inl (card->baseaddr + 0x80), inl (card->baseaddr + 0x84)); @@ -2300,7 +2568,10 @@ goto handle_one_block; out: - return userbuf - orig_userbuf; + if (userbuf - orig_userbuf) + return userbuf - orig_userbuf; + else + return ret; } @@ -2314,7 +2585,6 @@ file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); assert (file != NULL); - assert (buffer != NULL); card = file->private_data; assert (card != NULL); @@ -2410,6 +2680,7 @@ add_wait_queue(&chan->wait, &wait); for (;;) { + DPRINTK ("FRAGS %d FRAGNUM %d\n", atomic_read(&chan->n_frags), chan->frag_number); __set_current_state(TASK_INTERRUPTIBLE); if (atomic_read (&chan->n_frags) >= chan->frag_number) break; @@ -2535,7 +2806,7 @@ info.fragments, info.bytes); - return copy_to_user(arg, &info, sizeof (info)) ? -EFAULT : 0; + return copy_to_user (arg, &info, sizeof (info))?-EFAULT:0; } @@ -2567,7 +2838,7 @@ if (chan->is_active) { unsigned long extra; info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size; - extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT); + extra = chan->frag_size - via_sg_offset(chan); info.ptr += extra; info.bytes += extra; } else { @@ -2579,7 +2850,7 @@ info.blocks, info.ptr); - return copy_to_user(arg, &info, sizeof (info)) ? -EFAULT : 0; + return copy_to_user (arg, &info, sizeof (info))?-EFAULT:0; } @@ -2688,7 +2959,7 @@ rc = put_user (val, (int *)arg); break; - /* query or set number of channels (1=mono, 2=stereo) */ + /* query or set number of channels (1=mono, 2=stereo, 4/6 for multichannel) */ case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) { rc = -EFAULT; @@ -2709,11 +2980,10 @@ val = rc; } else { - if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) || - (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO))) - val = 2; + if (rd) + val = card->ch_in.channels; else - val = 1; + val = card->ch_out.channels; } DPRINTK ("CHANNELS EXIT, returning %d\n", val); rc = put_user (val, (int *)arg); @@ -2873,10 +3143,11 @@ val = chan->frag_number - atomic_read (&chan->n_frags); + assert(val >= 0); + if (val > 0) { val *= chan->frag_size; - val -= chan->frag_size - - inl (chan->iobase + VIA_PCM_BLOCK_COUNT); + val -= chan->frag_size - via_sg_offset(chan); } val += chan->slop_len % chan->frag_size; } else @@ -2982,7 +3253,7 @@ static int via_dsp_open (struct inode *inode, struct file *file) { int minor = minor(inode->i_rdev); - struct via_info *card = NULL; + struct via_info *card; struct pci_dev *pdev = NULL; struct via_channel *chan; struct pci_driver *drvr; @@ -2995,6 +3266,7 @@ return -EINVAL; } + card = NULL; while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { drvr = pci_dev_driver (pdev); if (drvr == &via_driver) { @@ -3037,9 +3309,11 @@ /* why is this forced to 16-bit stereo in all drivers? */ chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; + chan->channels = 2; + // TO DO - use FIFO: via_capture_fifo(card, 1); via_chan_pcm_fmt (chan, 0); - via_set_rate (&card->ac97, chan, 44100); + via_set_rate (card->ac97, chan, 44100); } /* handle output to analog source */ @@ -3052,16 +3326,17 @@ /* 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; + chan->channels = 2; via_chan_pcm_fmt (chan, 0); - via_set_rate (&card->ac97, chan, 44100); + via_set_rate (card->ac97, chan, 44100); } else { 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); + via_set_rate (card->ac97, chan, 44100); } else { via_chan_pcm_fmt (chan, 1); - via_set_rate (&card->ac97, chan, 8000); + via_set_rate (card->ac97, chan, 8000); } } } @@ -3091,7 +3366,7 @@ if (file->f_mode & FMODE_WRITE) { rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); - if (rc) + if (rc && rc != ERESTARTSYS) /* Nobody needs to know about ^C */ printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc); via_chan_free (card, &card->ch_out); @@ -3130,11 +3405,12 @@ DPRINTK ("ENTER\n"); if (printed_version++ == 0) - printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n"); + printk (KERN_INFO "Via 686a/8233/8235 audio driver " VIA_VERSION "\n"); rc = pci_enable_device (pdev); if (rc) goto err_out; + rc = pci_request_regions (pdev, "via82cxxx_audio"); if (rc) @@ -3154,6 +3430,7 @@ card->baseaddr = pci_resource_start (pdev, 0); card->card_num = via_num_cards++; spin_lock_init (&card->lock); + spin_lock_init (&card->ac97_lock); init_MUTEX (&card->syscall_sem); init_MUTEX (&card->open_sem); @@ -3166,7 +3443,15 @@ * which means it has a few extra features */ if (pci_resource_start (pdev, 2) > 0) card->rev_h = 1; - + + /* Overkill for now, but more flexible done right */ + + card->intmask = id->driver_data; + card->legacy = !card->intmask; + card->sixchannel = id->driver_data; + + if(card->sixchannel) + printk(KERN_INFO PFX "Six channel audio available\n"); if (pdev->irq < 1) { printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq); rc = -ENODEV; @@ -3179,6 +3464,8 @@ goto err_out_kfree; } + pci_set_master(pdev); + /* * init AC97 mixer and codec */ @@ -3222,26 +3509,29 @@ /* Disable by default */ card->midi_info.io_base = 0; - pci_read_config_byte (pdev, 0x42, &r42); - /* Disable MIDI interrupt */ - pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK); - if (r42 & VIA_CR42_MIDI_ENABLE) + if(card->legacy) { - if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */ - card->midi_info.io_base = pci_resource_start (pdev, 2); - else /* Address selected by byte 0x43 */ + pci_read_config_byte (pdev, 0x42, &r42); + /* Disable MIDI interrupt */ + pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK); + if (r42 & VIA_CR42_MIDI_ENABLE) { - u8 r43; - pci_read_config_byte (pdev, 0x43, &r43); - card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2); - } + if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */ + card->midi_info.io_base = pci_resource_start (pdev, 2); + else /* Address selected by byte 0x43 */ + { + u8 r43; + pci_read_config_byte (pdev, 0x43, &r43); + card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2); + } - card->midi_info.irq = -pdev->irq; - if (probe_uart401(& card->midi_info, THIS_MODULE)) - { - card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc; - pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK); - printk("Enabled Via MIDI\n"); + card->midi_info.irq = -pdev->irq; + if (probe_uart401(& card->midi_info, THIS_MODULE)) + { + card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc; + pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK); + printk("Enabled Via MIDI\n"); + } } } #endif @@ -3501,7 +3791,7 @@ } sprintf (s, "driver/via/%d/ac97", card->card_num); - if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, card->ac97)) { rc = -EIO; goto err_out_info; } diff -urN linux-2.5.75-bk1/sound/oss/ymfpci.c linux-2.5.75-bk2/sound/oss/ymfpci.c --- linux-2.5.75-bk1/sound/oss/ymfpci.c 2003-07-10 13:11:35.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/ymfpci.c 2003-07-12 04:37:48.000000000 -0700 @@ -177,15 +177,16 @@ ymfpci_t *codec = dev->private_data; u32 cmd; + spin_lock(&codec->ac97_lock); /* XXX Do make use of dev->id */ ymfpci_codec_ready(codec, 0, 0); cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd); + spin_unlock(&codec->ac97_lock); } -static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +static u16 _ymfpci_codec_read(ymfpci_t *unit, u8 reg) { - ymfpci_t *unit = dev->private_data; int i; if (ymfpci_codec_ready(unit, 0, 0)) @@ -200,6 +201,18 @@ return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); } +static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +{ + ymfpci_t *unit = dev->private_data; + u16 ret; + + spin_lock(&unit->ac97_lock); + ret = _ymfpci_codec_read(unit, reg); + spin_unlock(&unit->ac97_lock); + + return ret; +} + /* * Misc routines */ @@ -2444,9 +2457,8 @@ struct ac97_codec *codec; u16 eid; - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + if ((codec = ac97_alloc_codec()) == NULL) return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ @@ -2462,7 +2474,7 @@ } eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); - if (eid==0xFFFFFF) { + if (eid==0xFFFF) { printk(KERN_WARNING "ymfpci: no codec attached ?\n"); goto out_kfree; } @@ -2478,7 +2490,7 @@ return 0; out_kfree: - kfree(codec); + ac97_release_codec(codec); return -ENODEV; } @@ -2517,6 +2529,7 @@ spin_lock_init(&codec->reg_lock); spin_lock_init(&codec->voice_lock); + spin_lock_init(&codec->ac97_lock); init_MUTEX(&codec->open_sem); INIT_LIST_HEAD(&codec->states); codec->pci = pcidev; @@ -2615,7 +2628,7 @@ out_release_region: release_mem_region(pci_resource_start(pcidev, 0), 0x8000); out_free: - kfree(codec); + ac97_release_codec(codec->ac97_codec[0]); return -ENODEV; } @@ -2628,7 +2641,7 @@ list_del(&codec->ymf_devs); unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer); - kfree(codec->ac97_codec[0]); + ac97_release_codec(codec->ac97_codec[0]); unregister_sound_dsp(codec->dev_audio); free_irq(pcidev->irq, codec); ymfpci_memfree(codec); @@ -2643,7 +2656,6 @@ unload_uart401(&codec->mpu_data); } #endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - kfree(codec); } MODULE_AUTHOR("Jaroslav Kysela"); diff -urN linux-2.5.75-bk1/sound/oss/ymfpci.h linux-2.5.75-bk2/sound/oss/ymfpci.h --- linux-2.5.75-bk1/sound/oss/ymfpci.h 2003-07-10 13:08:14.000000000 -0700 +++ linux-2.5.75-bk2/sound/oss/ymfpci.h 2003-07-12 04:37:48.000000000 -0700 @@ -275,6 +275,7 @@ spinlock_t reg_lock; spinlock_t voice_lock; + spinlock_t ac97_lock; /* soundcore stuff */ int dev_audio;