Parent repository is http://linux-sound.bkbits.net/linux-sound
======== ChangeSet 1.1564 ========
D 1.1564 04/02/29 12:55:43-08:00 akpm@mnm.(none) 37851 37844 0/0/1
P ChangeSet
C Merge http://linux-sound.bkbits.net/linux-sound
C into mnm.(none):/usr/src/bk-alsa
------------------------------------------------

diff -Nru a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
--- a/Documentation/sound/alsa/ALSA-Configuration.txt	Sun Feb 29 12:58:38 2004
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt	Sun Feb 29 12:58:38 2004
@@ -692,6 +692,15 @@
 
     The power-management is supported.
 
+  Module snd-mixart
+  -----------------
+
+    Module for Digigram miXart8 soundcards.
+
+    Module supports multiple cards.
+    Note: One miXart8 board will be represented as 4 alsa cards.
+          See MIXART.txt for details.
+
   Module snd-mpu401
   -----------------
 
@@ -1169,6 +1178,16 @@
     Module supports autoprobe and multiple chips (max 8).
 
     The power-management is supported.
+
+  Module snd-pdaudiocf
+  --------------------
+
+    Module for Sound Core PDAudioCF soundcard.
+
+    irq_mask	  - IRQ mask (PCMCIA type)
+    irq_list	  - List of available interrupts for this soundcard
+
+    Note: the driver is build only when CONFIG_ISA is set.
 
 
 Configuring Non-ISAPNP Cards
diff -Nru a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl	Sun Feb 29 12:58:38 2004
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl	Sun Feb 29 12:58:38 2004
@@ -3769,14 +3769,20 @@
             <programlisting>
 <![CDATA[
   static int snd_myctl_info(snd_kcontrol_t *kcontrol,
-  static char *texts[4] = {
-          "First", "Second", "Third", "Fourth"
-  };
-  uinfo->value.enumerated.items = 4;
-  if (uinfo->value.enumerated.item > 3)
-          uinfo->value.enumerated.item = 3;
-  strcpy(uinfo->value.enumerated.name,
-         texts[uinfo->value.enumerated.item]);
+                          snd_ctl_elem_info_t *uinfo)
+  {
+          static char *texts[4] = {
+                  "First", "Second", "Third", "Fourth"
+          };
+          uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+          uinfo->count = 1;
+          uinfo->value.enumerated.items = 4;
+          if (uinfo->value.enumerated.item > 3)
+                  uinfo->value.enumerated.item = 3;
+          strcpy(uinfo->value.enumerated.name,
+                 texts[uinfo->value.enumerated.item]);
+          return 0;
+  }
 ]]>
             </programlisting>
           </informalexample>
diff -Nru a/Documentation/sound/alsa/Joystick.txt b/Documentation/sound/alsa/Joystick.txt
--- a/Documentation/sound/alsa/Joystick.txt	Sun Feb 29 12:58:39 2004
+++ b/Documentation/sound/alsa/Joystick.txt	Sun Feb 29 12:58:39 2004
@@ -43,7 +43,8 @@
     ens1370	joystick	0 = disable (default), 1 = enable
     ens1371	joystick_port	0 = disable (default), 1 = auto-detect,
                                 manual: 0x200, 0x208, 0x210, 0x218
-    cmipci	joystick	0 = disable (default), 1 = enable
+    cmipci	joystick_port	0 = disable (default), 1 = auto-detect,
+                                manual: any address (e.g. 0x200)
     cs4281	N/A		N/A
     cs46xx	N/A		N/A
     es1938	N/A		N/A
diff -Nru a/Documentation/sound/alsa/MIXART.txt b/Documentation/sound/alsa/MIXART.txt
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/Documentation/sound/alsa/MIXART.txt	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,96 @@
+    Alsa driver for Digigram miXart8 and miXart8AES/EBU soundcards
+	    Digigram <alsa@digigram.com>
+
+
+GENERAL
+=======
+
+The miXart8 is a multichannel audio processing and mixing soundcard
+that has 4 stereo audio inputs and 4 stereo audio outputs.
+The miXart8AES/EBU is the same with a add-on card that offers further
+4 digital stereo audio inputs and outputs.
+Furthermore the add-on card offers external clock synchronisation
+(AES/EBU, Word Clock, Time Code and Video Synchro)
+
+The mainboard has a PowerPC that offers onboard mpeg encoding and
+decoding, samplerate conversions and various effects.
+
+The driver don't work properly at all until the certain firmwares
+are loaded, i.e. no PCM nor mixer devices will appear.
+Use the mixartloader that can be found in the alsa-tools package.
+
+
+VERSION 0.1.0
+=============
+
+One miXart8 board will be represented as 4 alsa cards, each with 1
+stereo analog capture 'pcm0c' and 1 stereo analog playback 'pcm0p' device.
+With a miXart8AES/EBU there is in addition 1 stereo digital input
+'pcm1c' and 1 stereo digital output 'pcm1p' per card.
+
+Formats
+-------
+U8, S16_LE, S16_BE, S24_3LE, S24_3BE, FLOAT_LE, FLOAT_BE
+Sample rates : 8000 - 48000 Hz continously
+
+Playback
+--------
+For instance the playback devices are configured to have max. 4
+substreams performing hardware mixing. This could be changed to a
+maximum of 24 substreams if wished.
+Mono files will be played on the left and right channel. Each channel
+can be muted for each stream to use 8 analog/digital outputs seperately.
+
+Capture
+-------
+There is one substream per capture device. For instance only stereo
+formats are supported.
+
+Mixer
+-----
+<Master> and <Master Capture> : analog volume control of playback and capture PCM.
+<PCM 0-3> and <PCM Capture> : digital volume control of each analog substream.
+<AES 0-3> and <AES Capture> : digital volume control of each AES/EBU substream.
+<Monitoring> : Loopback from 'pcm0c' to 'pcm0p' with digital volume
+and mute control.
+
+Rem : for best audio quality try to keep a 0 attenuation on the PCM
+and AES volume controls which is set by 219 in the range from 0 to 255
+(about 86% with alsamixer)
+
+
+NOT YET IMPLEMENTED
+===================
+
+- external clock support (AES/EBU, Word Clock, Time Code, Video Sync)
+- MPEG audio formats
+- mono record
+- on-board effects and samplerate conversions
+- linked streams
+
+
+FIRMWARE
+========
+
+For loading the firmware automatically after the module is loaded, use
+the post-install command.  For example, add the following entry to
+/etc/modprobe.conf for miXart driver:
+
+	install snd-mixart /sbin/modprobe --first-time -i snd-mixart && \
+			   /usr/bin/mixartloader
+(for 2.2/2.4 kernels, add "post-install snd-mixart /usr/bin/vxloader" to
+ /etc/modules.conf, instead.)
+
+The firmware binaries are installed on /usr/share/alsa/firmware
+(or /usr/local/share/alsa/firmware, depending to the prefix option of
+configure).  There will be a miXart.conf file, which define the dsp image
+files.
+
+The firmware files are copyright by Digigram SA
+
+
+COPYRIGHT
+=========
+
+Copyright (c) 2003 Digigram SA <alsa@digigram.com>
+Distributalbe under GPL.
diff -Nru a/include/sound/ak4117.h b/include/sound/ak4117.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/sound/ak4117.h	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,191 @@
+#ifndef __SOUND_AK4117_H
+#define __SOUND_AK4117_H
+
+/*
+ *  Routines for Asahi Kasei AK4117
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define AK4117_REG_PWRDN	0x00	/* power down */
+#define AK4117_REG_CLOCK	0x01	/* clock control */
+#define AK4117_REG_IO		0x02	/* input/output control */
+#define AK4117_REG_INT0_MASK	0x03	/* interrupt0 mask */
+#define AK4117_REG_INT1_MASK	0x04	/* interrupt1 mask */
+#define AK4117_REG_RCS0		0x05	/* receiver status 0 */
+#define AK4117_REG_RCS1		0x06	/* receiver status 1 */
+#define AK4117_REG_RCS2		0x07	/* receiver status 2 */
+#define AK4117_REG_RXCSB0	0x08	/* RX channel status byte 0 */
+#define AK4117_REG_RXCSB1	0x09	/* RX channel status byte 1 */
+#define AK4117_REG_RXCSB2	0x0a	/* RX channel status byte 2 */
+#define AK4117_REG_RXCSB3	0x0b	/* RX channel status byte 3 */
+#define AK4117_REG_RXCSB4	0x0c	/* RX channel status byte 4 */
+#define AK4117_REG_Pc0		0x0d	/* burst preamble Pc byte 0 */
+#define AK4117_REG_Pc1		0x0e	/* burst preamble Pc byte 1 */
+#define AK4117_REG_Pd0		0x0f	/* burst preamble Pd byte 0 */
+#define AK4117_REG_Pd1		0x10	/* burst preamble Pd byte 1 */
+#define AK4117_REG_QSUB_ADDR	0x11	/* Q-subcode address + control */
+#define AK4117_REG_QSUB_TRACK	0x12	/* Q-subcode track */
+#define AK4117_REG_QSUB_INDEX	0x13	/* Q-subcode index */
+#define AK4117_REG_QSUB_MINUTE	0x14	/* Q-subcode minute */
+#define AK4117_REG_QSUB_SECOND	0x15	/* Q-subcode second */
+#define AK4117_REG_QSUB_FRAME	0x16	/* Q-subcode frame */
+#define AK4117_REG_QSUB_ZERO	0x17	/* Q-subcode zero */
+#define AK4117_REG_QSUB_ABSMIN	0x18	/* Q-subcode absolute minute */
+#define AK4117_REG_QSUB_ABSSEC	0x19	/* Q-subcode absolute second */
+#define AK4117_REG_QSUB_ABSFRM	0x1a	/* Q-subcode absolute frame */
+
+/* sizes */
+#define AK4117_REG_RXCSB_SIZE	((AK4117_REG_RXCSB4-AK4117_REG_RXCSB0)+1)
+#define AK4117_REG_QSUB_SIZE	((AK4117_REG_QSUB_ABSFRM-AK4117_REG_QSUB_ADDR)+1)
+
+/* AK4117_REG_PWRDN bits */
+#define AK4117_EXCT		(1<<4)	/* 0 = X'tal mode, 1 = external clock mode */
+#define AK4117_XTL1		(1<<3)	/* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */
+#define AK4117_XTL0		(1<<2)	/* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */
+#define AK4117_XTL_11_2896M	(0)
+#define AK4117_XTL_12_288M	AK4117_XTL0
+#define AK4117_XTL_24_576M	AK4117_XTL1
+#define AK4117_XTL_EXT		(AK4117_XTL1|AK4117_XTL0)
+#define AK4117_PWN		(1<<1)	/* 0 = power down, 1 = normal operation */
+#define AK4117_RST		(1<<0)	/* 0 = reset & initialize (except this register), 1 = normal operation */
+
+/* AK4117_REQ_CLOCK bits */
+#define AK4117_LP		(1<<7)	/* 0 = normal mode, 1 = low power mode (Fs up to 48kHz only) */
+#define AK4117_PKCS1		(1<<6)	/* master clock frequency at PLL mode (when LP == 0) */
+#define AK4117_PKCS0		(1<<5)
+#define AK4117_PKCS_512fs	(0)
+#define AK4117_PKCS_256fs	AK4117_PKCS0
+#define AK4117_PKCS_128fs	AK4117_PKCS1
+#define AK4117_DIV		(1<<4)	/* 0 = MCKO == Fs, 1 = MCKO == Fs / 2; X'tal mode only */
+#define AK4117_XCKS1		(1<<3)	/* master clock frequency at X'tal mode */
+#define AK4117_XCKS0		(1<<2)
+#define AK4117_XCKS_128fs	(0)
+#define AK4117_XCKS_256fs	AK4117_XCKS0
+#define AK4117_XCKS_512fs	AK4117_XCKS1
+#define AK4117_XCKS_1024fs	(AK4117_XCKS1|AK4117_XCKS0)
+#define AK4117_CM1		(1<<1)	/* MCKO operation mode select */
+#define AK4117_CM0		(1<<0)
+#define AK4117_CM_PLL		(0)		/* use RX input as master clock */
+#define AK4117_CM_XTAL		(AK4117_CM0)	/* use X'tal as master clock */
+#define AK4117_CM_PLL_XTAL	(AK4117_CM1)	/* use Rx input but X'tal when PLL loses lock */
+#define AK4117_CM_MONITOR	(AK4117_CM0|AK4117_CM1) /* use X'tal as master clock, but use PLL for monitoring */
+
+/* AK4117_REG_IO */
+#define AK4117_IPS		(1<<7)	/* Input Recovery Data Select, 0 = RX0, 1 = RX1 */
+#define AK4117_UOUTE		(1<<6)	/* U-bit output enable to UOUT, 0 = disable, 1 = enable */
+#define AK4117_CS12		(1<<5)	/* channel status select, 0 = channel1, 1 = channel2 */
+#define AK4117_EFH2		(1<<4)	/* INT0 pin hold count select */
+#define AK4117_EFH1		(1<<3)
+#define AK4117_EFH_512LRCLK	(0)
+#define AK4117_EFH_1024LRCLK	(AK4117_EFH1)
+#define AK4117_EFH_2048LRCLK	(AK4117_EFH2)
+#define AK4117_EFH_4096LRCLK	(AK4117_EFH1|AK4117_EFH2)
+#define AK4117_DIF2		(1<<2)	/* audio data format control */
+#define AK4117_DIF1		(1<<1)
+#define AK4117_DIF0		(1<<0)
+#define AK4117_DIF_16R		(0)				/* STDO: 16-bit, right justified */
+#define AK4117_DIF_18R		(AK4117_DIF0)			/* STDO: 18-bit, right justified */
+#define AK4117_DIF_20R		(AK4117_DIF1)			/* STDO: 20-bit, right justified */
+#define AK4117_DIF_24R		(AK4117_DIF1|AK4117_DIF0)	/* STDO: 24-bit, right justified */
+#define AK4117_DIF_24L		(AK4117_DIF2)			/* STDO: 24-bit, left justified */
+#define AK4117_DIF_24I2S	(AK4117_DIF2|AK4117_DIF0)	/* STDO: I2S */
+
+/* AK4117_REG_INT0_MASK & AK4117_INT1_MASK */
+#define AK4117_MULK		(1<<7)	/* mask enable for UNLOCK bit */
+#define AK4117_MPAR		(1<<6)	/* mask enable for PAR bit */
+#define AK4117_MAUTO		(1<<5)	/* mask enable for AUTO bit */
+#define AK4117_MV		(1<<4)	/* mask enable for V bit */
+#define AK4117_MAUD		(1<<3)	/* mask enable for AUDION bit */
+#define AK4117_MSTC		(1<<2)	/* mask enable for STC bit */
+#define AK4117_MCIT		(1<<1)	/* mask enable for CINT bit */
+#define AK4117_MQIT		(1<<0)	/* mask enable for QINT bit */
+
+/* AK4117_REG_RCS0 */
+#define AK4117_UNLCK		(1<<7)	/* PLL lock status, 0 = lock, 1 = unlock */
+#define AK4117_PAR		(1<<6)	/* parity error or biphase error status, 0 = no error, 1 = error */
+#define AK4117_AUTO		(1<<5)	/* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */
+#define AK4117_V		(1<<4)	/* Validity bit, 0 = valid, 1 = invalid */
+#define AK4117_AUDION		(1<<3)	/* audio bit output, 0 = audio, 1 = non-audio */
+#define AK4117_STC		(1<<2)	/* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */
+#define AK4117_CINT		(1<<1)	/* channel status buffer interrupt, 0 = no change, 1 = change */
+#define AK4117_QINT		(1<<0)	/* Q-subcode buffer interrupt, 0 = no change, 1 = changed */
+
+/* AK4117_REG_RCS1 */
+#define AK4117_DTSCD		(1<<6)	/* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */
+#define AK4117_NPCM		(1<<5)	/* Non-PCM bit stream detection, 0 = no detect, 1 = detect */
+#define AK4117_PEM		(1<<4)	/* Pre-emphasis detect, 0 = OFF, 1 = ON */
+#define AK4117_FS3		(1<<3)	/* sampling frequency detection */
+#define AK4117_FS2		(1<<2)
+#define AK4117_FS1		(1<<1)
+#define AK4117_FS0		(1<<0)
+#define AK4117_FS_44100HZ	(0)
+#define AK4117_FS_48000HZ	(AK4117_FS1)
+#define AK4117_FS_32000HZ	(AK4117_FS1|AK4117_FS0)
+#define AK4117_FS_88200HZ	(AK4117_FS3)
+#define AK4117_FS_96000HZ	(AK4117_FS3|AK4117_FS1)
+#define AK4117_FS_176400HZ	(AK4117_FS3|AK4117_FS2)
+#define AK4117_FS_192000HZ	(AK4117_FS3|AK4117_FS2|AK4117_FS1)
+
+/* AK4117_REG_RCS2 */
+#define AK4117_CCRC		(1<<1)	/* CRC for channel status, 0 = no error, 1 = error */
+#define AK4117_QCRC		(1<<0)	/* CRC for Q-subcode, 0 = no error, 1 = error */
+
+/* flags for snd_ak4117_check_rate_and_errors() */
+#define AK4117_CHECK_NO_STAT	(1<<0)	/* no statistics */
+#define AK4117_CHECK_NO_RATE	(1<<1)	/* no rate check */
+
+#define AK4117_CONTROLS		13
+
+typedef void (ak4117_write_t)(void *private_data, unsigned char addr, unsigned char data);
+typedef unsigned char (ak4117_read_t)(void *private_data, unsigned char addr);
+
+typedef struct ak4117 ak4117_t;
+
+struct ak4117 {
+	snd_card_t * card;
+	ak4117_write_t * write;
+	ak4117_read_t * read;
+	void * private_data;
+	unsigned int init: 1;
+	spinlock_t lock;
+	unsigned char regmap[5];
+	snd_kcontrol_t *kctls[AK4117_CONTROLS];
+	snd_pcm_substream_t *substream;
+	unsigned long parity_errors;
+	unsigned long v_bit_errors;
+	unsigned long qcrc_errors;
+	unsigned long ccrc_errors;
+	unsigned char rcs0;
+	unsigned char rcs1;
+	unsigned char rcs2;
+	struct timer_list timer;	/* statistic timer */
+	void *change_callback_private;
+	void (*change_callback)(ak4117_t *ak4117, unsigned char c0, unsigned char c1);
+};
+
+int snd_ak4117_create(snd_card_t *card, ak4117_read_t *read, ak4117_write_t *write,
+		      unsigned char pgm[5], void *private_data, ak4117_t **r_ak4117);
+void snd_ak4117_reg_write(ak4117_t *chip, unsigned char reg, unsigned char mask, unsigned char val);
+void snd_ak4117_reinit(ak4117_t *chip);
+int snd_ak4117_build(ak4117_t *ak4117, snd_pcm_substream_t *capture_substream);
+int snd_ak4117_external_rate(ak4117_t *ak4117);
+int snd_ak4117_check_rate_and_errors(ak4117_t *ak4117, unsigned int flags);
+
+#endif /* __SOUND_AK4117_H */
+
diff -Nru a/include/sound/asequencer.h b/include/sound/asequencer.h
--- a/include/sound/asequencer.h	Sun Feb 29 12:58:39 2004
+++ b/include/sound/asequencer.h	Sun Feb 29 12:58:39 2004
@@ -594,6 +594,7 @@
 #define SNDRV_SEQ_PORT_TYPE_MIDI_GS	(1<<3)	/* GS compatible device */
 #define SNDRV_SEQ_PORT_TYPE_MIDI_XG	(1<<4)	/* XG compatible device */
 #define SNDRV_SEQ_PORT_TYPE_MIDI_MT32	(1<<5)	/* MT-32 compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GM2	(1<<6)	/* General MIDI 2 compatible device */
 
 /* other standards...*/
 #define SNDRV_SEQ_PORT_TYPE_SYNTH	(1<<10)	/* Synth device (no MIDI compatible - direct wavetable) */
@@ -605,7 +606,7 @@
 /* misc. conditioning flags */
 #define SNDRV_SEQ_PORT_FLG_GIVEN_PORT	(1<<0)
 #define SNDRV_SEQ_PORT_FLG_TIMESTAMP	(1<<1)
-#define SNDRV_SEQ_PORT_FLG_TIME_REAL	(1<<1)
+#define SNDRV_SEQ_PORT_FLG_TIME_REAL	(1<<2)
 
 struct sndrv_seq_port_info {
 	struct sndrv_seq_addr addr;	/* client/port numbers */
diff -Nru a/include/sound/emu10k1.h b/include/sound/emu10k1.h
--- a/include/sound/emu10k1.h	Sun Feb 29 12:58:38 2004
+++ b/include/sound/emu10k1.h	Sun Feb 29 12:58:38 2004
@@ -644,9 +644,13 @@
 #define SOLEH			0x5d		/* Stop on loop enable high register		*/
 
 #define SPBYPASS		0x5e		/* SPDIF BYPASS mode register			*/
-#define SPBYPASS_ENABLE		0x00000001	/* Enable SPDIF bypass mode			*/
+#define SPBYPASS_SPDIF0_MASK	0x00000003	/* SPDIF 0 bypass mode				*/
+#define SPBYPASS_SPDIF1_MASK	0x0000000c	/* SPDIF 1 bypass mode				*/
+/* bypass mode: 0 - DSP; 1 - SPDIF A, 2 - SPDIF B, 3 - SPDIF C					*/
+#define SPBYPASS_FORMAT		0x00000f00      /* If 1, SPDIF XX uses 24 bit, if 0 - 20 bit	*/
 
 #define AC97SLOT		0x5f            /* additional AC97 slots enable bits		*/
+#define AC97SLOT_10K2		0x03
 #define AC97SLOT_CNTR		0x10            /* Center enable */
 #define AC97SLOT_LFE		0x20            /* LFE enable */
 
@@ -837,7 +841,7 @@
 typedef struct snd_emu10k1_memblk {
 	snd_util_memblk_t mem;
 	/* private part */
-	short first_page, last_page, pages, mapped_page;
+	int first_page, last_page, pages, mapped_page;
 	unsigned int map_locked;
 	struct list_head mapped_link;
 	struct list_head mapped_order_link;
diff -Nru a/include/sound/info.h b/include/sound/info.h
--- a/include/sound/info.h	Sun Feb 29 12:58:39 2004
+++ b/include/sound/info.h	Sun Feb 29 12:58:39 2004
@@ -171,7 +171,7 @@
 					 struct proc_dir_entry *de) { ; }
 
 #define snd_card_proc_new(card,name,entryp)  0 /* always success */
-#define snd_info_set_text_ops(entry,private_data,read) /*NOP*/
+#define snd_info_set_text_ops(entry,private_data,read_size,read) /*NOP*/
 
 #endif
 
diff -Nru a/include/sound/pcm.h b/include/sound/pcm.h
--- a/include/sound/pcm.h	Sun Feb 29 12:58:39 2004
+++ b/include/sound/pcm.h	Sun Feb 29 12:58:39 2004
@@ -889,6 +889,9 @@
 snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream,
 				    void **bufs, snd_pcm_uframes_t frames);
 
+int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime);
+
+
 /*
  *  Timer interface
  */
diff -Nru a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h
--- a/include/sound/pcm_oss.h	Sun Feb 29 12:58:39 2004
+++ b/include/sound/pcm_oss.h	Sun Feb 29 12:58:39 2004
@@ -50,6 +50,7 @@
 	unsigned int maxfrags;
 	unsigned int subdivision;		/* requested subdivision */
 	size_t period_bytes;			/* requested period size */
+	size_t period_frames;			/* period frames for poll */
 	size_t period_ptr;			/* actual write pointer to period */
 	unsigned int periods;
 	size_t buffer_bytes;			/* requested buffer size */
diff -Nru a/include/sound/sndmagic.h b/include/sound/sndmagic.h
--- a/include/sound/sndmagic.h	Sun Feb 29 12:58:38 2004
+++ b/include/sound/sndmagic.h	Sun Feb 29 12:58:38 2004
@@ -200,6 +200,7 @@
 #define azf3328_t_magic				0xa15a4200
 #define snd_card_harmony_t_magic		0xa15a4300
 #define bt87x_t_magic				0xa15a4400
+#define pdacf_t_magic				0xa15a4500
 
 #else
 
diff -Nru a/sound/core/Makefile b/sound/core/Makefile
--- a/sound/core/Makefile	Sun Feb 29 12:58:39 2004
+++ b/sound/core/Makefile	Sun Feb 29 12:58:39 2004
@@ -107,5 +107,7 @@
 obj-$(CONFIG_SND_VXP440) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
 obj-$(CONFIG_SND_VX222) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
 obj-$(CONFIG_SND_BT87X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
+obj-$(CONFIG_SND_MIXART) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
+obj-$(CONFIG_SND_PDAUDIOCF) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
 
 obj-m := $(sort $(obj-m))
diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
--- a/sound/core/oss/pcm_oss.c	Sun Feb 29 12:58:39 2004
+++ b/sound/core/oss/pcm_oss.c	Sun Feb 29 12:58:39 2004
@@ -133,6 +133,15 @@
 	return (runtime->oss.buffer_bytes * frames) / buffer_size;
 }
 
+static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	if (buffer_size == runtime->oss.buffer_bytes)
+		return bytes_to_frames(runtime, bytes);
+	return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
+}
+
 static int snd_pcm_oss_format_from(int format)
 {
 	switch (format) {
@@ -254,6 +263,7 @@
 
 	snd_assert(oss_period_size >= 16, return -EINVAL);
 	runtime->oss.period_bytes = oss_period_size;
+	runtime->oss.period_frames = 1;
 	runtime->oss.periods = oss_periods;
 	return 0;
 }
@@ -511,6 +521,8 @@
 	if (runtime->dma_area)
 		snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
 
+	runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
+
 	err = 0;
 failure:
 	if (sw_params)
@@ -2098,7 +2110,7 @@
 	if (atomic_read(&runtime->mmap_count))
 		return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
 	else
-		return snd_pcm_playback_ready(substream);
+		return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
 }
 
 static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream)
@@ -2107,7 +2119,7 @@
 	if (atomic_read(&runtime->mmap_count))
 		return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
 	else
-		return snd_pcm_capture_ready(substream);
+		return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
 }
 
 static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
diff -Nru a/sound/core/pcm.c b/sound/core/pcm.c
--- a/sound/core/pcm.c	Sun Feb 29 12:58:39 2004
+++ b/sound/core/pcm.c	Sun Feb 29 12:58:39 2004
@@ -327,8 +327,9 @@
 		snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
 		snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);	
 		snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate);
-		snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes);	
+		snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes);
 		snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods);
+		snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
 	}
 #endif
 	snd_pcm_stream_unlock_irq(substream);
@@ -1060,3 +1061,4 @@
 EXPORT_SYMBOL(snd_pcm_format_silence_64);
 EXPORT_SYMBOL(snd_pcm_format_set_silence);
 EXPORT_SYMBOL(snd_pcm_build_linear_format);
+EXPORT_SYMBOL(snd_pcm_limit_hw_rates);
diff -Nru a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
--- a/sound/core/pcm_misc.c	Sun Feb 29 12:58:39 2004
+++ b/sound/core/pcm_misc.c	Sun Feb 29 12:58:39 2004
@@ -654,3 +654,35 @@
 	}
 	return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]);
 }
+
+/**
+ * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
+ * @runtime: the runtime instance
+ *
+ * Determines the rate_min and rate_max fields from the rates bits of
+ * the given runtime->hw.
+ *
+ * Returns zero if successful.
+ */
+int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime)
+{
+	static unsigned rates[] = {
+		/* ATTENTION: these values depend on the definition in pcm.h! */
+		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+		64000, 88200, 96000, 176400, 192000
+	};
+	int i;
+	for (i = 0; i < (int)ARRAY_SIZE(rates); i++) {
+		if (runtime->hw.rates & (1 << i)) {
+			runtime->hw.rate_min = rates[i];
+			break;
+		}
+	}
+	for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) {
+		if (runtime->hw.rates & (1 << i)) {
+			runtime->hw.rate_max = rates[i];
+			break;
+		}
+	}
+	return 0;
+}
diff -Nru a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
--- a/sound/core/seq/oss/seq_oss_midi.c	Sun Feb 29 12:58:38 2004
+++ b/sound/core/seq/oss/seq_oss_midi.c	Sun Feb 29 12:58:38 2004
@@ -491,7 +491,7 @@
 			}
 		}
 	}
-	snd_seq_oss_midi_close(dp, dev);
+	// snd_seq_oss_midi_close(dp, dev);
 	snd_use_lock_free(&mdev->use_lock);
 }
 
diff -Nru a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
--- a/sound/core/seq/oss/seq_oss_synth.c	Sun Feb 29 12:58:38 2004
+++ b/sound/core/seq/oss/seq_oss_synth.c	Sun Feb 29 12:58:38 2004
@@ -410,6 +410,8 @@
 		if (midi_synth_dev.opened <= 0)
 			return;
 		snd_seq_oss_midi_reset(dp, info->midi_mapped);
+		/* reopen the device */
+		snd_seq_oss_midi_close(dp, dev);
 		if (snd_seq_oss_midi_open(dp, info->midi_mapped,
 					  dp->file_mode) < 0) {
 			midi_synth_dev.opened--;
diff -Nru a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
--- a/sound/core/seq/seq_clientmgr.c	Sun Feb 29 12:58:38 2004
+++ b/sound/core/seq/seq_clientmgr.c	Sun Feb 29 12:58:38 2004
@@ -547,6 +547,36 @@
 
 
 /*
+ * expand a quoted event.
+ */
+static int expand_quoted_event(snd_seq_event_t *event)
+{
+	snd_seq_event_t *quoted;
+
+	quoted = event->data.quote.event;
+	if (quoted == NULL) {
+		snd_printd("seq: quoted event is NULL\n");
+		return -EINVAL;
+	}
+
+	event->type = quoted->type;
+	event->tag = quoted->tag;
+	event->source = quoted->source;
+	/* don't use quoted destination */
+	event->data = quoted->data;
+	/* use quoted timestamp only if subscription/port didn't update it */
+	if (event->queue == SNDRV_SEQ_QUEUE_DIRECT) {
+		event->flags = quoted->flags;
+		event->queue = quoted->queue;
+		event->time = quoted->time;
+	} else {
+		event->flags = (event->flags & SNDRV_SEQ_TIME_STAMP_MASK)
+			| (quoted->flags & ~SNDRV_SEQ_TIME_STAMP_MASK);
+	}
+	return 0;
+}
+
+/*
  * deliver an event to the specified destination.
  * if filter is non-zero, client filter bitmap is tested.
  *
@@ -581,12 +611,9 @@
 		update_timestamp_of_queue(event, dest_port->time_queue,
 					  dest_port->time_real);
 
-	/* expand the quoted event */
 	if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) {
 		quoted = 1;
-		event = event->data.quote.event;
-		if (event == NULL) {
-			snd_printd("seq: quoted event is NULL\n");
+		if (expand_quoted_event(event) < 0) {
 			result = 0; /* do not send bounce error */
 			goto __skip;
 		}
@@ -694,8 +721,8 @@
 	if (dest_client == NULL)
 		return 0; /* no matching destination */
 
-	read_lock(&client->ports_lock);
-	list_for_each(p, &client->ports_list_head) {
+	read_lock(&dest_client->ports_lock);
+	list_for_each(p, &dest_client->ports_list_head) {
 		client_port_t *port = list_entry(p, client_port_t, list);
 		event->dest.port = port->addr.port;
 		/* pass NULL as source client to avoid error bounce */
@@ -706,7 +733,7 @@
 			break;
 		num_ev++;
 	}
-	read_unlock(&client->ports_lock);
+	read_unlock(&dest_client->ports_lock);
 	snd_seq_client_unlock(dest_client);
 	event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
 	return (err < 0) ? err : num_ev;
diff -Nru a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
--- a/sound/core/seq/seq_fifo.c	Sun Feb 29 12:58:39 2004
+++ b/sound/core/seq/seq_fifo.c	Sun Feb 29 12:58:39 2004
@@ -171,10 +171,12 @@
 {
 	snd_seq_event_cell_t *cell;
 	unsigned long flags;
+	wait_queue_t wait;
 
 	snd_assert(f != NULL, return -EINVAL);
 
 	*cellp = NULL;
+	init_waitqueue_entry(&wait, current);
 	spin_lock_irqsave(&f->lock, flags);
 	while ((cell = fifo_cell_out(f)) == NULL) {
 		if (nonblock) {
@@ -182,17 +184,19 @@
 			spin_unlock_irqrestore(&f->lock, flags);
 			return -EAGAIN;
 		}
-		spin_unlock(&f->lock);
-		interruptible_sleep_on(&f->input_sleep);
-		spin_lock(&f->lock);
-
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&f->input_sleep, &wait);
+		spin_unlock_irq(&f->lock);
+		schedule();
+		spin_lock_irq(&f->lock);
+		remove_wait_queue(&f->input_sleep, &wait);
 		if (signal_pending(current)) {
 			spin_unlock_irqrestore(&f->lock, flags);
 			return -ERESTARTSYS;
 		}
 	}
-	*cellp = cell;
 	spin_unlock_irqrestore(&f->lock, flags);
+	*cellp = cell;
 
 	return 0;
 }
diff -Nru a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
--- a/sound/core/seq/seq_memory.c	Sun Feb 29 12:58:38 2004
+++ b/sound/core/seq/seq_memory.c	Sun Feb 29 12:58:38 2004
@@ -220,12 +220,14 @@
 	snd_seq_event_cell_t *cell;
 	unsigned long flags;
 	int err = -EAGAIN;
+	wait_queue_t wait;
 
 	if (pool == NULL)
 		return -EINVAL;
 
 	*cellp = NULL;
 
+	init_waitqueue_entry(&wait, current);
 	spin_lock_irqsave(&pool->lock, flags);
 	if (pool->ptr == NULL) {	/* not initialized */
 		snd_printd("seq: pool is not initialized\n");
@@ -234,9 +236,12 @@
 	}
 	while (pool->free == NULL && ! nonblock && ! pool->closing) {
 
-		spin_unlock(&pool->lock);
-		interruptible_sleep_on(&pool->output_sleep);
-		spin_lock(&pool->lock);
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&pool->output_sleep, &wait);
+		spin_unlock_irq(&pool->lock);
+		schedule();
+		spin_lock_irq(&pool->lock);
+		remove_wait_queue(&pool->output_sleep, &wait);
 		/* interrupted? */
 		if (signal_pending(current)) {
 			err = -ERESTARTSYS;
diff -Nru a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
--- a/sound/core/seq/seq_midi.c	Sun Feb 29 12:58:39 2004
+++ b/sound/core/seq/seq_midi.c	Sun Feb 29 12:58:39 2004
@@ -285,7 +285,7 @@
 	cinfo.client = client->seq_client;
 	cinfo.type = KERNEL_CLIENT;
 	name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI";
-	snprintf(cinfo.name, sizeof(cinfo.name), "Rawmidi %d - %s", card->number, name);
+	snprintf(cinfo.name, sizeof(cinfo.name), "%s - Rawmidi %d", name, card->number);
 	return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
 }
 
diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
--- a/sound/drivers/mpu401/mpu401_uart.c	Sun Feb 29 12:58:39 2004
+++ b/sound/drivers/mpu401/mpu401_uart.c	Sun Feb 29 12:58:39 2004
@@ -528,9 +528,9 @@
 	mpu->irq = irq;
 	mpu->irq_flags = irq_flags;
 	if (card->shortname[0])
-		snprintf(rmidi->name, sizeof(rmidi->name), "%s MPU-401", card->shortname);
+		snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname);
 	else
-		sprintf(rmidi->name, "MPU-401 (UART) %d-%d", card->number, device);
+		sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device);
 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output);
 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input);
 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
diff -Nru a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile
--- a/sound/drivers/opl3/Makefile	Sun Feb 29 12:58:38 2004
+++ b/sound/drivers/opl3/Makefile	Sun Feb 29 12:58:38 2004
@@ -10,36 +10,66 @@
 endif
 
 OPL3_OBJS = snd-opl3-lib.o
-ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
-OPL3_OBJS += snd-opl3-synth.o
-endif
+OPL3_SYNTH_OBJS = snd-opl3-synth.o
+
+#
+# this function returns:
+#   "m" - CONFIG_SND_SEQUENCER is m
+#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+#   otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ALS100) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_ALS100)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_AZT2320) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_AZT2320)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_AZT3328) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_AZT3328)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_DT019X) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_DT019X)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_ES18XX) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_ES18XX)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_OPL3SA2) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_OPL3SA2)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_AD1816A) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_AD1816A)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_CS4232) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_CS4232)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_PC98_CS4232) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_PC98_CS4232)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_CS4236) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_CS4236)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_ES1688) += $(OPL3_OBJS)
-obj-$(CONFIG_SND_GUSEXTREME) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_OPTI92X_CS4231)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_OPTI93X) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_OPTI93X)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_SB8) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_SB8)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_SB16) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_SB16)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_SBAWE) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_WAVEFRONT) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_WAVEFRONT)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_ALS4000) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_ALS4000)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_CMIPCI) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_CMIPCI)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_CS4281) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_CS4281)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_ES1938) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_ES1938)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_FM801) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_FM801)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_SONICVIBES) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += $(OPL3_SYNTH_OBJS)
 obj-$(CONFIG_SND_YMFPCI) += $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += $(OPL3_SYNTH_OBJS)
 
 obj-m := $(sort $(obj-m))
diff -Nru a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile
--- a/sound/drivers/opl4/Makefile	Sun Feb 29 12:58:39 2004
+++ b/sound/drivers/opl4/Makefile	Sun Feb 29 12:58:39 2004
@@ -7,9 +7,19 @@
 snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
 
 OPL4_OBJS := snd-opl4-lib.o
-ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
-  OPL4_OBJS += snd-opl4-synth.o
-endif
+OPL4_SYNTH_OBJS := snd-opl4-synth.o
+
+#
+# this function returns:
+#   "m" - CONFIG_SND_SEQUENCER is m
+#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+#   otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
 
 obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL4_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += $(OPL4_SYNTH_OBJS)
 obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL4_OBJS)
+obj-$(call sequencer,$CONFIG_SND_OPTI92X_CS4231)) += $(OPL4_SYNTH_OBJS)
+
+obj-m := $(sort $(obj-m))
diff -Nru a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
--- a/sound/drivers/vx/vx_core.c	Sun Feb 29 12:58:39 2004
+++ b/sound/drivers/vx/vx_core.c	Sun Feb 29 12:58:39 2004
@@ -355,11 +355,12 @@
  */
 int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh)
 {
+	unsigned long flags;
 	int err;
 
-	spin_lock_bh(&chip->lock);
+	spin_lock_irqsave(&chip->lock, flags);
 	err = vx_send_msg_nolock(chip, rmh);
-	spin_unlock_bh(&chip->lock);
+	spin_unlock_irqrestore(&chip->lock, flags);
 	return err;
 }
 
@@ -414,11 +415,12 @@
  */
 int vx_send_rih(vx_core_t *chip, int cmd)
 {
+	unsigned long flags;
 	int err;
 
-	spin_lock_bh(&chip->lock);
+	spin_lock_irqsave(&chip->lock, flags);
 	err = vx_send_rih_nolock(chip, cmd);
-	spin_unlock_bh(&chip->lock);
+	spin_unlock_irqrestore(&chip->lock, flags);
 	return err;
 }
 
diff -Nru a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
--- a/sound/drivers/vx/vx_mixer.c	Sun Feb 29 12:58:38 2004
+++ b/sound/drivers/vx/vx_mixer.c	Sun Feb 29 12:58:38 2004
@@ -919,7 +919,7 @@
 	if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0)
 		return err;
 	/* VU, peak, saturation meters */
-	for (c = 0; c < 1; c++) {
+	for (c = 0; c < 2; c++) {
 		static char *dir[2] = { "Output", "Input" };
 		for (i = 0; i < chip->hw->num_ins; i++) {
 			int val = (i * 2) | (c << 8);
diff -Nru a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
--- a/sound/drivers/vx/vx_pcm.c	Sun Feb 29 12:58:38 2004
+++ b/sound/drivers/vx/vx_pcm.c	Sun Feb 29 12:58:38 2004
@@ -561,7 +561,7 @@
 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
 	.rate_min =		5000,
 	.rate_max =		48000,
-	.channels_min =		2,
+	.channels_min =		1,
 	.channels_max =		2,
 	.buffer_bytes_max =	(128*1024),
 	.period_bytes_min =	126,
@@ -958,7 +958,7 @@
 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
 	.rate_min =		5000,
 	.rate_max =		48000,
-	.channels_min =		2,
+	.channels_min =		1,
 	.channels_max =		2,
 	.buffer_bytes_max =	(128*1024),
 	.period_bytes_min =	126,
diff -Nru a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
--- a/sound/i2c/other/Makefile	Sun Feb 29 12:58:38 2004
+++ b/sound/i2c/other/Makefile	Sun Feb 29 12:58:38 2004
@@ -3,10 +3,12 @@
 # Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
 #
 
+snd-ak4117-objs := ak4117.o
 snd-ak4xxx-adda-objs := ak4xxx-adda.o
 snd-tea575x-tuner-objs := tea575x-tuner.o
 
 # Module Dependency
+obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
 obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
 obj-$(CONFIG_SND_ICE1724) += snd-ak4xxx-adda.o
 obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff -Nru a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/i2c/other/ak4117.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,561 @@
+/*
+ *  Routines for control of the AK4117 via 4-wire serial interface
+ *  IEC958 (S/PDIF) receiver by Asahi Kasei
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ak4117.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("AK4117 IEC958 (S/PDIF) receiver by Asahi Kasei");
+MODULE_LICENSE("GPL");
+
+#define chip_t ak4117_t
+
+#define AK4117_ADDR			0x00 /* fixed address */
+
+static void snd_ak4117_timer(unsigned long data);
+
+static void reg_write(ak4117_t *ak4117, unsigned char reg, unsigned char val)
+{
+	ak4117->write(ak4117->private_data, reg, val);
+	if (reg < sizeof(ak4117->regmap))
+		ak4117->regmap[reg] = val;
+}
+
+static inline unsigned char reg_read(ak4117_t *ak4117, unsigned char reg)
+{
+	return ak4117->read(ak4117->private_data, reg);
+}
+
+#if 0
+static void reg_dump(ak4117_t *ak4117)
+{
+	int i;
+
+	printk("AK4117 REG DUMP:\n");
+	for (i = 0; i < 0x1b; i++)
+		printk("reg[%02x] = %02x (%02x)\n", i, reg_read(ak4117, i), i < sizeof(ak4117->regmap) ? ak4117->regmap[i] : 0);
+}
+#endif
+
+static void snd_ak4117_free(ak4117_t *chip)
+{
+	del_timer(&chip->timer);
+	snd_magic_kfree(chip);
+}
+
+static int snd_ak4117_dev_free(snd_device_t *device)
+{
+	ak4117_t *chip = snd_magic_cast(ak4117_t, device->device_data, return -ENXIO);
+	snd_ak4117_free(chip);
+	return 0;
+}
+
+int snd_ak4117_create(snd_card_t *card, ak4117_read_t *read, ak4117_write_t *write,
+		      unsigned char pgm[5], void *private_data, ak4117_t **r_ak4117)
+{
+	ak4117_t *chip;
+	int err = 0;
+	unsigned char reg;
+	static snd_device_ops_t ops = {
+		.dev_free =     snd_ak4117_dev_free,
+	};
+
+	chip = (ak4117_t *)snd_magic_kcalloc(ak4117_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->lock);
+	chip->card = card;
+	chip->read = read;
+	chip->write = write;
+	chip->private_data = private_data;
+	init_timer(&chip->timer);
+	chip->timer.data = (unsigned long)chip;
+	chip->timer.function = snd_ak4117_timer;
+
+	for (reg = 0; reg < 5; reg++)
+		chip->regmap[reg] = pgm[reg];
+	snd_ak4117_reinit(chip);
+
+	chip->rcs0 = reg_read(chip, AK4117_REG_RCS0) & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
+	chip->rcs1 = reg_read(chip, AK4117_REG_RCS1);
+	chip->rcs2 = reg_read(chip, AK4117_REG_RCS2);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+		goto __fail;
+
+	if (r_ak4117)
+		*r_ak4117 = chip;
+	return 0;
+
+      __fail:
+	snd_ak4117_free(chip);
+	return err < 0 ? err : -EIO;
+}
+
+void snd_ak4117_reg_write(ak4117_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+	if (reg >= 5)
+		return;
+	reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
+}
+
+void snd_ak4117_reinit(ak4117_t *chip)
+{
+	unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg;
+
+	del_timer(&chip->timer);
+	chip->init = 1;
+	/* bring the chip to reset state and powerdown state */
+	reg_write(chip, AK4117_REG_PWRDN, 0);
+	udelay(200);
+	/* release reset, but leave powerdown */
+	reg_write(chip, AK4117_REG_PWRDN, (old | AK4117_RST) & ~AK4117_PWN);
+	udelay(200);
+	for (reg = 1; reg < 5; reg++)
+		reg_write(chip, reg, chip->regmap[reg]);
+	/* release powerdown, everything is initialized now */
+	reg_write(chip, AK4117_REG_PWRDN, old | AK4117_RST | AK4117_PWN);
+	chip->init = 0;
+	chip->timer.expires = 1 + jiffies;
+	add_timer(&chip->timer);
+}
+
+static unsigned int external_rate(unsigned char rcs1)
+{
+	switch (rcs1 & (AK4117_FS0|AK4117_FS1|AK4117_FS2|AK4117_FS3)) {
+	case AK4117_FS_32000HZ: return 32000;
+	case AK4117_FS_44100HZ: return 44100;
+	case AK4117_FS_48000HZ: return 48000;
+	case AK4117_FS_88200HZ: return 88200;
+	case AK4117_FS_96000HZ: return 96000;
+	case AK4117_FS_176400HZ: return 176400;
+	case AK4117_FS_192000HZ: return 192000;
+	default:		return 0;
+	}
+}
+
+static int snd_ak4117_in_error_info(snd_kcontrol_t *kcontrol,
+				    snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = LONG_MAX;
+	return 0;
+}
+
+static int snd_ak4117_in_error_get(snd_kcontrol_t *kcontrol,
+				   snd_ctl_elem_value_t *ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+	long *ptr;
+
+	spin_lock_irq(&chip->lock);
+	ptr = (long *)(((char *)chip) + kcontrol->private_value);
+	ucontrol->value.integer.value[0] = *ptr;
+	*ptr = 0;
+	spin_unlock_irq(&chip->lock);
+	return 0;
+}
+
+static int snd_ak4117_in_bit_info(snd_kcontrol_t *kcontrol,
+				  snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ak4117_in_bit_get(snd_kcontrol_t *kcontrol,
+				 snd_ctl_elem_value_t *ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char reg = kcontrol->private_value & 0xff;
+	unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
+	unsigned char inv = (kcontrol->private_value >> 31) & 1;
+
+	ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
+	return 0;
+}
+
+static int snd_ak4117_rx_info(snd_kcontrol_t *kcontrol,
+			      snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ak4117_rx_get(snd_kcontrol_t *kcontrol,
+			     snd_ctl_elem_value_t *ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = (chip->regmap[AK4117_REG_IO] & AK4117_IPS) ? 1 : 0;
+	return 0;
+}
+
+static int snd_ak4117_rx_put(snd_kcontrol_t *kcontrol,
+			     snd_ctl_elem_value_t *ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+	int change;
+	u8 old_val;
+	
+	spin_lock_irq(&chip->lock);
+	old_val = chip->regmap[AK4117_REG_IO];
+	change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0);
+	if (change)
+		reg_write(chip, AK4117_REG_IO, (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0));
+	spin_unlock_irq(&chip->lock);
+	return change;
+}
+
+static int snd_ak4117_rate_info(snd_kcontrol_t *kcontrol,
+				snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 192000;
+	return 0;
+}
+
+static int snd_ak4117_rate_get(snd_kcontrol_t *kcontrol,
+			       snd_ctl_elem_value_t *ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4117_REG_RCS1));
+	return 0;
+}
+
+static int snd_ak4117_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ak4117_spdif_get(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned i;
+
+	for (i = 0; i < AK4117_REG_RXCSB_SIZE; i++)
+		ucontrol->value.iec958.status[i] = reg_read(chip, AK4117_REG_RXCSB0 + i);
+	return 0;
+}
+
+static int snd_ak4117_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ak4117_spdif_mask_get(snd_kcontrol_t * kcontrol,
+				      snd_ctl_elem_value_t * ucontrol)
+{
+	memset(ucontrol->value.iec958.status, 0xff, AK4117_REG_RXCSB_SIZE);
+	return 0;
+}
+
+static int snd_ak4117_spdif_pinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0xffff;
+	uinfo->count = 4;
+	return 0;
+}
+
+static int snd_ak4117_spdif_pget(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_value_t * ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned short tmp;
+
+	ucontrol->value.integer.value[0] = 0xf8f2;
+	ucontrol->value.integer.value[1] = 0x4e1f;
+	tmp = reg_read(chip, AK4117_REG_Pc0) | (reg_read(chip, AK4117_REG_Pc1) << 8);
+	ucontrol->value.integer.value[2] = tmp;
+	tmp = reg_read(chip, AK4117_REG_Pd0) | (reg_read(chip, AK4117_REG_Pd1) << 8);
+	ucontrol->value.integer.value[3] = tmp;
+	return 0;
+}
+
+static int snd_ak4117_spdif_qinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = AK4117_REG_QSUB_SIZE;
+	return 0;
+}
+
+static int snd_ak4117_spdif_qget(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_value_t * ucontrol)
+{
+	ak4117_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned i;
+
+	for (i = 0; i < AK4117_REG_QSUB_SIZE; i++)
+		ucontrol->value.bytes.data[i] = reg_read(chip, AK4117_REG_QSUB_ADDR + i);
+	return 0;
+}
+
+/* Don't forget to change AK4117_CONTROLS define!!! */
+static snd_kcontrol_new_t snd_ak4117_iec958_controls[] = {
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Parity Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_in_error_info,
+	.get =		snd_ak4117_in_error_get,
+	.private_value = offsetof(ak4117_t, parity_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 V-Bit Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_in_error_info,
+	.get =		snd_ak4117_in_error_get,
+	.private_value = offsetof(ak4117_t, v_bit_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 C-CRC Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_in_error_info,
+	.get =		snd_ak4117_in_error_get,
+	.private_value = offsetof(ak4117_t, ccrc_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Q-CRC Errors",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_in_error_info,
+	.get =		snd_ak4117_in_error_get,
+	.private_value = offsetof(ak4117_t, qcrc_errors),
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 External Rate",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_rate_info,
+	.get =		snd_ak4117_rate_get,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.info =		snd_ak4117_spdif_mask_info,
+	.get =		snd_ak4117_spdif_mask_get,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_spdif_info,
+	.get =		snd_ak4117_spdif_get,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Preample Capture Default",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_spdif_pinfo,
+	.get =		snd_ak4117_spdif_pget,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Q-subcode Capture Default",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_spdif_qinfo,
+	.get =		snd_ak4117_spdif_qget,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Audio",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_in_bit_info,
+	.get =		snd_ak4117_in_bit_get,
+	.private_value = (1<<31) | (3<<8) | AK4117_REG_RCS0,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 Non-PCM Bitstream",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_in_bit_info,
+	.get =		snd_ak4117_in_bit_get,
+	.private_value = (5<<8) | AK4117_REG_RCS1,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"IEC958 DTS Bitstream",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		snd_ak4117_in_bit_info,
+	.get =		snd_ak4117_in_bit_get,
+	.private_value = (6<<8) | AK4117_REG_RCS1,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		"AK4117 Input Select",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+	.info =		snd_ak4117_rx_info,
+	.get =		snd_ak4117_rx_get,
+	.put =		snd_ak4117_rx_put,
+}
+};
+
+int snd_ak4117_build(ak4117_t *ak4117, snd_pcm_substream_t *cap_substream)
+{
+	snd_kcontrol_t *kctl;
+	unsigned int idx;
+	int err;
+
+	snd_assert(cap_substream, return -EINVAL);
+	ak4117->substream = cap_substream;
+	for (idx = 0; idx < AK4117_CONTROLS; idx++) {
+		kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.device = cap_substream->pcm->device;
+		kctl->id.subdevice = cap_substream->number;
+		err = snd_ctl_add(ak4117->card, kctl);
+		if (err < 0)
+			return err;
+		ak4117->kctls[idx] = kctl;
+	}
+	return 0;
+}
+
+int snd_ak4117_external_rate(ak4117_t *ak4117)
+{
+	unsigned char rcs1;
+
+	rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
+	return external_rate(rcs1);
+}
+
+int snd_ak4117_check_rate_and_errors(ak4117_t *ak4117, unsigned int flags)
+{
+	snd_pcm_runtime_t *runtime = ak4117->substream ? ak4117->substream->runtime : NULL;
+	unsigned long _flags;
+	int res = 0;
+	unsigned char rcs0, rcs1, rcs2;
+	unsigned char c0, c1;
+
+	rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
+	if (flags & AK4117_CHECK_NO_STAT)
+		goto __rate;
+	rcs0 = reg_read(ak4117, AK4117_REG_RCS0);
+	rcs2 = reg_read(ak4117, AK4117_REG_RCS2);
+	// printk("AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x\n", rcs0, rcs1, rcs2);
+	spin_lock_irqsave(&ak4117->lock, _flags);
+	if (rcs0 & AK4117_PAR)
+		ak4117->parity_errors++;
+	if (rcs0 & AK4117_V)
+		ak4117->v_bit_errors++;
+	if (rcs2 & AK4117_CCRC)
+		ak4117->ccrc_errors++;
+	if (rcs2 & AK4117_QCRC)
+		ak4117->qcrc_errors++;
+	c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^
+                     (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK));
+	c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^
+	             (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
+	ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
+	ak4117->rcs1 = rcs1;
+	ak4117->rcs2 = rcs2;
+	spin_unlock_irqrestore(&ak4117->lock, _flags);
+
+	if (rcs0 & AK4117_PAR)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[0]->id);
+	if (rcs0 & AK4117_V)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[1]->id);
+	if (rcs2 & AK4117_CCRC)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[2]->id);
+	if (rcs2 & AK4117_QCRC)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[3]->id);
+
+	/* rate change */
+	if (c1 & 0x0f)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[4]->id);
+
+	if ((c1 & AK4117_PEM) | (c0 & AK4117_CINT))
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[6]->id);
+	if (c0 & AK4117_QINT)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[8]->id);
+
+	if (c0 & AK4117_AUDION)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[9]->id);
+	if (c1 & AK4117_NPCM)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[10]->id);
+	if (c1 & AK4117_DTSCD)
+		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[11]->id);
+		
+	if (ak4117->change_callback && (c0 | c1) != 0)
+		ak4117->change_callback(ak4117, c0, c1);
+
+      __rate:
+	/* compare rate */
+	res = external_rate(rcs1);
+	if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) {
+		snd_pcm_stream_lock_irqsave(ak4117->substream, _flags);
+		if (snd_pcm_running(ak4117->substream)) {
+			// printk("rate changed (%i <- %i)\n", runtime->rate, res);
+			snd_pcm_stop(ak4117->substream, SNDRV_PCM_STATE_DRAINING);
+			wake_up(&runtime->sleep);
+			res = 1;
+		}
+		snd_pcm_stream_unlock_irqrestore(ak4117->substream, _flags);
+	}
+	return res;
+}
+
+static void snd_ak4117_timer(unsigned long data)
+{
+	ak4117_t *chip = snd_magic_cast(ak4117_t, (void *)data, return);
+
+	if (chip->init)
+		return;
+	snd_ak4117_check_rate_and_errors(chip, 0);
+	chip->timer.expires = 1 + jiffies;
+	add_timer(&chip->timer);
+}
+
+EXPORT_SYMBOL(snd_ak4117_create);
+EXPORT_SYMBOL(snd_ak4117_reg_write);
+EXPORT_SYMBOL(snd_ak4117_reinit);
+EXPORT_SYMBOL(snd_ak4117_build);
+EXPORT_SYMBOL(snd_ak4117_external_rate);
+EXPORT_SYMBOL(snd_ak4117_check_rate_and_errors);
diff -Nru a/sound/pci/Kconfig b/sound/pci/Kconfig
--- a/sound/pci/Kconfig	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/Kconfig	Sun Feb 29 12:58:39 2004
@@ -55,6 +55,12 @@
 	help
 	  Say 'Y' or 'M' to include support for Korg 1212IO.
 
+config SND_MIXART
+	tristate "Digigram miXart"
+	depends on SND
+	help
+	  Say 'Y' or 'M' to include support for Digigram miXart soundcard.
+
 config SND_NM256
 	tristate "NeoMagic NM256AV/ZX"
 	depends on SND
@@ -156,7 +162,8 @@
 
 config CONFIG_SND_FM801_TEA575X
 	tristate "ForteMedia FM801 + TEA5757 tuner"
-	depends on SND_FM801 && CONFIG_VIDEO_DEV
+	depends on SND_FM801
+        select CONFIG_VIDEO_DEV
 	help
 	  Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards
           with TEA5757 tuner connected to GPIO1-3 pins (Media Forte SF256-PCS-02).
diff -Nru a/sound/pci/Makefile b/sound/pci/Makefile
--- a/sound/pci/Makefile	Sun Feb 29 12:58:38 2004
+++ b/sound/pci/Makefile	Sun Feb 29 12:58:38 2004
@@ -38,4 +38,16 @@
 obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
 obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
 
-obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ ice1712/ vx222/
+obj-$(CONFIG_SND) += \
+	ac97/ \
+	ali5451/ \
+	cs46xx/ \
+	emu10k1/ \
+	ice1712/ \
+	korg1212/ \
+	mixart/ \
+	nm256/ \
+	rme9652/ \
+	trident/ \
+	ymfpci/ \
+	vx222/
diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
--- a/sound/pci/ac97/ac97_codec.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/ac97/ac97_codec.c	Sun Feb 29 12:58:39 2004
@@ -100,6 +100,7 @@
 { 0x41445361, 0xffffffff, "AD1886",		patch_ad1886,	NULL },
 { 0x41445362, 0xffffffff, "AD1887",		patch_ad1881,	NULL },
 { 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881,	NULL },
+{ 0x41445368, 0xffffffff, "AD1888",		patch_ad1888,	NULL },
 { 0x41445370, 0xffffffff, "AD1980",		patch_ad1980,	NULL },
 { 0x41445372, 0xffffffff, "AD1981A",		patch_ad1981a,	NULL },
 { 0x41445374, 0xffffffff, "AD1981B",		patch_ad1981b,	NULL },
@@ -1526,38 +1527,40 @@
 	return 0;
 }
 
-static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate)
+static int snd_ac97_test_rate(ac97_t *ac97, int reg, int shadow_reg, int rate)
 {
 	unsigned short val;
 	unsigned int tmp;
 
 	tmp = ((unsigned int)rate * ac97->bus->clock) / 48000;
 	snd_ac97_write_cache(ac97, reg, tmp & 0xffff);
+	if (shadow_reg)
+		snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff);
 	val = snd_ac97_read(ac97, reg);
 	return val == (tmp & 0xffff);
 }
 
-static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result)
+static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result)
 {
 	unsigned int result = 0;
 
 	/* test a non-standard rate */
-	if (snd_ac97_test_rate(ac97, reg, 11000))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000))
 		result |= SNDRV_PCM_RATE_CONTINUOUS;
 	/* let's try to obtain standard rates */
-	if (snd_ac97_test_rate(ac97, reg, 8000))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000))
 		result |= SNDRV_PCM_RATE_8000;
-	if (snd_ac97_test_rate(ac97, reg, 11025))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025))
 		result |= SNDRV_PCM_RATE_11025;
-	if (snd_ac97_test_rate(ac97, reg, 16000))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000))
 		result |= SNDRV_PCM_RATE_16000;
-	if (snd_ac97_test_rate(ac97, reg, 22050))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050))
 		result |= SNDRV_PCM_RATE_22050;
-	if (snd_ac97_test_rate(ac97, reg, 32000))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000))
 		result |= SNDRV_PCM_RATE_32000;
-	if (snd_ac97_test_rate(ac97, reg, 44100))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100))
 		result |= SNDRV_PCM_RATE_44100;
-	if (snd_ac97_test_rate(ac97, reg, 48000))
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000))
 		result |= SNDRV_PCM_RATE_48000;
 	*r_result = result;
 }
@@ -1866,8 +1869,8 @@
 	if (ac97->ext_id & 0x0189)	/* L/R, MIC, SDAC, LDAC VRA support */
 		snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189);
 	if (ac97->ext_id & AC97_EI_VRA) {	/* VRA support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]);
-		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]);
 	} else {
 		ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
 		ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
@@ -1884,16 +1887,16 @@
 						SNDRV_PCM_RATE_32000;
 	}
 	if (ac97->ext_id & AC97_EI_VRM) {	/* MIC VRA support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]);
 	} else {
 		ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
 	}
 	if (ac97->ext_id & AC97_EI_SDAC) {	/* SDAC support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
 		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
 	}
 	if (ac97->ext_id & AC97_EI_LDAC) {	/* LDAC support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
 		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
 	}
 	/* additional initializations */
@@ -2112,6 +2115,8 @@
 {
 	/* FIXME: error checks.. */
 	if (remove_master) {
+		if (ctl_find(ac97, "Headphone Playback Switch") == NULL)
+			return 0;
 		snd_ac97_remove_ctl(ac97, "Master Playback Switch");
 		snd_ac97_remove_ctl(ac97, "Master Playback Volume");
 	} else {
diff -Nru a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
--- a/sound/pci/ac97/ac97_patch.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/ac97/ac97_patch.c	Sun Feb 29 12:58:39 2004
@@ -652,7 +652,7 @@
 
 static const snd_kcontrol_new_t snd_ac97_controls_ad1885[] = {
 	AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0),
-	AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0),
+	/* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */
 	AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0),
 	AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0),
 };
@@ -682,6 +682,9 @@
 	jack = snd_ac97_read(ac97, AC97_AD_JACK_SPDIF);
 	snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, jack | 0x0300);
 
+	/* set default */
+	snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404);
+
 	ac97->build_ops = &patch_ad1885_build_ops;
 	return 0;
 }
@@ -799,7 +802,7 @@
 	return 0;
 }
 
-static int snd_ac97_ad1980_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int snd_ac97_ad1888_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	uinfo->count = 1;
@@ -808,7 +811,7 @@
 	return 0;
 }
 
-static int snd_ac97_ad1980_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
+static int snd_ac97_ad1888_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
 {
 	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
 	unsigned short val;
@@ -818,7 +821,7 @@
 	return 0;
 }
 
-static int snd_ac97_ad1980_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_ac97_ad1888_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
 	unsigned short val;
@@ -829,7 +832,7 @@
 				    AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val);
 }
 
-static int snd_ac97_ad1980_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int snd_ac97_ad1888_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
 	static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"};
 
@@ -842,7 +845,7 @@
 	return 0;
 }
 
-static int snd_ac97_ad1980_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
+static int snd_ac97_ad1888_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
 {
 	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
 	unsigned short val;
@@ -855,7 +858,7 @@
 	return 0;
 }
 
-static int snd_ac97_ad1980_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_ac97_ad1888_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
 	unsigned short val;
@@ -871,51 +874,47 @@
 				    AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val);
 }
 
-static const snd_kcontrol_new_t snd_ac97_ad1980_controls[] = {
+static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Exchange Front/Surround",
-		.info = snd_ac97_ad1980_lohpsel_info,
-		.get = snd_ac97_ad1980_lohpsel_get,
-		.put = snd_ac97_ad1980_lohpsel_put
+		.info = snd_ac97_ad1888_lohpsel_info,
+		.get = snd_ac97_ad1888_lohpsel_get,
+		.put = snd_ac97_ad1888_lohpsel_put
 	},
 	AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Downmix",
-		.info = snd_ac97_ad1980_downmix_info,
-		.get = snd_ac97_ad1980_downmix_get,
-		.put = snd_ac97_ad1980_downmix_put
+		.info = snd_ac97_ad1888_downmix_info,
+		.get = snd_ac97_ad1888_downmix_get,
+		.put = snd_ac97_ad1888_downmix_put
 	},
 	AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0),
 	AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0),
 };
 
-static int patch_ad1980_specific(ac97_t *ac97)
+static int patch_ad1888_specific(ac97_t *ac97)
 {
-	int err;
-
 	/* rename 0x04 as "Master" and 0x02 as "Master Surround" */
 	snd_ac97_rename_ctl(ac97, "Master Playback Switch", "Master Surround Playback Switch");
 	snd_ac97_rename_ctl(ac97, "Master Playback Volume", "Master Surround Playback Volume");
 	snd_ac97_rename_ctl(ac97, "Headphone Playback Switch", "Master Playback Switch");
 	snd_ac97_rename_ctl(ac97, "Headphone Playback Volume", "Master Playback Volume");
-	if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
-		return err;
-	return patch_build_controls(ac97, snd_ac97_ad1980_controls, ARRAY_SIZE(snd_ac97_ad1980_controls));
+	return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls));
 }
 
-static struct snd_ac97_build_ops patch_ad1980_build_ops = {
+static struct snd_ac97_build_ops patch_ad1888_build_ops = {
 	.build_post_spdif = patch_ad198x_post_spdif,
-	.build_specific = patch_ad1980_specific
+	.build_specific = patch_ad1888_specific
 };
 
-int patch_ad1980(ac97_t * ac97)
+int patch_ad1888(ac97_t * ac97)
 {
 	unsigned short misc;
 	
 	patch_ad1881(ac97);
-	ac97->build_ops = &patch_ad1980_build_ops;
+	ac97->build_ops = &patch_ad1888_build_ops;
 	/* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */
 	/* it seems that most vendors connect line-out connector to headphone out of AC'97 */
 	/* AD-compatible mode */
@@ -930,6 +929,27 @@
 	return 0;
 }
 
+static int patch_ad1980_specific(ac97_t *ac97)
+{
+	int err;
+
+	if ((err = patch_ad1888_specific(ac97)) < 0)
+		return err;
+	return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+}
+
+static struct snd_ac97_build_ops patch_ad1980_build_ops = {
+	.build_post_spdif = patch_ad198x_post_spdif,
+	.build_specific = patch_ad1980_specific
+};
+
+int patch_ad1980(ac97_t * ac97)
+{
+	patch_ad1888(ac97);
+	ac97->build_ops = &patch_ad1980_build_ops;
+	return 0;
+}
+
 static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = {
 	AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0),
 	AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0)
@@ -1014,8 +1034,7 @@
                                       ucontrol->value.integer.value[0] ? (1 << 10) : 0);
         if (change) {
                 /* GPIO0 write for mic */
-                snd_ac97_update_bits(ac97, 0x76, 0x01,
-                                     ucontrol->value.integer.value[0] ? 0 : 0x01);
+                snd_ac97_update_bits(ac97, 0x76, 0x01, 0x01);
                 /* GPIO0 high for mic */
                 snd_ac97_update_bits(ac97, 0x78, 0x100,
                                      ucontrol->value.integer.value[0] ? 0 : 0x100);
@@ -1092,10 +1111,7 @@
 		mic_off = snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10);
 		/* GPIO0 direction */
 		val = snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP);
-		if (mic_off)
-			val &= ~0x01;
-		else
-			val |= 0x01;
+		val |= 0x01;
 		snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP, val);
 		val = snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS);
 		if (mic_off)
@@ -1111,10 +1127,36 @@
 	return 0;
 }
 
+static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+        ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
+        return 0;
+}
+
+static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+        int change;
+
+	snd_ac97_update_bits(ac97, 0x7a, 1 << 12, /* misc control; vrefout disable */
+			     ucontrol->value.integer.value[0] ? (1 << 12) : 0);
+	change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
+				      ucontrol->value.integer.value[0] ? (1 << 10) : 0);
+	return change;
+}
+
+
 static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = {
 	AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
 	AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0),
-	AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Mic As Center/LFE",
+		.info = snd_ac97_info_single,
+		.get = snd_ac97_alc655_mic_get,
+		.put = snd_ac97_alc655_mic_put,
+	},
 };
 
 static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
@@ -1187,15 +1229,21 @@
 
 int patch_alc655(ac97_t * ac97)
 {
+	unsigned int val;
+
 	ac97->spec.dev_flags = (ac97->id == 0x414c4780); /* ALC658 */
 
 	ac97->build_ops = &patch_alc655_ops;
 
-	/* enable spdif in */
-	snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK,
-			     snd_ac97_read(ac97, AC97_ALC650_MULTICH) | 0x8000);
-	snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK,
-			     snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x02);
+	/* adjust default values */
+	val = snd_ac97_read(ac97, 0x7a); /* misc control */
+	val |= (1 << 1); /* spdif input pin */
+	val &= ~(1 << 12); /* vref enable */
+	snd_ac97_write_cache(ac97, 0x7a, val);
+	val = snd_ac97_read(ac97, AC97_ALC650_MULTICH);
+	val |= (1 << 15); /* enable spdif in */
+	val &= ~(1 << 10); /* disable center on mic */
+	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val);
 
 	/* full DAC volume */
 	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
diff -Nru a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
--- a/sound/pci/ac97/ac97_patch.h	Sun Feb 29 12:58:38 2004
+++ b/sound/pci/ac97/ac97_patch.h	Sun Feb 29 12:58:38 2004
@@ -41,6 +41,7 @@
 int patch_ad1881(ac97_t * ac97);
 int patch_ad1885(ac97_t * ac97);
 int patch_ad1886(ac97_t * ac97);
+int patch_ad1888(ac97_t * ac97);
 int patch_ad1980(ac97_t * ac97);
 int patch_ad1981a(ac97_t * ac97);
 int patch_ad1981b(ac97_t * ac97);
diff -Nru a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
--- a/sound/pci/ac97/ac97_pcm.c	Sun Feb 29 12:58:38 2004
+++ b/sound/pci/ac97/ac97_pcm.c	Sun Feb 29 12:58:38 2004
@@ -31,6 +31,7 @@
 #include <sound/pcm.h>
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
 #include "ac97_patch.h"
 #include "ac97_id.h"
 #include "ac97_local.h"
@@ -176,6 +177,7 @@
 static int set_spdif_rate(ac97_t *ac97, unsigned short rate)
 {
 	unsigned short old, bits, reg, mask;
+	unsigned int sbits;
 
 	if (! (ac97->ext_id & AC97_EI_SPDIF))
 		return -ENODEV;
@@ -213,6 +215,26 @@
 	if (old != bits) {
 		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
 		snd_ac97_update_bits(ac97, reg, mask, bits);
+		/* update the internal spdif bits */
+		spin_lock(&ac97->reg_lock);
+		sbits = ac97->spdif_status;
+		if (sbits & IEC958_AES0_PROFESSIONAL) {
+			sbits &= ~IEC958_AES0_PRO_FS;
+			switch (rate) {
+			case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break;
+			case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break;
+			case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break;
+			}
+		} else {
+			sbits &= ~(IEC958_AES3_CON_FS << 24);
+			switch (rate) {
+			case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break;
+			case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break;
+			case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break;
+			}
+		}
+		ac97->spdif_status = sbits;
+		spin_unlock(&ac97->reg_lock);
 	}
 	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);
 	return 0;
diff -Nru a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
--- a/sound/pci/ac97/ak4531_codec.c	Sun Feb 29 12:58:38 2004
+++ b/sound/pci/ac97/ak4531_codec.c	Sun Feb 29 12:58:38 2004
@@ -285,7 +285,7 @@
 AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
 AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1),
 AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
-AK4531_INPUT_SW("Aux Input Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
+AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
 
 AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
 AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1),
diff -Nru a/sound/pci/als4000.c b/sound/pci/als4000.c
--- a/sound/pci/als4000.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/als4000.c	Sun Feb 29 12:58:39 2004
@@ -99,9 +99,11 @@
 MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
 MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard.");
 MODULE_PARM_SYNTAX(enable, SNDRV_INDEX_DESC);
+#ifdef SUPPORT_JOYSTICK
 MODULE_PARM(joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
 MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
 MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED);
+#endif
 
 #define chip_t sb_t
 
diff -Nru a/sound/pci/cmipci.c b/sound/pci/cmipci.c
--- a/sound/pci/cmipci.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/cmipci.c	Sun Feb 29 12:58:39 2004
@@ -3151,7 +3151,7 @@
 #ifdef SUPPORT_JOYSTICK
 	if (joystick_port[dev] > 0) {
 		if (joystick_port[dev] == 1) { /* auto-detect */
-			static int ports[] = { 0x200, 0x201, 0 };
+			static int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */
 			int i;
 			for (i = 0; ports[i]; i++) {
 				joystick_port[dev] = ports[i];
diff -Nru a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
--- a/sound/pci/ice1712/ice1712.c	Sun Feb 29 12:58:38 2004
+++ b/sound/pci/ice1712/ice1712.c	Sun Feb 29 12:58:38 2004
@@ -2375,6 +2375,7 @@
 	ice->omni = omni ? 1 : 0;
 	spin_lock_init(&ice->reg_lock);
 	init_MUTEX(&ice->gpio_mutex);
+	init_MUTEX(&ice->open_mutex);
 	ice->gpio.set_mask = snd_ice1712_set_gpio_mask;
 	ice->gpio.set_dir = snd_ice1712_set_gpio_dir;
 	ice->gpio.set_data = snd_ice1712_set_gpio_data;
diff -Nru a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
--- a/sound/pci/ice1712/ice1712.h	Sun Feb 29 12:58:38 2004
+++ b/sound/pci/ice1712/ice1712.h	Sun Feb 29 12:58:38 2004
@@ -335,6 +335,9 @@
 	unsigned short hoontech_boxconfig[4];
 	unsigned int cur_rate;		/* current rate */
 
+	struct semaphore open_mutex;
+	snd_pcm_substream_t *pcm_reserved[4];
+
 	unsigned int akm_codecs;
 	akm4xxx_t *akm;
 	struct snd_ice1712_spdif spdif;
diff -Nru a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
--- a/sound/pci/ice1712/ice1724.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/ice1712/ice1724.c	Sun Feb 29 12:58:39 2004
@@ -237,6 +237,18 @@
 				if (ice->capture_pro_substream)
 					snd_pcm_period_elapsed(ice->capture_pro_substream);
 			}
+			if (mtstat & VT1724_MULTI_PDMA1) {
+				if (ice->playback_con_substream_ds[0])
+					snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]);
+			}
+			if (mtstat & VT1724_MULTI_PDMA2) {
+				if (ice->playback_con_substream_ds[1])
+					snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]);
+			}
+			if (mtstat & VT1724_MULTI_PDMA3) {
+				if (ice->playback_con_substream_ds[2])
+					snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]);
+			}
 			if (mtstat & VT1724_MULTI_PDMA4) {
 				if (ice->playback_con_substream)
 					snd_pcm_period_elapsed(ice->playback_con_substream);
@@ -282,16 +294,6 @@
 	.mask = 0,
 };
 
-static unsigned int hw_channels[] = {
-	2, 4, 6, 8
-};
-
-static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
-	.count = ARRAY_SIZE(hw_channels),
-	.list = hw_channels,
-	.mask = 0,
-};
-
 static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
 {
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
@@ -300,21 +302,16 @@
 	struct list_head *pos;
 	snd_pcm_substream_t *s;
 
+	what = 0;
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
+		what |= (unsigned long)(s->runtime->private_data);
+		snd_pcm_trigger_done(s, substream);
+	}
+
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		what = 0;
-		snd_pcm_group_for_each(pos, substream) {
-			s = snd_pcm_group_substream_entry(pos);
-			if (s == ice->playback_pro_substream)
-				what |= VT1724_PDMA0_PAUSE;
-			else if (s == ice->capture_pro_substream)
-				what |= VT1724_RDMA0_PAUSE;
-			else if (s == ice->playback_con_substream)
-				what |= VT1724_PDMA4_PAUSE;
-			else if (s == ice->capture_con_substream)
-				what |= VT1724_RDMA1_PAUSE;
-		}
 		spin_lock(&ice->reg_lock);
 		old = inb(ICEMT1724(ice, DMA_PAUSE));
 		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
@@ -327,24 +324,6 @@
 
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_STOP:
-		what = 0;
-		s = substream;
-		snd_pcm_group_for_each(pos, substream) {
-			s = snd_pcm_group_substream_entry(pos);
-			if (s == ice->playback_pro_substream) {
-				what |= VT1724_PDMA0_START;
-				snd_pcm_trigger_done(s, substream);
-			} else if (s == ice->capture_pro_substream) {
-				what |= VT1724_RDMA0_START;
-				snd_pcm_trigger_done(s, substream);
-			} else if (s == ice->playback_con_substream) {
-				what |= VT1724_PDMA4_START;
-				snd_pcm_trigger_done(s, substream);
-			} else if (s == ice->capture_con_substream) {
-				what |= VT1724_RDMA1_START;
-				snd_pcm_trigger_done(s, substream);
-			}
-		}
 		spin_lock(&ice->reg_lock);
 		old = inb(ICEMT1724(ice, DMA_CONTROL));
 		if (cmd == SNDRV_PCM_TRIGGER_START)
@@ -364,8 +343,10 @@
 /*
  */
 
-#define DMA_STARTS	(VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|VT1724_PDMA4_START)
-#define DMA_PAUSES	(VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|VT1724_PDMA4_PAUSE)
+#define DMA_STARTS	(VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\
+	VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START)
+#define DMA_PAUSES	(VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\
+	VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)
 
 static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force)
 {
@@ -448,13 +429,52 @@
 				    snd_pcm_hw_params_t * hw_params)
 {
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int i, chs;
 
+	chs = params_channels(hw_params);
+	down(&ice->open_mutex);
+	/* mark surround channels */
+	if (substream == ice->playback_pro_substream) {
+		chs = chs / 2 - 1;
+		for (i = 0; i < chs; i++) {
+			if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
+				up(&ice->open_mutex);
+				return -EBUSY;
+			}
+			ice->pcm_reserved[i] = substream;
+		}
+		for (; i < 3; i++) {
+			if (ice->pcm_reserved[i] == substream)
+				ice->pcm_reserved[i] = NULL;
+		}
+	} else {
+		for (i = 0; i < 3; i++) {
+			if (ice->playback_con_substream_ds[i] == substream) {
+				if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
+					up(&ice->open_mutex);
+					return -EBUSY;
+				}
+				ice->pcm_reserved[i] = substream;
+				break;
+			}
+		}
+	}
+	up(&ice->open_mutex);
 	snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 }
 
 static int snd_vt1724_pcm_hw_free(snd_pcm_substream_t * substream)
 {
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int i;
+
+	down(&ice->open_mutex);
+	/* unmark surround channels */
+	for (i = 0; i < 3; i++)
+		if (ice->pcm_reserved[i] == substream)
+			ice->pcm_reserved[i] = NULL;
+	up(&ice->open_mutex);
 	return snd_pcm_lib_free_pages(substream);
 }
 
@@ -593,14 +613,14 @@
 	.rate_max =		192000,
 	.channels_min =		2,
 	.channels_max =		8,
-	.buffer_bytes_max =	(1UL << 21),	/* 18bits dword */
+	.buffer_bytes_max =	(1UL << 21),	/* 19bits dword */
 	.period_bytes_min =	8 * 4 * 2,	/* FIXME: constraints needed */
 	.period_bytes_max =	(1UL << 21),
-	.periods_min =		1,
+	.periods_min =		2,
 	.periods_max =		1024,
 };
 
-static snd_pcm_hardware_t snd_vt1724_capture_pro =
+static snd_pcm_hardware_t snd_vt1724_2ch_stereo =
 {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -612,10 +632,10 @@
 	.rate_max =		192000,
 	.channels_min =		2,
 	.channels_max =		2,
-	.buffer_bytes_max =	(256*1024),
+	.buffer_bytes_max =	(1UL << 18),	/* 16bits dword */
 	.period_bytes_min =	2 * 4 * 2,
-	.period_bytes_max =	(256*1024),
-	.periods_min =		1,
+	.period_bytes_max =	(1UL << 18),
+	.periods_min =		2,
 	.periods_max =		1024,
 };
 
@@ -628,7 +648,9 @@
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int chs;
 
+	runtime->private_data = (void*)VT1724_PDMA0_START; /* irq/status/trigger bit */
 	ice->playback_pro_substream = substream;
 	runtime->hw = snd_vt1724_playback_pro;
 	snd_pcm_set_sync(substream);
@@ -639,7 +661,17 @@
 	else
 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
 
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
+	down(&ice->open_mutex);
+	/* calculate the currently available channels */
+	for (chs = 0; chs < 3; chs++) {
+		if (ice->pcm_reserved[chs])
+			break;
+	}
+	chs = (chs + 1) * 2;
+	runtime->hw.channels_max = chs;
+	if (chs > 2) /* channels must be even */
+		snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+	up(&ice->open_mutex);
 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 				   VT1724_BUFFER_ALIGN);
 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
@@ -652,8 +684,9 @@
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
+	runtime->private_data = (void*)VT1724_RDMA0_START; /* irq/status/trigger bit */
 	ice->capture_pro_substream = substream;
-	runtime->hw = snd_vt1724_capture_pro;
+	runtime->hw = snd_vt1724_2ch_stereo;
 	snd_pcm_set_sync(substream);
 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 	if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) &&
@@ -735,25 +768,6 @@
  * SPDIF PCM
  */
 
-static snd_pcm_hardware_t snd_vt1724_playback_spdif =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
-	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
-	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
-	.rate_min =		4000,
-	.rate_max =		192000,
-	.channels_min =		2,
-	.channels_max =		2,
-	.buffer_bytes_max =	(256*1024),
-	.period_bytes_min =	2 * 4 * 2,
-	.period_bytes_max =	(256*1024),
-	.periods_min =		1,
-	.periods_max =		1024,
-};
-
 const static struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
 	.addr = VT1724_MT_PDMA4_ADDR,
 	.size = VT1724_MT_PDMA4_SIZE,
@@ -795,8 +809,9 @@
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
+	runtime->private_data = (void*)VT1724_PDMA4_START; /* irq/status/trigger bit */
 	ice->playback_con_substream = substream;
-	runtime->hw = snd_vt1724_playback_spdif;
+	runtime->hw = snd_vt1724_2ch_stereo;
 	snd_pcm_set_sync(substream);
 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 
@@ -820,8 +835,9 @@
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
+	runtime->private_data = (void*)VT1724_RDMA1_START; /* irq/status/trigger bit */
 	ice->capture_con_substream = substream;
-	runtime->hw = snd_vt1724_playback_spdif;
+	runtime->hw = snd_vt1724_2ch_stereo;
 	snd_pcm_set_sync(substream);
 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
@@ -903,6 +919,127 @@
 
 
 /*
+ * independent surround PCMs
+ */
+
+const static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = {
+	{
+		.addr = VT1724_MT_PDMA1_ADDR,
+		.size = VT1724_MT_PDMA1_SIZE,
+		.count = VT1724_MT_PDMA1_COUNT,
+		.start = VT1724_PDMA1_START,
+		.pause = VT1724_PDMA1_PAUSE,
+	},
+	{
+		.addr = VT1724_MT_PDMA2_ADDR,
+		.size = VT1724_MT_PDMA2_SIZE,
+		.count = VT1724_MT_PDMA2_COUNT,
+		.start = VT1724_PDMA2_START,
+		.pause = VT1724_PDMA2_PAUSE,
+	},
+	{
+		.addr = VT1724_MT_PDMA3_ADDR,
+		.size = VT1724_MT_PDMA3_SIZE,
+		.count = VT1724_MT_PDMA3_COUNT,
+		.start = VT1724_PDMA3_START,
+		.pause = VT1724_PDMA3_PAUSE,
+	},
+};
+
+static int snd_vt1724_playback_indep_prepare(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned char val;
+
+	spin_lock(&ice->reg_lock);
+	val = 3 - substream->number;
+	if (inb(ICEMT1724(ice, BURST)) < val)
+		outb(val, ICEMT1724(ice, BURST));
+	spin_unlock(&ice->reg_lock);
+	return snd_vt1724_pcm_prepare(substream, &vt1724_playback_dma_regs[substream->number]);
+}
+
+static snd_pcm_uframes_t snd_vt1724_playback_indep_pointer(snd_pcm_substream_t * substream)
+{
+	return snd_vt1724_pcm_pointer(substream, &vt1724_playback_dma_regs[substream->number]);
+}
+
+static int snd_vt1724_playback_indep_open(snd_pcm_substream_t *substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	down(&ice->open_mutex);
+	/* already used by PDMA0? */
+	if (ice->pcm_reserved[substream->number]) {
+		up(&ice->open_mutex);
+		return -EBUSY; /* FIXME: should handle blocking mode properly */
+	}
+	up(&ice->open_mutex);
+	runtime->private_data = (void*)(1 << (substream->number + 4));
+	ice->playback_con_substream_ds[substream->number] = substream;
+	runtime->hw = snd_vt1724_2ch_stereo;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
+	return 0;
+}
+
+static int snd_vt1724_playback_indep_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	if (PRO_RATE_RESET)
+		snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
+	ice->playback_con_substream_ds[substream->number] = NULL;
+	ice->pcm_reserved[substream->number] = NULL;
+
+	return 0;
+}
+
+static snd_pcm_ops_t snd_vt1724_playback_indep_ops = {
+	.open =		snd_vt1724_playback_indep_open,
+	.close =	snd_vt1724_playback_indep_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_vt1724_pcm_hw_params,
+	.hw_free =	snd_vt1724_pcm_hw_free,
+	.prepare =	snd_vt1724_playback_indep_prepare,
+	.trigger =	snd_vt1724_pcm_trigger,
+	.pointer =	snd_vt1724_playback_indep_pointer,
+};
+
+
+static int __devinit snd_vt1724_pcm_indep(ice1712_t * ice, int device)
+{
+	snd_pcm_t *pcm;
+	int play;
+	int err;
+
+	play = ice->num_total_dacs / 2 - 1;
+	if (play <= 0)
+		return 0;
+
+	err = snd_pcm_new(ice->card, "ICE1724 Surrounds", device, play, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_vt1724_playback_indep_ops);
+
+	pcm->private_data = ice;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1724 Surround PCM");
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
+
+	ice->pcm_ds = pcm;
+
+	return 0;
+}
+
+
+/*
  *  Mixer section
  */
 
@@ -1808,6 +1945,7 @@
 	ice->vt1724 = 1;
 	spin_lock_init(&ice->reg_lock);
 	init_MUTEX(&ice->gpio_mutex);
+	init_MUTEX(&ice->open_mutex);
 	ice->gpio.set_mask = snd_vt1724_set_gpio_mask;
 	ice->gpio.set_dir = snd_vt1724_set_gpio_dir;
 	ice->gpio.set_data = snd_vt1724_set_gpio_data;
@@ -1932,6 +2070,11 @@
 		return err;
 	}
 	
+	if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
 	if ((err = snd_vt1724_ac97_mixer(ice)) < 0) {
 		snd_card_free(card);
 		return err;
diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
--- a/sound/pci/intel8x0.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/intel8x0.c	Sun Feb 29 12:58:39 2004
@@ -143,6 +143,9 @@
 #ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO
 #define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO	0x00da
 #endif
+#ifndef PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO	0x00ea
+#endif
 
 enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };
 
@@ -443,6 +446,7 @@
 	{ 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE */
 	{ 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE2 */
 	{ 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE3 */
+	{ 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* CK8S */
 	{ 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD8111 */
 	{ 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD768 */
 	{ 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI },   /* Ali5455 */
@@ -804,10 +808,19 @@
 	spin_lock(&chip->reg_lock);
 	status = igetdword(chip, chip->int_sta_reg);
 	if ((status & chip->int_sta_mask) == 0) {
-		if (status)
+		static int err_count = 10;
+		if (status) {
+			/* ack */
 			iputdword(chip, chip->int_sta_reg, status);
+			status ^= igetdword(chip, chip->int_sta_reg);
+		}
 		spin_unlock(&chip->reg_lock);
-		return IRQ_NONE;
+		if (status && err_count) {
+			err_count--;
+			snd_printd("intel8x0: unknown IRQ bits 0x%x (sta_mask=0x%x)\n",
+				   status, chip->int_sta_mask);
+		}
+		return IRQ_RETVAL(status);
 	}
 
 	for (i = 0; i < chip->bdbars_count; i++) {
@@ -1017,6 +1030,7 @@
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 	ichdev_t *ichdev = get_ichdev(substream);
+	unsigned long flags;
 	size_t ptr1, ptr;
 
 	ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << chip->pcm_pos_shift;
@@ -1024,7 +1038,9 @@
 		ptr = ichdev->fragsize1 - ptr1;
 	else
 		ptr = 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
 	ptr += ichdev->position;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	if (ptr >= ichdev->size)
 		return 0;
 	return bytes_to_frames(substream->runtime, ptr);
@@ -1079,21 +1095,12 @@
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	static unsigned int i, rates[] = {
-		/* ATTENTION: these values depend on the definition in pcm.h! */
-		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000
-	};
 	int err;
 
 	ichdev->substream = substream;
 	runtime->hw = snd_intel8x0_stream;
 	runtime->hw.rates = ichdev->pcm->rates;
-	for (i = 0; i < ARRAY_SIZE(rates); i++) {
-		if (runtime->hw.rates & (1 << i)) {
-			runtime->hw.rate_min = rates[i];
-			break;
-		}
-	}
+	snd_pcm_limit_hw_rates(runtime);
 	if (chip->device_type == DEVICE_SIS) {
 		runtime->hw.buffer_bytes_max = 64*1024;
 		runtime->hw.period_bytes_max = 64*1024;
@@ -1741,6 +1748,12 @@
 	},
 	{
 		.vendor = 0x8086,
+		.device = 0x4856,
+		.name = "Intel D845WN (82801BA)",
+		.type = AC97_TUNE_SWAP_HP
+	},
+	{
+		.vendor = 0x8086,
 		.device = 0x4d44,
 		.name = "Intel D850EMV2",	/* AD1885 */
 		.type = AC97_TUNE_HP_ONLY
@@ -2567,6 +2580,7 @@
 	{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" },
 	{ PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" },
 	{ PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" },
+	{ PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" },
 	{ 0x746d, "AMD AMD8111" },
 	{ 0x7445, "AMD AMD768" },
 	{ 0x5455, "ALi M5455" },
diff -Nru a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/Makefile	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
+
+obj-$(CONFIG_SND_MIXART) += snd-mixart.o
diff -Nru a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,1461 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * main file with alsa callbacks
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "mixart.h"
+#include "mixart_hwdep.h"
+#include "mixart_core.h"
+#include "mixart_mixer.h"
+
+#define CARD_NAME "miXart"
+
+MODULE_AUTHOR("Digigram <alsa@digigram.com>");
+MODULE_DESCRIPTION("Digigram " CARD_NAME);
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Digigram," CARD_NAME "}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;             /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;              /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;     /* Enable this card */
+
+#define chip_t mixart_t
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+
+/*
+ */
+
+static struct pci_device_id snd_mixart_ids[] = {
+	{ 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_mixart_ids);
+
+
+static int mixart_set_pipe_state(mixart_mgr_t *mgr, mixart_pipe_t* pipe, int start)
+{
+	mixart_group_state_req_t group_state;
+	mixart_group_state_resp_t group_state_resp;
+	mixart_msg_t request;
+	int err;
+	u32 system_msg_uid;
+
+	switch(pipe->status) {
+	case PIPE_RUNNING:
+	case PIPE_CLOCK_SET:
+		if(start) return 0; /* already started */
+		break;
+	case PIPE_STOPPED:
+		if(!start) return 0; /* already stopped */
+		break;
+	default:
+		snd_printk(KERN_ERR "error mixart_set_pipe_state called with wrong pipe->status!\n");
+		return -EINVAL;      /* function called with wrong pipe status */
+	}
+
+	system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */
+
+	/* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */
+
+	request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD;
+	request.uid = (mixart_uid_t){0,0};
+	request.data = &system_msg_uid;
+	request.size = sizeof(system_msg_uid);
+
+	err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid);
+	if(err) {
+		snd_printk(KERN_ERR "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
+		return err;
+	}
+
+	/* start or stop the pipe (1 pipe) */
+
+	memset(&group_state, 0, sizeof(group_state));
+	group_state.pipe_count = 1;
+	group_state.pipe_uid[0] = pipe->group_uid;
+
+	if(start)
+		request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET;
+	else
+		request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET;
+
+	request.uid = pipe->group_uid; /*(mixart_uid_t){0,0};*/
+	request.data = &group_state;
+	request.size = sizeof(group_state);
+
+	err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
+	if (err < 0 || group_state_resp.txx_status != 0) {
+		snd_printk(KERN_ERR "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status);
+		return -EINVAL;
+	}
+
+	if(start) {
+		u32 stat;
+
+		group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
+
+		err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
+		if (err < 0 || group_state_resp.txx_status != 0) {
+			snd_printk(KERN_ERR "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status);
+ 			return -EINVAL;
+		}
+
+		/* in case of start send a synchro top */
+
+		request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
+		request.uid = (mixart_uid_t){0,0};
+		request.data = NULL;
+		request.size = 0;
+
+		err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat);
+		if (err < 0 || stat != 0) {
+			snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n", err, stat);
+			return -EINVAL;
+		}
+
+		pipe->status = PIPE_RUNNING;
+	}
+	else /* !start */
+		pipe->status = PIPE_STOPPED;
+
+	return 0;
+}
+
+
+static int mixart_set_clock(mixart_mgr_t *mgr, mixart_pipe_t *pipe, unsigned int rate)
+{
+	mixart_msg_t request;
+	mixart_clock_properties_t clock_properties;
+	mixart_clock_properties_resp_t clock_prop_resp;
+	int err;
+
+	switch(pipe->status) {
+	case PIPE_CLOCK_SET:
+		break;
+	case PIPE_RUNNING:
+		if(rate != 0)
+			break;
+	default:
+		if(rate == 0)
+			return 0; /* nothing to do */
+		else {
+			snd_printk(KERN_ERR "error mixart_set_clock(%d) called with wrong pipe->status !\n", rate);
+			return -EINVAL;
+		}
+	}
+
+	memset(&clock_properties, 0, sizeof(clock_properties));
+	clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK;
+	clock_properties.clock_mode = CM_STANDALONE;
+	clock_properties.frequency = rate;
+	clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
+	clock_properties.uid_caller[0] = pipe->group_uid;
+
+	snd_printdd("mixart_set_clock to %d kHz\n", rate);
+
+	request.message_id = MSG_CLOCK_SET_PROPERTIES;
+	request.uid = mgr->uid_console_manager;
+	request.data = &clock_properties;
+	request.size = sizeof(clock_properties);
+
+	err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp);
+	if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) {
+		snd_printk(KERN_ERR "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n", err, clock_prop_resp.status, clock_prop_resp.clock_mode);
+		return -EINVAL;
+	}
+
+	if(rate)  pipe->status = PIPE_CLOCK_SET;
+	else      pipe->status = PIPE_RUNNING;
+
+	return 0;
+}
+
+
+/*
+ *  Allocate or reference output pipe for analog IOs (pcmp0/1)
+ */
+mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring)
+{
+	int stream_count;
+	mixart_pipe_t *pipe;
+	mixart_msg_t request;
+
+	if(capture) {
+		if (pcm_number == MIXART_PCM_ANALOG) {
+			pipe = &(chip->pipe_in_ana);  /* analog inputs */
+		} else {
+			pipe = &(chip->pipe_in_dig); /* digital inputs */
+		}
+		request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP;
+		stream_count = MIXART_CAPTURE_STREAMS;
+	} else {
+		if (pcm_number == MIXART_PCM_ANALOG) {
+			pipe = &(chip->pipe_out_ana);  /* analog outputs */
+		} else {
+			pipe = &(chip->pipe_out_dig);  /* digital outputs */
+		}
+		request.message_id = MSG_STREAM_ADD_INPUT_GROUP;
+		stream_count = MIXART_PLAYBACK_STREAMS;
+	}
+
+	/* a new stream is opened and there are already all streams in use */
+	if( (monitoring == 0) && (pipe->references >= stream_count) ) {
+		return NULL;
+	}
+
+	/* pipe is not yet defined */
+	if( pipe->status == PIPE_UNDEFINED ) {
+		int err, i;
+		mixart_streaming_group_t streaming_group_resp;
+		mixart_streaming_group_req_t streaming_group_req;
+
+		snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number);
+
+		request.uid = (mixart_uid_t){0,0};      /* should be StreamManagerUID, but zero is OK if there is only one ! */
+		request.data = &streaming_group_req;
+		request.size = sizeof(streaming_group_req);
+
+		memset(&streaming_group_req, 0, sizeof(streaming_group_req));
+
+		streaming_group_req.stream_count = stream_count;
+		streaming_group_req.channel_count = 2;
+		streaming_group_req.latency = 256;
+		streaming_group_req.connector = pipe->uid_left_connector;  /* the left connector */
+
+		for (i=0; i<stream_count; i++) {
+			int j;
+			/* we don't yet know the format, so config 16 bit pcm audio for instance */
+			streaming_group_req.stream_info[i].size_max_byte_frame = 1024;
+			streaming_group_req.stream_info[i].size_max_sample_frame = 256;
+			streaming_group_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */
+
+			/* find the right bufferinfo_array */
+			j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i;
+			if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */
+
+			streaming_group_req.flow_entry[i] = j;
+
+			chip->mgr->flowinfo_array[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo_physaddr + (j * sizeof(mixart_bufferinfo_t));
+			chip->mgr->flowinfo_array[j].bufferinfo_count = 1;               /* 1 will set the miXart to ring-buffer mode ! */
+
+			chip->mgr->bufferinfo_array[j].buffer_address = 0;               /* buffer is not yet allocated */
+			chip->mgr->bufferinfo_array[j].available_length = 0;             /* buffer is not yet allocated */
+
+			/* construct the identifier of the stream buffer received in the interrupts ! */
+			chip->mgr->bufferinfo_array[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i;
+			if(capture) {
+				chip->mgr->bufferinfo_array[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK;
+			}
+		}
+
+		err = snd_mixart_send_msg(chip->mgr, &request, sizeof(streaming_group_resp), &streaming_group_resp);
+		if((err < 0) || (streaming_group_resp.status != 0)) {
+			snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, streaming_group_resp.status);
+			return NULL;
+		}
+
+		pipe->group_uid = streaming_group_resp.group;     /* id of the pipe, as returned by embedded */
+		pipe->stream_count = streaming_group_resp.stream_count;
+		/* pipe->stream_uid[i] = streaming_group_resp.stream[i].stream_uid; */
+
+		pipe->status = PIPE_STOPPED;
+	}
+
+	if(monitoring)	pipe->monitoring = 1;
+	else		pipe->references++;
+
+	return pipe;
+}
+
+
+int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring)
+{
+	int err = 0;
+
+	if(pipe->status == PIPE_UNDEFINED)
+		return 0;
+
+	if(monitoring)
+		pipe->monitoring = 0;
+	else
+		pipe->references--;
+
+	if((pipe->references <= 0) && (pipe->monitoring == 0)) {
+
+		mixart_msg_t request;
+		mixart_delete_group_resp_t delete_resp;
+
+		/* release the clock */
+		err = mixart_set_clock( mgr, pipe, 0);
+		if( err < 0 ) {
+			snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n");
+		}
+
+		/* stop the pipe */
+		err = mixart_set_pipe_state(mgr, pipe, 0);
+		if( err < 0 ) {
+			snd_printk(KERN_ERR "error stopping pipe!\n");
+		}
+
+		request.message_id = MSG_STREAM_DELETE_GROUP;
+		request.uid = (mixart_uid_t){0,0};
+		request.data = &pipe->group_uid;            /* the streaming group ! */
+		request.size = sizeof(pipe->group_uid);
+
+		/* delete the pipe */
+		err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
+		if ((err < 0) || (delete_resp.status != 0)) {
+			snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status);
+		}
+
+		pipe->group_uid = (mixart_uid_t){0,0};
+		pipe->stream_count = 0;
+		pipe->status = PIPE_UNDEFINED;
+	}
+
+	return err;
+}
+
+static int mixart_set_stream_state(mixart_stream_t *stream, int start)
+{
+	mixart_t *chip;
+	mixart_stream_state_req_t stream_state_req;
+	mixart_msg_t request;
+
+	if(!stream->substream)
+		return -EINVAL;
+
+	memset(&stream_state_req, 0, sizeof(stream_state_req));
+	stream_state_req.stream_count = 1;
+	stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
+	stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;
+
+	if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
+	else
+		request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;
+
+	request.uid = (mixart_uid_t){0,0};
+	request.data = &stream_state_req;
+	request.size = sizeof(stream_state_req);
+
+	stream->abs_period_elapsed = 0;            /* reset stream pos      */
+	stream->buf_periods = 0;
+	stream->buf_period_frag = 0;
+
+	chip = snd_pcm_substream_chip(stream->substream);
+
+	return snd_mixart_send_msg_nonblock(chip->mgr, &request);
+}
+
+/*
+ *  Trigger callback
+ */
+
+static int snd_mixart_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+	mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+
+		snd_printdd("SNDRV_PCM_TRIGGER_START\n");
+
+		/* START_STREAM */
+		if( mixart_set_stream_state(stream, 1) )
+			return -EINVAL;
+
+		stream->status = MIXART_STREAM_STATUS_RUNNING;
+
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+
+		/* STOP_STREAM */
+		if( mixart_set_stream_state(stream, 0) )
+			return -EINVAL;
+
+		stream->status = MIXART_STREAM_STATUS_OPEN;
+
+		snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		/* TODO */
+		stream->status = MIXART_STREAM_STATUS_PAUSE;
+		snd_printdd("SNDRV_PCM_PAUSE_PUSH\n");
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		/* TODO */
+		stream->status = MIXART_STREAM_STATUS_RUNNING;
+		snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n");
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mixart_sync_nonblock_events(mixart_mgr_t *mgr)
+{
+	int timeout = HZ;
+	while (atomic_read(&mgr->msg_processed) > 0) {
+		if (! timeout--) {
+			snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n");
+			return -EBUSY;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+	return 0;
+}
+
+/*
+ *  prepare callback for all pcms
+ *
+ *  NOTE: this callback is non-atomic (pcm->info_flags |= SNDRV_PCM_INFO_NONATOMIC_OPS)
+ */
+static int snd_mixart_prepare(snd_pcm_substream_t *subs)
+{
+	mixart_t *chip = snd_pcm_substream_chip(subs);
+	mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+
+	/* TODO de fa�on non bloquante, r�appliquer les hw_params (rate, bits, codec) */
+
+	snd_printdd("snd_mixart_prepare\n");
+
+	mixart_sync_nonblock_events(chip->mgr);
+
+	/* only the first stream can choose the sample rate */
+	/* the further opened streams will be limited to its frequency (see open) */
+	if(chip->mgr->ref_count_rate == 1)
+		chip->mgr->sample_rate = subs->runtime->rate;
+
+	/* set the clock only once (first stream) on the same pipe */
+	if(stream->pipe->references == 1) {
+		if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int mixart_set_format(mixart_stream_t *stream, snd_pcm_format_t format)
+{
+	int err;
+	mixart_t *chip;
+	mixart_msg_t request;
+	mixart_stream_param_desc_t stream_param;
+	mixart_return_uid_t resp;
+
+	chip = snd_pcm_substream_chip(stream->substream);
+
+	memset(&stream_param, 0, sizeof(stream_param));
+
+	stream_param.coding_type = CT_LINEAR;
+	stream_param.number_of_channel = stream->channels;
+
+	stream_param.sampling_freq = chip->mgr->sample_rate;
+	if(stream_param.sampling_freq == 0)
+		stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */
+
+	switch(format){
+	case SNDRV_PCM_FORMAT_U8:
+		stream_param.sample_type = ST_INTEGER_8;
+		stream_param.sample_size = 8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		stream_param.sample_type = ST_INTEGER_16LE;
+		stream_param.sample_size = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		stream_param.sample_type = ST_INTEGER_16BE;
+		stream_param.sample_size = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		stream_param.sample_type = ST_INTEGER_24LE;
+		stream_param.sample_size = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3BE:
+		stream_param.sample_type = ST_INTEGER_24BE;
+		stream_param.sample_size = 24;
+		break;
+	case SNDRV_PCM_FMTBIT_FLOAT_LE:
+		stream_param.sample_type = ST_FLOATING_POINT_32LE;
+		stream_param.sample_size = 32;
+		break;
+	case  SNDRV_PCM_FMTBIT_FLOAT_BE:
+		stream_param.sample_type = ST_FLOATING_POINT_32BE;
+		stream_param.sample_size = 32;
+		break;
+	default:
+		snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n");
+		return -EINVAL;
+	}
+
+	snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
+		   stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);
+
+	/* TODO: what else to configure ? */
+	/* stream_param.samples_per_frame = 2; */
+	/* stream_param.bytes_per_frame = 4; */
+	/* stream_param.bytes_per_sample = 2; */
+
+	stream_param.pipe_count = 1;      /* set to 1 */
+	stream_param.stream_count = 1;    /* set to 1 */
+	stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
+	stream_param.stream_desc[0].stream_idx = stream->substream->number;
+
+	request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
+	request.uid = (mixart_uid_t){0,0};
+	request.data = &stream_param;
+	request.size = sizeof(stream_param);
+
+	err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+	if((err < 0) || resp.error_code) {
+		snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/*
+ *  HW_PARAMS callback for all pcms
+ */
+static int snd_mixart_hw_params(snd_pcm_substream_t *subs,
+                                snd_pcm_hw_params_t *hw)
+{
+	mixart_t *chip = snd_pcm_substream_chip(subs);
+	mixart_mgr_t *mgr = chip->mgr;
+	mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+	snd_pcm_format_t format;
+	int err;
+	int channels;
+
+	/* set up channels */
+	channels = params_channels(hw);
+
+	/*  set up format for the stream */
+	format = params_format(hw);
+
+	down(&mgr->setup_mutex);
+
+	/* update the stream levels */
+	if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
+		int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
+		if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
+			mixart_update_playback_stream_level(chip, is_aes, subs->number);
+		else
+			mixart_update_capture_stream_level( chip, is_aes);
+	}
+
+	stream->channels = channels;
+
+	/* set the format to the board */
+	err = mixart_set_format(stream, format);
+	if(err < 0) {
+		return err;
+	}
+
+	/* allocate buffer */
+	err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
+
+	if (err > 0) {
+		int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
+		if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
+			i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
+		}
+		mgr->bufferinfo_array[i].buffer_address = subs->runtime->dma_addr;
+		mgr->bufferinfo_array[i].available_length = subs->runtime->dma_bytes;
+		/* mgr->bufferinfo_array[i].buffer_id  is already defined */
+
+		snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i, subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number);
+	}
+	up(&mgr->setup_mutex);
+
+	return err;
+}
+
+static int snd_mixart_hw_free(snd_pcm_substream_t *subs)
+{
+	mixart_t *chip = snd_pcm_substream_chip(subs);
+	snd_pcm_lib_free_pages(subs);
+	mixart_sync_nonblock_events(chip->mgr);
+	return 0;
+}
+
+
+
+/*
+ *  TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
+ */
+static snd_pcm_hardware_t snd_mixart_analog_caps =
+{
+	.info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+			      SNDRV_PCM_INFO_PAUSE),
+	.formats	  = ( SNDRV_PCM_FMTBIT_U8 |
+			      SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+			      SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
+			      SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
+	.rates            = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min         = 8000,
+	.rate_max         = 48000,
+	.channels_min     = 1,
+	.channels_max     = 2,
+	.buffer_bytes_max = (32*1024),
+	.period_bytes_min = 256,                  /* 256 frames U8 mono*/
+	.period_bytes_max = (16*1024),
+	.periods_min      = 2,
+	.periods_max      = (32*1024/256),
+};
+
+static snd_pcm_hardware_t snd_mixart_digital_caps =
+{
+	.info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+			      SNDRV_PCM_INFO_PAUSE),
+	.formats	  = ( SNDRV_PCM_FMTBIT_U8 |
+			      SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+			      SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
+			      SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
+	.rates            = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min         = 32000,
+	.rate_max         = 48000,
+	.channels_min     = 1,
+	.channels_max     = 2,
+	.buffer_bytes_max = (32*1024),
+	.period_bytes_min = 256,                  /* 256 frames U8 mono*/
+	.period_bytes_max = (16*1024),
+	.periods_min      = 2,
+	.periods_max      = (32*1024/256),
+};
+
+
+static int snd_mixart_playback_open(snd_pcm_substream_t *subs)
+{
+	mixart_t            *chip = snd_pcm_substream_chip(subs);
+	mixart_mgr_t        *mgr = chip->mgr;
+	snd_pcm_runtime_t   *runtime = subs->runtime;
+	snd_pcm_t           *pcm = subs->pcm;
+	mixart_stream_t     *stream;
+	mixart_pipe_t       *pipe;
+	int err = 0;
+	int pcm_number;
+
+	down(&mgr->setup_mutex);
+
+	if ( pcm == chip->pcm ) {
+		pcm_number = MIXART_PCM_ANALOG;
+		runtime->hw = snd_mixart_analog_caps;
+	} else {
+		snd_assert ( pcm == chip->pcm_dig ); 
+		pcm_number = MIXART_PCM_DIGITAL;
+		runtime->hw = snd_mixart_digital_caps;
+	}
+	snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
+
+	/* get stream info */
+	stream = &(chip->playback_stream[pcm_number][subs->number]);
+
+	if (stream->status != MIXART_STREAM_STATUS_FREE){
+		/* streams in use */
+		snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
+		err = -EBUSY;
+		goto _exit_open;
+	}
+
+	/* get pipe pointer (out pipe) */
+	pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);
+
+	if (pipe == NULL) {
+		err = -EINVAL;
+		goto _exit_open;
+	}
+
+	/* start the pipe if necessary */
+	err = mixart_set_pipe_state(chip->mgr, pipe, 1);
+	if( err < 0 ) {
+		snd_printk(KERN_ERR "error starting pipe!\n");
+		snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
+		err = -EINVAL;
+		goto _exit_open;
+	}
+
+	stream->pipe        = pipe;
+	stream->pcm_number  = pcm_number;
+	stream->status      = MIXART_STREAM_STATUS_OPEN;
+	stream->substream   = subs;
+	stream->channels    = 0; /* not configured yet */
+
+	runtime->private_data = stream;
+
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
+
+	/* if a sample rate is already used, another stream cannot change */
+	if(mgr->ref_count_rate++) {
+		if(mgr->sample_rate) {
+			runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
+		}
+	}
+
+ _exit_open:
+	up(&mgr->setup_mutex);
+
+	return err;
+}
+
+
+static int snd_mixart_capture_open(snd_pcm_substream_t *subs)
+{
+	mixart_t            *chip = snd_pcm_substream_chip(subs);
+	mixart_mgr_t        *mgr = chip->mgr;
+	snd_pcm_runtime_t   *runtime = subs->runtime;
+	snd_pcm_t           *pcm = subs->pcm;
+	mixart_stream_t     *stream;
+	mixart_pipe_t       *pipe;
+	int err = 0;
+	int pcm_number;
+
+	down(&mgr->setup_mutex);
+
+	if ( pcm == chip->pcm ) {
+		pcm_number = MIXART_PCM_ANALOG;
+		runtime->hw = snd_mixart_analog_caps;
+	} else {
+		snd_assert ( pcm == chip->pcm_dig ); 
+		pcm_number = MIXART_PCM_DIGITAL;
+		runtime->hw = snd_mixart_digital_caps;
+	}
+
+	runtime->hw.channels_min = 2; /* for instance, no mono */
+
+	snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
+
+	/* get stream info */
+	stream = &(chip->capture_stream[pcm_number]);
+
+	if (stream->status != MIXART_STREAM_STATUS_FREE){
+		/* streams in use */
+		snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
+		err = -EBUSY;
+		goto _exit_open;
+	}
+
+	/* get pipe pointer (in pipe) */
+	pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);
+
+	if (pipe == NULL) {
+		err = -EINVAL;
+		goto _exit_open;
+	}
+
+	/* start the pipe if necessary */
+	err = mixart_set_pipe_state(chip->mgr, pipe, 1);
+	if( err < 0 ) {
+		snd_printk(KERN_ERR "error starting pipe!\n");
+		snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
+		err = -EINVAL;
+		goto _exit_open;
+	}
+
+	stream->pipe        = pipe;
+	stream->pcm_number  = pcm_number;
+	stream->status      = MIXART_STREAM_STATUS_OPEN;
+	stream->substream   = subs;
+	stream->channels    = 0; /* not configured yet */
+
+	runtime->private_data = stream;
+
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
+
+	/* if a sample rate is already used, another stream cannot change */
+	if(mgr->ref_count_rate++) {
+		if(mgr->sample_rate) {
+			runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
+		}
+	}
+
+ _exit_open:
+	up(&mgr->setup_mutex);
+
+	return err;
+}
+
+
+
+static int snd_mixart_close(snd_pcm_substream_t *subs)
+{
+	mixart_t *chip = snd_pcm_substream_chip(subs);
+	mixart_mgr_t *mgr = chip->mgr;
+	mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
+
+	down(&mgr->setup_mutex);
+
+	snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number);
+
+	/* sample rate released */
+	if(--mgr->ref_count_rate == 0) {
+		mgr->sample_rate = 0;
+	}
+
+	/* delete pipe */
+	if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {
+
+		snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number);
+	}
+
+	stream->pipe      = NULL;
+	stream->status    = MIXART_STREAM_STATUS_FREE;
+	stream->substream = NULL;
+
+	up(&mgr->setup_mutex);
+	return 0;
+}
+
+
+static snd_pcm_uframes_t snd_mixart_stream_pointer(snd_pcm_substream_t * subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	mixart_stream_t   *stream  = (mixart_stream_t*)runtime->private_data;
+
+	return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
+}
+
+
+
+static snd_pcm_ops_t snd_mixart_playback_ops = {
+	.open      = snd_mixart_playback_open,
+	.close     = snd_mixart_close,
+	.ioctl     = snd_pcm_lib_ioctl,
+	.prepare   = snd_mixart_prepare,
+	.hw_params = snd_mixart_hw_params,
+	.hw_free   = snd_mixart_hw_free,
+	.trigger   = snd_mixart_trigger,
+	.pointer   = snd_mixart_stream_pointer,
+};
+
+static snd_pcm_ops_t snd_mixart_capture_ops = {
+	.open      = snd_mixart_capture_open,
+	.close     = snd_mixart_close,
+	.ioctl     = snd_pcm_lib_ioctl,
+	.prepare   = snd_mixart_prepare,
+	.hw_params = snd_mixart_hw_params,
+	.hw_free   = snd_mixart_hw_free,
+	.trigger   = snd_mixart_trigger,
+	.pointer   = snd_mixart_stream_pointer,
+};
+
+static void preallocate_buffers(mixart_t *chip, snd_pcm_t *pcm)
+{
+	snd_pcm_substream_t *subs;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		int idx = 0;
+		for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
+			/* set up the unique device id with the chip index */
+			subs->dma_device.id = subs->pcm->device << 16 |
+				subs->stream << 8 | (subs->number + 1) |
+				(chip->chip_idx + 1) << 24;
+	}
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->mgr->pci, pcm, 32*1024, 32*1024);
+}
+
+/*
+ */
+static int snd_mixart_pcm_analog(mixart_t *chip)
+{
+	int err;
+	snd_pcm_t *pcm;
+	char name[32];
+
+	sprintf(name, "miXart analog %d", chip->chip_idx);
+	if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
+			       MIXART_PLAYBACK_STREAMS,
+			       MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
+		snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx);
+		return err;
+	}
+
+	pcm->private_data = chip;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
+
+	pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS;
+	strcpy(pcm->name, name);
+
+	preallocate_buffers(chip, pcm);
+
+	chip->pcm = pcm;
+	return 0;
+}
+
+
+/*
+ */
+static int snd_mixart_pcm_digital(mixart_t *chip)
+{
+	int err;
+	snd_pcm_t *pcm;
+	char name[32];
+
+	sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
+	if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
+			       MIXART_PLAYBACK_STREAMS,
+			       MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
+		snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx);
+		return err;
+	}
+
+	pcm->private_data = chip;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
+
+	pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS;
+	strcpy(pcm->name, name);
+
+	preallocate_buffers(chip, pcm);
+
+	chip->pcm_dig = pcm;
+	return 0;
+}
+
+static int snd_mixart_chip_free(mixart_t *chip)
+{
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_mixart_chip_dev_free(snd_device_t *device)
+{
+	mixart_t *chip = snd_magic_cast(mixart_t, device->device_data, return -ENXIO);
+	return snd_mixart_chip_free(chip);
+}
+
+
+/*
+ */
+static int __devinit snd_mixart_create(mixart_mgr_t *mgr, snd_card_t *card, int idx)
+{
+	int err;
+	mixart_t *chip;
+	static snd_device_ops_t ops = {
+		.dev_free = snd_mixart_chip_dev_free,
+	};
+
+	mgr->chip[idx] = chip = snd_magic_kcalloc(mixart_t, 0, GFP_KERNEL);
+	if (! chip) {
+		snd_printk(KERN_ERR "cannot allocate chip\n");
+		return -ENOMEM;
+	}
+
+	chip->card = card;
+	chip->chip_idx = idx;
+	chip->mgr = mgr;
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_mixart_chip_free(chip);
+		return err;
+	}
+
+	if (idx == 0) {
+		/* create a DSP loader only on first cardX*/
+		err = snd_mixart_hwdep_new(mgr);
+		if (err < 0)
+			return err;
+	}
+
+	snd_card_set_dev(card, &mgr->pci->dev);
+
+	return 0;
+}
+
+int snd_mixart_create_pcm(mixart_t* chip)
+{
+	int err;
+
+	err = snd_mixart_pcm_analog(chip);
+	if (err < 0)
+		return err;
+
+	if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+
+		err = snd_mixart_pcm_digital(chip);
+		if (err < 0)
+			return err;
+	}
+	return err;
+}
+
+
+/*
+ * release all the cards assigned to a manager instance
+ */
+static int snd_mixart_free(mixart_mgr_t *mgr)
+{
+	unsigned int i;
+
+	for (i = 0; i < mgr->num_cards; i++) {
+		if (mgr->chip[i])
+			snd_card_free(mgr->chip[i]->card);
+	}
+
+	/* stop mailbox */
+	snd_mixart_exit_mailbox(mgr);
+
+	/* release irq  */
+	if (mgr->irq >= 0)
+		free_irq(mgr->irq, (void *)mgr);
+
+	/* reset board if some firmware was loaded */
+	if(mgr->hwdep->dsp_loaded) {
+		snd_mixart_reset_board(mgr);
+		snd_printdd("reset miXart !\n");
+	}
+
+	/* release the i/o ports */
+	for (i = 0; i < 2; i++) {
+		if (mgr->mem[i].virt)
+			iounmap((void *)mgr->mem[i].virt);
+		if (mgr->mem[i].res) {
+			release_resource(mgr->mem[i].res);
+			kfree_nocheck(mgr->mem[i].res);
+		}
+	}
+
+	/* free flowarray */
+	if(mgr->flowinfo_array) {
+		snd_free_pci_pages(mgr->pci, PAGE_ALIGN(MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)),
+				   mgr->flowinfo_array, mgr->flowinfo_physaddr);
+	}
+	/* free bufferarray */
+	if(mgr->bufferinfo_array) {
+		snd_free_pci_pages(mgr->pci, PAGE_ALIGN(MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)),
+				   mgr->bufferinfo_array, mgr->bufferinfo_physaddr);
+	}
+
+	snd_magic_kfree(mgr);
+	return 0;
+}
+
+/*
+ * proc interface
+ */
+static long long snd_mixart_BA0_llseek(snd_info_entry_t *entry,
+				       void *private_file_data,
+				       struct file *file,
+				       long long offset,
+				       int orig)
+{
+	offset = offset & ~3; /* 4 bytes aligned */
+
+	switch(orig) {
+	case 0:  /* SEEK_SET */
+		file->f_pos = offset;
+		break;
+	case 1:  /* SEEK_CUR */
+		file->f_pos += offset;
+		break;
+	case 2:  /* SEEK_END, offset is negative */
+		file->f_pos = MIXART_BA0_SIZE + offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if(file->f_pos > MIXART_BA0_SIZE)
+		file->f_pos = MIXART_BA0_SIZE;
+	return file->f_pos;
+}
+
+static long long snd_mixart_BA1_llseek(snd_info_entry_t *entry,
+				       void *private_file_data,
+				       struct file *file,
+				       long long offset,
+				       int orig)
+{
+	offset = offset & ~3; /* 4 bytes aligned */
+
+	switch(orig) {
+	case 0:  /* SEEK_SET */
+		file->f_pos = offset;
+		break;
+	case 1:  /* SEEK_CUR */
+		file->f_pos += offset;
+		break;
+	case 2: /* SEEK_END, offset is negative */
+		file->f_pos = MIXART_BA1_SIZE + offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if(file->f_pos > MIXART_BA1_SIZE)
+		file->f_pos = MIXART_BA1_SIZE;
+	return file->f_pos;
+}
+
+/*
+  mixart_BA0 proc interface for BAR 0 - read callback
+ */
+static long snd_mixart_BA0_read(snd_info_entry_t *entry, void *file_private_data,
+				struct file *file, char *buf, long count)
+{
+	mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO);
+
+	count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
+	if(count <= 0)
+		return 0;
+	if(file->f_pos + count > MIXART_BA0_SIZE)
+		count = (long)(MIXART_BA0_SIZE - file->f_pos);
+	if(copy_to_user_fromio(buf, MIXART_MEM( mgr, file->f_pos ), count))
+		return -EFAULT;
+	file->f_pos += count;
+	return count;
+}
+
+/*
+  mixart_BA1 proc interface for BAR 1 - read callback
+ */
+static long snd_mixart_BA1_read(snd_info_entry_t *entry, void *file_private_data,
+				struct file *file, char *buf, long count)
+{
+	mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO);
+
+	count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
+	if(count <= 0)
+		return 0;
+	if(file->f_pos + count > MIXART_BA1_SIZE)
+		count = (long)(MIXART_BA1_SIZE - file->f_pos);
+	if(copy_to_user_fromio(buf, MIXART_REG( mgr, file->f_pos ), count))
+		return -EFAULT;
+	file->f_pos += count;
+	return count;
+}
+
+static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
+	.read   = snd_mixart_BA0_read,
+	.llseek = snd_mixart_BA0_llseek
+};
+
+static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
+	.read   = snd_mixart_BA1_read,
+	.llseek = snd_mixart_BA1_llseek
+};
+
+
+static void snd_mixart_proc_read(snd_info_entry_t *entry, 
+                                 snd_info_buffer_t * buffer)
+{
+	mixart_t *chip = snd_magic_cast(mixart_t, entry->private_data, return);        
+	u32 ref; 
+
+	snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx);
+
+	/* stats available when embedded OS is running */
+	if (chip->mgr->hwdep->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) {
+		snd_iprintf(buffer, "- hardware -\n");
+		switch (chip->mgr->board_type ) {
+		case MIXART_DAUGHTER_TYPE_NONE     : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break;
+		case MIXART_DAUGHTER_TYPE_AES      : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break;
+		case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break;
+		default:                             snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break;
+		}
+
+		snd_iprintf(buffer, "- system load -\n");	 
+
+		/* get perf reference */
+
+		ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET));
+
+		if (ref) {
+			u32 mailbox   = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref;
+			u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref;
+			u32 interr    = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref;
+
+			snd_iprintf(buffer, "\tstreaming          : %d\n", streaming);
+			snd_iprintf(buffer, "\tmailbox            : %d\n", mailbox);
+			snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr);
+		}
+	} /* endif elf loaded */
+}
+
+static void __devinit snd_mixart_proc_init(mixart_t *chip)
+{
+	snd_info_entry_t *entry;
+
+	/* text interface to read perf and temp meters */
+	if (! snd_card_proc_new(chip->card, "board_info", &entry)) {
+		entry->private_data = chip;
+		entry->c.text.read_size = 1024;
+		entry->c.text.read = snd_mixart_proc_read;
+	}
+
+	if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = chip->mgr;	
+		entry->c.ops = &snd_mixart_proc_ops_BA0;
+		entry->size = MIXART_BA0_SIZE;
+	}
+	if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = chip->mgr;
+		entry->c.ops = &snd_mixart_proc_ops_BA1;
+		entry->size = MIXART_BA1_SIZE;
+	}
+}
+/* end of proc interface */
+
+
+/*
+ *    probe function - creates the card manager
+ */
+static int __devinit snd_mixart_probe(struct pci_dev *pci,
+				      const struct pci_device_id *pci_id)
+{
+	static int dev;
+	mixart_mgr_t *mgr;
+	unsigned int i;
+	int err;
+	size_t size;
+
+	/*
+	 */
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (! enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	pci_set_master(pci);
+
+	/* check if we can restrict PCI DMA transfers to 32 bits */
+	if (!pci_dma_supported(pci, 0xffffffff)) {
+		snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0xffffffff);
+
+	/*
+	 */
+	mgr = snd_magic_kcalloc(mixart_mgr_t, 0, GFP_KERNEL);
+	if (! mgr)
+		return -ENOMEM;
+
+	mgr->pci = pci;
+	mgr->irq = -1;
+
+	/* resource assignment */
+	for (i = 0; i < 2; i++) {
+		static int memory_sizes[2] = {
+			MIXART_BA0_SIZE, /* 16M */	  
+			MIXART_BA1_SIZE  /* 4 k */
+		};
+		mgr->mem[i].phys = pci_resource_start(pci, i);
+		mgr->mem[i].res = request_mem_region(mgr->mem[i].phys, memory_sizes[i], CARD_NAME);
+		if (! mgr->mem[i].res) {
+			snd_printk(KERN_ERR "unable to grab memory 0x%lx\n", mgr->mem[i].phys);
+			snd_mixart_free(mgr);
+			return -EBUSY;
+		}
+		mgr->mem[i].virt = (unsigned long)ioremap_nocache(mgr->mem[i].phys, memory_sizes[i]);
+	}
+
+	if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) {
+		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+		snd_mixart_free(mgr);
+		return -EBUSY;
+	}
+	mgr->irq = pci->irq;
+
+	sprintf(mgr->shortname, "Digigram miXart");
+	sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq);
+
+	/* ISR spinlock  */
+	mgr->lock = SPIN_LOCK_UNLOCKED;
+
+	/* init mailbox  */
+	mgr->msg_fifo_readptr = 0;
+	mgr->msg_fifo_writeptr = 0;
+
+	mgr->msg_lock = SPIN_LOCK_UNLOCKED;
+	init_MUTEX(&mgr->msg_mutex);
+	init_waitqueue_head(&mgr->msg_sleep);
+	atomic_set(&mgr->msg_processed, 0);
+
+	/* init setup mutex*/
+	init_MUTEX(&mgr->setup_mutex);
+
+	/* init message taslket */
+	tasklet_init( &mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr);
+
+	/* card assignment */
+	mgr->num_cards = MIXART_MAX_CARDS; /* 4  FIXME: configurable? */
+	for (i = 0; i < mgr->num_cards; i++) {
+		snd_card_t *card;
+		char tmpid[16];
+		int idx;
+
+		if (index[dev] < 0)
+			idx = index[dev];
+		else
+			idx = index[dev] + i;
+		snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev], i);
+		card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
+
+		if (! card) {
+			snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
+			snd_mixart_free(mgr);
+			return -ENOMEM;
+		}
+
+		strcpy(card->driver, CARD_NAME);
+		sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i);
+		sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
+
+		if ((err = snd_mixart_create(mgr, card, i)) < 0) {
+			snd_mixart_free(mgr);
+			return err;
+		}
+
+		if(i==0) {
+			/* init proc interface only for chip0 */
+			snd_mixart_proc_init(mgr->chip[i]);
+		}
+
+		if ((err = snd_card_register(card)) < 0) {
+			snd_mixart_free(mgr);
+			return err;
+		}
+	}
+
+	/* init firmware status (mgr->hwdep->dsp_loaded reset in hwdep_new) */
+	mgr->board_type = MIXART_DAUGHTER_TYPE_NONE;
+
+	/* create array of streaminfo */
+	size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) );
+	mgr->flowinfo_array = snd_malloc_pci_pages(mgr->pci, size, &mgr->flowinfo_physaddr);
+	if(!mgr->flowinfo_array) {
+		snd_mixart_free(mgr);
+		return -ENOMEM;
+	}
+	/* init streaminfo_array */
+	memset(mgr->flowinfo_array, 0, size);
+
+	/* create array of bufferinfo */
+	size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) );
+	mgr->bufferinfo_array = snd_malloc_pci_pages(mgr->pci, size, &mgr->bufferinfo_physaddr);
+	if(!mgr->bufferinfo_array) {
+		snd_mixart_free(mgr);
+		return -ENOMEM;
+	}
+	/* init bufferinfo_array */
+	memset(mgr->bufferinfo_array, 0, size);
+
+	pci_set_drvdata(pci, mgr);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_mixart_remove(struct pci_dev *pci)
+{
+	snd_mixart_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "Digigram miXart",
+	.id_table = snd_mixart_ids,
+	.probe = snd_mixart_probe,
+	.remove = __devexit_p(snd_mixart_remove),
+};
+
+static int __init alsa_card_mixart_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		snd_printk(KERN_ERR "Digigram miXart soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_mixart_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_mixart_init)
+module_exit(alsa_card_mixart_exit)
+
+#ifndef MODULE
+
+/* format is: snd-mixart=enable,index,id */
+
+static int __init alsa_card_mixart_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2 &&
+	       get_option(&str,&index[nr_dev]) == 2 &&
+	       get_id(&str,&id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-mixart=", alsa_card_mixart_setup);
+
+#endif /* ifndef MODULE */
diff -Nru a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart.h	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,243 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * main header file
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_H
+#define __SOUND_MIXART_H
+
+#include <sound/pcm.h>
+
+#define MIXART_DRIVER_VERSION	0x000100	/* 0.1.0 */
+
+
+/*
+ */
+
+#define mixart_t_magic		0xa17a3e01
+#define mixart_mgr_t_magic	0xa17a3e02
+
+typedef struct snd_mixart mixart_t;
+typedef struct snd_mixart_mgr mixart_mgr_t;
+
+typedef struct snd_mixart_stream mixart_stream_t;
+typedef struct snd_mixart_pipe mixart_pipe_t;
+
+typedef struct mixart_bufferinfo mixart_bufferinfo_t;
+typedef struct mixart_flowinfo mixart_flowinfo_t;
+typedef struct mixart_uid mixart_uid_t;
+
+struct mixart_uid
+{
+	u32 object_id;
+	u32 desc;
+};
+
+struct mem_area {
+	unsigned long phys;
+	unsigned long virt;
+	struct resource *res;
+};
+
+
+typedef struct mixart_route mixart_route_t;
+struct mixart_route {
+	unsigned char connected;
+	unsigned char phase_inv;
+	int volume;
+};
+
+
+/* firmware status codes  */
+#define MIXART_MOTHERBOARD_XLX_INDEX  0
+#define MIXART_MOTHERBOARD_ELF_INDEX  1
+#define MIXART_AESEBUBOARD_XLX_INDEX  2
+#define MIXART_HARDW_FILES_MAX_INDEX  3  /* xilinx, elf, AESEBU xilinx */
+
+#define MIXART_MAX_CARDS	4
+#define MSG_FIFO_SIZE           16
+
+#define MIXART_MAX_PHYS_CONNECTORS  (MIXART_MAX_CARDS * 2 * 2) /* 4 * stereo * (analog+digital) */
+
+struct snd_mixart_mgr {
+	unsigned int num_cards;
+	mixart_t *chip[MIXART_MAX_CARDS];
+
+	struct pci_dev *pci;
+
+	int irq;
+
+	/* memory-maps */
+	struct mem_area mem[2];
+
+	/* share the name */
+	char shortname[32];         /* short name of this soundcard */
+	char longname[80];          /* name of this soundcard */
+
+	/* message tasklet */
+	struct tasklet_struct msg_taskq;
+
+	/* one and only blocking message or notification may be pending  */
+	u32 pending_event;
+	wait_queue_head_t msg_sleep;
+
+	/* messages stored for tasklet */
+	u32 msg_fifo[MSG_FIFO_SIZE];
+	int msg_fifo_readptr;
+	int msg_fifo_writeptr;
+	atomic_t msg_processed;       /* number of messages to be processed in takslet */
+
+	spinlock_t lock;              /* interrupt spinlock */
+	spinlock_t msg_lock;          /* mailbox spinlock */
+	struct semaphore msg_mutex;   /* mutex for blocking_requests */
+
+	struct semaphore setup_mutex; /* mutex used in hw_params, open and close */
+
+	/* hardware interface */
+	snd_hwdep_t *hwdep;
+	unsigned int board_type;      /* read from embedded once elf file is loaded, 250 = miXart8, 251 = with AES, 252 = with Cobranet */
+
+	mixart_flowinfo_t*   flowinfo_array;
+	dma_addr_t           flowinfo_physaddr;
+	mixart_bufferinfo_t* bufferinfo_array;
+	dma_addr_t           bufferinfo_physaddr;
+
+	mixart_uid_t         uid_console_manager;
+	int sample_rate;
+	int ref_count_rate;
+
+	struct semaphore mixer_mutex; /* mutex for mixer */
+
+};
+
+
+#define MIXART_STREAM_STATUS_FREE	0
+#define MIXART_STREAM_STATUS_OPEN	1
+#define MIXART_STREAM_STATUS_RUNNING	2
+#define MIXART_STREAM_STATUS_DRAINING	3
+#define MIXART_STREAM_STATUS_PAUSE	4
+
+#define MIXART_PLAYBACK_STREAMS		4
+#define MIXART_CAPTURE_STREAMS		1
+
+#define MIXART_PCM_ANALOG		0
+#define MIXART_PCM_DIGITAL		1
+#define MIXART_PCM_TOTAL		2
+
+#define MIXART_MAX_STREAM_PER_CARD  (MIXART_PCM_TOTAL * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS) )
+
+
+#define MIXART_NOTIFY_CARD_MASK		0xF000
+#define MIXART_NOTIFY_CARD_OFFSET	12
+#define MIXART_NOTIFY_PCM_MASK		0x0F00
+#define MIXART_NOTIFY_PCM_OFFSET	8
+#define MIXART_NOTIFY_CAPT_MASK		0x0080
+#define MIXART_NOTIFY_SUBS_MASK		0x007F
+
+
+struct snd_mixart_stream {
+	snd_pcm_substream_t *substream;
+	mixart_pipe_t *pipe;
+	int pcm_number;
+
+	int status;      /* nothing, running, draining */
+
+	u64  abs_period_elapsed;  /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */
+	u32  buf_periods;         /* periods counter in the buffer (< runtime->periods) */
+	u32  buf_period_frag;     /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */
+
+	int channels;
+};
+
+
+enum mixart_pipe_status {
+	PIPE_UNDEFINED,
+	PIPE_STOPPED,
+	PIPE_RUNNING,
+	PIPE_CLOCK_SET
+};
+
+struct snd_mixart_pipe {
+	mixart_uid_t group_uid;			/* id of the pipe, as returned by embedded */
+	int          stream_count;
+	mixart_uid_t uid_left_connector;	/* UID's for the audio connectors */
+	mixart_uid_t uid_right_connector;
+	enum mixart_pipe_status status;
+	int references;             /* number of subs openned */
+	int monitoring;             /* pipe used for monitoring issue */
+};
+
+
+struct snd_mixart {
+	snd_card_t *card;
+	mixart_mgr_t *mgr;
+	int chip_idx;               /* zero based */
+	snd_hwdep_t *hwdep;	    /* DSP loader, only for the first card */
+
+	snd_pcm_t *pcm;             /* PCM analog i/o */
+	snd_pcm_t *pcm_dig;         /* PCM digital i/o */
+
+	/* allocate stereo pipe for instance */
+	mixart_pipe_t pipe_in_ana;
+	mixart_pipe_t pipe_out_ana;
+
+	/* if AES/EBU daughter board is available, additional pipes possible on pcm_dig */
+	mixart_pipe_t pipe_in_dig;
+	mixart_pipe_t pipe_out_dig;
+
+	mixart_stream_t playback_stream[MIXART_PCM_TOTAL][MIXART_PLAYBACK_STREAMS]; /* 0 = pcm, 1 = pcm_dig */
+	mixart_stream_t capture_stream[MIXART_PCM_TOTAL];                           /* 0 = pcm, 1 = pcm_dig */
+
+	/* UID's for the physical io's */
+	mixart_uid_t uid_out_analog_physio;
+	mixart_uid_t uid_in_analog_physio;
+
+	int analog_playback_active[2];		/* Mixer : Master Playback active (!mute) */
+	int analog_playback_volume[2];		/* Mixer : Master Playback Volume */
+	int analog_capture_volume[2];		/* Mixer : Master Capture Volume */
+	int digital_playback_active[2*MIXART_PLAYBACK_STREAMS][2];	/* Mixer : Digital Playback Active [(analog+AES output)*streams][stereo]*/
+	int digital_playback_volume[2*MIXART_PLAYBACK_STREAMS][2];	/* Mixer : Digital Playback Volume [(analog+AES output)*streams][stereo]*/
+	int digital_capture_volume[2][2];	/* Mixer : Digital Capture Volume [analog+AES output][stereo] */
+	int monitoring_active[2];		/* Mixer : Monitoring Active */
+	int monitoring_volume[2];		/* Mixer : Monitoring Volume */
+};
+
+struct mixart_bufferinfo
+{
+	u32 buffer_address;
+	u32 reserved[5];
+	u32 available_length;
+	u32 buffer_id;
+};
+
+struct mixart_flowinfo
+{
+	u32 bufferinfo_array_phy_address;
+	u32 reserved[11];
+	u32 bufferinfo_count;
+	u32 capture;
+};
+
+/* exported */
+int snd_mixart_create_pcm(mixart_t* chip);
+mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring);
+int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring);
+
+#endif /* __SOUND_MIXART_H */
diff -Nru a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart_core.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,584 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * low level interface with interrupt handling and mail box implementation
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_hwdep.h"
+#include "mixart_core.h"
+
+
+#define MSG_TIMEOUT_JIFFIES         (400 * HZ) / 1000 /* 400 ms */
+
+#define MSG_DESCRIPTOR_SIZE         0x24
+#define MSG_HEADER_SIZE             (MSG_DESCRIPTOR_SIZE + 4)
+
+#define MSG_DEFAULT_SIZE            512
+
+#define MSG_TYPE_MASK               0x00000003    /* mask for following types */
+#define MSG_TYPE_NOTIFY             0             /* embedded -> driver (only notification, do not get_msg() !) */
+#define MSG_TYPE_COMMAND            1             /* driver <-> embedded (a command has no answer) */
+#define MSG_TYPE_REQUEST            2             /* driver -> embedded (request will get an answer back) */
+#define MSG_TYPE_ANSWER             3             /* embedded -> driver */
+#define MSG_CANCEL_NOTIFY_MASK      0x80000000    /* this bit is set for a notification that has been canceled */
+
+
+static int retrieve_msg_frame(mixart_mgr_t *mgr, u32 *msg_frame)
+{
+	/* read the message frame fifo */
+	u32 headptr, tailptr;
+
+	tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
+	headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD));
+
+	if (tailptr == headptr)
+		return 0; /* no message posted */
+
+	snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */
+	snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */
+
+	*msg_frame = readl_be(MIXART_MEM(mgr, tailptr));
+
+	/* increment the tail index */
+	tailptr += 4;
+	if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
+		tailptr = MSG_OUTBOUND_POST_STACK;
+	writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
+
+	return 1;
+}
+
+static int get_msg(mixart_mgr_t *mgr, mixart_msg_t *resp, u32 msg_frame_address )
+{
+	unsigned long flags;
+	u32  headptr, i;
+	u32  size;
+	int  err;
+
+	spin_lock_irqsave(&mgr->msg_lock, flags);
+	err = 0;
+
+	/* copy message descriptor from miXart to driver */
+	size                =  readl_be(MIXART_MEM(mgr, msg_frame_address));       /* size of descriptor + response */
+	resp->message_id    =  readl_be(MIXART_MEM(mgr, msg_frame_address + 4));   /* dwMessageID */
+	resp->uid.object_id =  readl_be(MIXART_MEM(mgr, msg_frame_address + 8));   /* uidDest */
+	resp->uid.desc      =  readl_be(MIXART_MEM(mgr, msg_frame_address + 12));  /* */
+
+	if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) {
+		err = -EINVAL;
+		snd_printk(KERN_ERR "problem with response size = %d\n", size);
+		goto _clean_exit;
+	}
+	size -= MSG_DESCRIPTOR_SIZE;
+
+	memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size);
+	resp->size = size;
+
+	/* swap if necessary */
+#ifndef __BIG_ENDIAN
+	size /= 4; /* u32 size */
+	for(i=0; i < size; i++) {
+		((u32*)resp->data)[i] = be32_to_cpu(((u32*)resp->data)[i]);
+	}
+#endif
+
+	/*
+	 * free message frame address
+	 */
+	headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
+
+	if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
+		err = -EINVAL;
+		goto _clean_exit;
+	}
+
+	/* give address back to outbound fifo */
+	writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
+
+	/* increment the outbound free head */
+	headptr += 4;
+	if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
+		headptr = MSG_OUTBOUND_FREE_STACK;
+
+	writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
+
+ _clean_exit:
+	spin_unlock_irqrestore(&mgr->msg_lock, flags);
+
+	return err;
+}
+
+
+/*
+ * send a message to miXart. return: the msg_frame used for this message
+ */
+/* call with mgr->msg_lock held! */
+static int send_msg( mixart_mgr_t *mgr,
+		     mixart_msg_t *msg,
+		     int max_answersize,
+		     int mark_pending,
+		     u32 *msg_event)
+{
+	u32 headptr, tailptr;
+	u32 msg_frame_address;
+	int err, i;
+
+	snd_assert(msg->size % 4 == 0, return -EINVAL);
+
+	err = 0;
+
+	/* get message frame address */
+	tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
+	headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD));
+
+	if (tailptr == headptr) {
+		snd_printk(KERN_ERR "error: no message frame available\n");
+		return -EBUSY;
+	}
+
+	if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
+		return -EINVAL;
+	}
+
+	msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr));
+	writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */
+
+	/* increment the inbound free tail */
+	tailptr += 4;
+	if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
+		tailptr = MSG_INBOUND_FREE_STACK;
+
+	writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
+
+	/* TODO : use memcpy_toio() with intermediate buffer to copy the message */
+
+	/* copy message descriptor to card memory */
+	writel_be( msg->size + MSG_DESCRIPTOR_SIZE,      MIXART_MEM(mgr, msg_frame_address) );      /* size of descriptor + request */
+	writel_be( msg->message_id ,                     MIXART_MEM(mgr, msg_frame_address + 4) );  /* dwMessageID */
+	writel_be( msg->uid.object_id,                   MIXART_MEM(mgr, msg_frame_address + 8) );  /* uidDest */
+	writel_be( msg->uid.desc,                        MIXART_MEM(mgr, msg_frame_address + 12) ); /* */
+	writel_be( MSG_DESCRIPTOR_SIZE,                  MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */
+	writel_be( MSG_DESCRIPTOR_SIZE,                  MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */
+	writel_be( msg->size,                            MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */
+	writel_be( MSG_DESCRIPTOR_SIZE,                  MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */
+	writel_be( 0,                                    MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */
+	writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */
+
+	/* copy message data to card memory */
+	for( i=0; i < msg->size; i+=4 ) {
+		writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i)  );
+	}
+
+	if( mark_pending ) {
+		if( *msg_event ) {
+			/* the pending event is the notification we wait for ! */
+			mgr->pending_event = *msg_event;
+		}
+		else {
+			/* the pending event is the answer we wait for (same address than the request)! */
+			mgr->pending_event = msg_frame_address;
+
+			/* copy address back to caller */
+			*msg_event = msg_frame_address;
+		}
+	}
+
+	/* mark the frame as a request (will have an answer) */
+	msg_frame_address |= MSG_TYPE_REQUEST;
+
+	/* post the frame */
+	headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
+
+	if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) {
+		return -EINVAL;
+	}
+
+	writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
+
+	/* increment the inbound post head */
+	headptr += 4;
+	if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
+		headptr = MSG_INBOUND_POST_STACK;
+
+	writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
+
+	return 0;
+}
+
+
+int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data)
+{
+	mixart_msg_t resp;
+	u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */
+	int err;
+	wait_queue_t wait;
+	long timeout;
+
+	down(&mgr->msg_mutex);
+
+	init_waitqueue_entry(&wait, current);
+
+	spin_lock_irq(&mgr->msg_lock);
+	/* send the message */
+	err = send_msg(mgr, request, max_resp_size, 1, &msg_frame);  /* send and mark the answer pending */
+	if (err) {
+		spin_unlock_irq(&mgr->msg_lock);
+		up(&mgr->msg_mutex);
+		return err;
+	}
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	add_wait_queue(&mgr->msg_sleep, &wait);
+	spin_unlock_irq(&mgr->msg_lock);
+	timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
+	remove_wait_queue(&mgr->msg_sleep, &wait);
+
+	if (! timeout) {
+		/* error - no ack */
+		up(&mgr->msg_mutex);
+		snd_printk(KERN_ERR "error: no reponse on msg %x\n", msg_frame);
+		return -EIO;
+	}
+
+	/* retrieve the answer into the same mixart_msg_t */
+	resp.message_id = 0;
+	resp.uid = (mixart_uid_t){0,0};
+	resp.data = resp_data;
+	resp.size = max_resp_size;
+
+	err = get_msg(mgr, &resp, msg_frame);
+
+	if( request->message_id != resp.message_id )
+		snd_printk(KERN_ERR "REPONSE ERROR!\n");
+
+	up(&mgr->msg_mutex);
+	return err;
+}
+
+
+int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event)
+{
+	int err;
+	wait_queue_t wait;
+	long timeout;
+
+	snd_assert(notif_event != 0, return -EINVAL);
+	snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL);
+	snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL);
+
+	down(&mgr->msg_mutex);
+
+	init_waitqueue_entry(&wait, current);
+
+	spin_lock_irq(&mgr->msg_lock);
+	/* send the message */
+	err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, &notif_event);  /* send and mark the notification event pending */
+	if(err) {
+		spin_unlock_irq(&mgr->msg_lock);
+		up(&mgr->msg_mutex);
+		return err;
+	}
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	add_wait_queue(&mgr->msg_sleep, &wait);
+	spin_unlock_irq(&mgr->msg_lock);
+	timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
+	remove_wait_queue(&mgr->msg_sleep, &wait);
+
+	if (! timeout) {
+		/* error - no ack */
+		up(&mgr->msg_mutex);
+		snd_printk(KERN_ERR "error: notification %x not received\n", notif_event);
+		return -EIO;
+	}
+
+	up(&mgr->msg_mutex);
+	return 0;
+}
+
+
+int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request)
+{
+	u32 message_frame;
+	unsigned long flags;
+	int err;
+
+	/* just send the message (do not mark it as a pending one) */
+	spin_lock_irqsave(&mgr->msg_lock, flags);
+	err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);
+	spin_unlock_irqrestore(&mgr->msg_lock, flags);
+
+	/* the answer will be handled by snd_mixart_msg_tasklet()  */
+	atomic_inc(&mgr->msg_processed);
+
+	return err;
+}
+
+
+/* common buffer of tasklet and interrupt to send/receive messages */
+static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4];
+
+
+void snd_mixart_msg_tasklet( unsigned long arg)
+{
+	mixart_mgr_t *mgr = ( mixart_mgr_t*)(arg);
+	mixart_msg_t resp;
+	u32 msg, addr, type;
+	int err;
+
+	spin_lock(&mgr->lock);
+
+	while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) {
+		msg = mgr->msg_fifo[mgr->msg_fifo_readptr];
+		mgr->msg_fifo_readptr++;
+		mgr->msg_fifo_readptr %= MSG_FIFO_SIZE;
+
+		/* process the message ... */
+		addr = msg & ~MSG_TYPE_MASK;
+		type = msg & MSG_TYPE_MASK;
+
+		switch (type) {
+		case MSG_TYPE_ANSWER:
+			/* answer to a message on that we did not wait for (send_msg_nonblock) */
+			resp.message_id = 0;
+			resp.data = mixart_msg_data;
+			resp.size = sizeof(mixart_msg_data);
+			err = get_msg(mgr, &resp, addr);
+			if( err < 0 ) {
+				snd_printk(KERN_ERR "tasklet: error(%d) reading mf %x\n", err, msg);
+				break;
+			}
+
+			switch(resp.message_id) {
+			case MSG_STREAM_START_INPUT_STAGE_PACKET:
+			case MSG_STREAM_START_OUTPUT_STAGE_PACKET:
+			case MSG_STREAM_STOP_INPUT_STAGE_PACKET:
+			case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET:
+				if(mixart_msg_data[0])
+					snd_printk(KERN_ERR "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", mixart_msg_data[0]);
+				break;
+			default:
+				snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%d)\n",
+					   msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size);
+				break;
+			}
+			break;
+ 		case MSG_TYPE_NOTIFY:
+			/* msg contains no address ! do not get_msg() ! */
+		case MSG_TYPE_COMMAND:
+			/* get_msg() necessary */
+		default:
+			snd_printk(KERN_ERR "tasklet doesn't know what to do with message %x\n", msg);
+		} /* switch type */
+
+		/* decrement counter */
+		atomic_dec(&mgr->msg_processed);
+
+	} /* while there is a msg in fifo */
+
+	spin_unlock(&mgr->lock);
+}
+
+
+irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, dev_id, return IRQ_NONE);
+	int err;
+	mixart_msg_t resp;
+
+	u32 msg;
+	u32 it_reg;
+
+	spin_lock(&mgr->lock);
+
+	it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET));
+	if( !(it_reg & MIXART_OIDI) ) {
+		/* this device did not cause the interrupt */
+		spin_unlock(&mgr->lock);
+		return IRQ_NONE;
+	}
+
+	/* mask all interrupts */
+	writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET));
+
+	/* outdoorbell register clear */
+	it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
+	writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
+
+	/* clear interrupt */
+	writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) );
+
+	/* process interrupt */
+	while (retrieve_msg_frame(mgr, &msg)) {
+
+		switch (msg & MSG_TYPE_MASK) {
+		case MSG_TYPE_COMMAND:
+			resp.message_id = 0;
+			resp.data = mixart_msg_data;
+			resp.size = sizeof(mixart_msg_data);
+			err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK);
+			if( err < 0 ) {
+				snd_printk(KERN_ERR "interrupt: error(%d) reading mf %x\n", err, msg);
+				break;
+			}
+
+			if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) {
+				int i;
+				mixart_timer_notify_t *notify = (mixart_timer_notify_t*)mixart_msg_data;
+
+				for(i=0; i<notify->stream_count; i++) {
+
+					u32 buffer_id = notify->streams[i].buffer_id;
+					unsigned int chip_number =  (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */
+					unsigned int pcm_number  =  (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET;  /* pcm0 to 3  */
+					unsigned int sub_number  =   buffer_id & MIXART_NOTIFY_SUBS_MASK;             /* 0 to MIXART_PLAYBACK_STREAMS */
+					unsigned int is_capture  = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0);      /* playback == 0 / capture == 1 */
+
+					mixart_t *chip  = mgr->chip[chip_number];
+					mixart_stream_t *stream;
+
+					if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) {
+						snd_printk(KERN_ERR "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n",
+							   buffer_id, notify->streams[i].sample_pos_low_part);
+						break;
+					}
+
+					if (is_capture)
+						stream = &chip->capture_stream[pcm_number];
+					else
+						stream = &chip->playback_stream[pcm_number][sub_number];
+
+					if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) {
+						snd_pcm_runtime_t *runtime = stream->substream->runtime;
+						int elapsed = 0;
+						u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32;
+						sample_count |= notify->streams[i].sample_pos_low_part;
+
+						while (1) {
+							u64 new_elapse_pos = stream->abs_period_elapsed +  runtime->period_size;
+
+							if (new_elapse_pos > sample_count) {
+								break; /* while */
+							}
+							else {
+								elapsed = 1;
+								stream->buf_periods++;
+								if (stream->buf_periods >= runtime->periods)
+									stream->buf_periods = 0;
+
+								stream->abs_period_elapsed = new_elapse_pos;
+							}
+						}
+						stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed );
+
+						if(elapsed) {
+							spin_unlock(&mgr->lock);
+							snd_pcm_period_elapsed(stream->substream);
+							spin_lock(&mgr->lock);
+						}
+					}
+				}
+				break;
+			}
+			if(resp.message_id == MSG_SERVICES_REPORT_TRACES) {
+				if(resp.size > 1) {
+#ifndef __BIG_ENDIAN
+					/* Traces are text: the swapped msg_data has to be swapped back ! */
+					int i;
+					for(i=0; i<(resp.size/4); i++) {
+						(mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]);
+					}
+#endif
+					((char*)mixart_msg_data)[resp.size - 1] = 0;
+					snd_printdd("MIXART TRACE : %s\n", (char*)mixart_msg_data);
+				}
+				break;
+			}
+
+			snd_printdd("command %x not handled\n", resp.message_id);
+			break;
+
+		case MSG_TYPE_NOTIFY:
+			if(msg & MSG_CANCEL_NOTIFY_MASK) {
+				msg &= ~MSG_CANCEL_NOTIFY_MASK;
+				snd_printk(KERN_ERR "canceled notification %x !\n", msg);
+			}
+			/* no break, continue ! */
+		case MSG_TYPE_ANSWER:
+			/* answer or notification to a message we are waiting for*/
+			spin_lock(&mgr->msg_lock);
+			if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {
+				wake_up(&mgr->msg_sleep);
+				mgr->pending_event = 0;
+			}
+			/* answer to a message we did't want to wait for */
+			else {
+				mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
+				mgr->msg_fifo_writeptr++;
+				mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
+				tasklet_hi_schedule(&mgr->msg_taskq);
+			}
+			spin_unlock(&mgr->msg_lock);
+			break;
+		case MSG_TYPE_REQUEST:
+		default:
+			snd_printdd("interrupt received request %x\n", msg);
+			/* TODO : are there things to do here ? */
+			break;
+		} /* switch on msg type */
+	} /* while there are msgs */
+
+	/* allow interrupt again */
+	writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+
+	spin_unlock(&mgr->lock);
+
+	return IRQ_HANDLED;
+}
+
+
+void snd_mixart_init_mailbox(mixart_mgr_t *mgr)
+{
+	writel( 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) );
+	writel( 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) );
+
+	/* allow outbound messagebox to generate interrupts */
+	if(mgr->irq >= 0) {
+		writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+	}
+	return;
+}
+
+void snd_mixart_exit_mailbox(mixart_mgr_t *mgr)
+{
+	/* no more interrupts on outbound messagebox */
+	writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
+	return;
+}
+
+void snd_mixart_reset_board(mixart_mgr_t *mgr)
+{
+	/* reset miXart */
+	writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) );
+	return;
+}
diff -Nru a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart_core.h	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,607 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * low level interface with interrupt handling and mail box implementation
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_CORE_H
+#define __SOUND_MIXART_CORE_H
+
+
+enum mixart_message_id {
+	MSG_CONNECTOR_GET_AUDIO_INFO         = 0x050008,
+	MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL    = 0x050009,
+	MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL    = 0x05000A,
+
+	MSG_CONSOLE_MANAGER                  = 0x070000,
+	MSG_CONSOLE_GET_CLOCK_UID            = 0x070003,
+
+	MSG_PHYSICALIO_SET_LEVEL             = 0x0F0008,
+
+	MSG_STREAM_ADD_INPUT_GROUP           = 0x130000,
+	MSG_STREAM_ADD_OUTPUT_GROUP          = 0x130001,
+	MSG_STREAM_DELETE_GROUP              = 0x130004,
+	MSG_STREAM_START_STREAM_GRP_PACKET   = 0x130006,
+	MSG_STREAM_START_INPUT_STAGE_PACKET  = 0x130007,
+	MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130008,
+	MSG_STREAM_STOP_STREAM_GRP_PACKET    = 0x130009,
+	MSG_STREAM_STOP_INPUT_STAGE_PACKET   = 0x13000A,
+	MSG_STREAM_STOP_OUTPUT_STAGE_PACKET  = 0x13000B,
+	MSG_STREAM_SET_INPUT_STAGE_PARAM     = 0x13000F,
+	MSG_STREAM_SET_OUTPUT_STAGE_PARAM    = 0x130010,
+	MSG_STREAM_SET_IN_AUDIO_LEVEL        = 0x130015,
+	MSG_STREAM_SET_OUT_STREAM_LEVEL      = 0x130017,
+
+	MSG_SYSTEM_FIRST_ID                  = 0x160000,
+	MSG_SYSTEM_ENUM_PHYSICAL_IO          = 0x16000E,
+	MSG_SYSTEM_ENUM_PLAY_CONNECTOR       = 0x160017,
+	MSG_SYSTEM_ENUM_RECORD_CONNECTOR     = 0x160018,
+	MSG_SYSTEM_WAIT_SYNCHRO_CMD          = 0x16002C,
+	MSG_SYSTEM_SEND_SYNCHRO_CMD          = 0x16002D,
+
+	MSG_SERVICES_TIMER_NOTIFY            = 0x1D0404,
+	MSG_SERVICES_REPORT_TRACES           = 0x1D0700,
+
+	MSG_CLOCK_CHECK_PROPERTIES           = 0x200001,
+	MSG_CLOCK_SET_PROPERTIES             = 0x200002,
+};
+
+
+typedef struct mixart_msg mixart_msg_t;
+struct mixart_msg
+{
+	u32          message_id;
+	mixart_uid_t uid;
+	void*        data;
+	size_t       size;
+};
+
+/* structs used to communicate with miXart */
+
+typedef struct mixart_enum_connector_resp mixart_enum_connector_resp_t;
+struct mixart_enum_connector_resp
+{
+	u32  error_code;
+	u32  first_uid_offset;
+	u32  uid_count;
+	u32  current_uid_index;
+	mixart_uid_t uid[MIXART_MAX_PHYS_CONNECTORS];
+} __attribute__((packed));
+
+
+/* used for following struct */
+#define MIXART_FLOAT_P_22_0_TO_HEX      0x41b00000  /* 22.0f */
+#define MIXART_FLOAT_M_20_0_TO_HEX      0xc1a00000  /* -20.0f */
+#define MIXART_FLOAT____0_0_TO_HEX      0x00000000  /* 0.0f */
+
+typedef struct mixart_audio_info_req mixart_audio_info_req_t;
+struct mixart_audio_info_req
+{
+	u32 line_max_level;    /* float */
+	u32 micro_max_level;   /* float */
+	u32 cd_max_level;      /* float */
+} __attribute__((packed));
+
+typedef struct mixart_analog_hw_info mixart_analog_hw_info_t;
+struct mixart_analog_hw_info
+{
+	u32 is_present;
+	u32 hw_connection_type;
+	u32 max_level;         /* float */
+	u32 min_var_level;     /* float */
+	u32 max_var_level;     /* float */
+	u32 step_var_level;    /* float */
+	u32 fix_gain;          /* float */
+	u32 zero_var;          /* float */
+} __attribute__((packed));
+
+typedef struct mixart_digital_hw_info mixart_digital_hw_info_t;
+struct mixart_digital_hw_info
+{
+	u32   hw_connection_type;
+	u32   presence;
+	u32   clock;
+	u32   reserved;
+} __attribute__((packed));
+
+typedef struct mixart_analog_info mixart_analog_info_t;
+struct mixart_analog_info
+{
+	u32                     type_mask;
+	mixart_analog_hw_info_t micro_info;
+	mixart_analog_hw_info_t line_info;
+	mixart_analog_hw_info_t cd_info;
+	u32                     analog_level_present;
+} __attribute__((packed));
+
+typedef struct mixart_digital_info mixart_digital_info_t;
+struct mixart_digital_info
+{
+	u32 type_mask;
+	mixart_digital_hw_info_t aes_info;
+	mixart_digital_hw_info_t adat_info;
+} __attribute__((packed));
+
+typedef struct mixart_audio_info mixart_audio_info_t;
+struct mixart_audio_info
+{
+	u32                   clock_type_mask;
+	mixart_analog_info_t  analog_info;
+	mixart_digital_info_t digital_info;
+} __attribute__((packed));
+
+typedef struct mixart_audio_info_resp mixart_audio_info_resp_t;
+struct mixart_audio_info_resp
+{
+	u32                 txx_status;
+	mixart_audio_info_t info;
+} __attribute__((packed));
+
+
+/* used for nb_bytes_max_per_sample */
+#define MIXART_FLOAT_P__4_0_TO_HEX      0x40800000  /* +4.0f */
+#define MIXART_FLOAT_P__8_0_TO_HEX      0x41000000  /* +8.0f */
+
+typedef struct mixart_stream_info mixart_stream_info_t;
+struct mixart_stream_info
+{
+	u32 size_max_byte_frame;
+	u32 size_max_sample_frame;
+	u32 nb_bytes_max_per_sample;  /* float */
+} __attribute__((packed));
+
+/*  MSG_STREAM_ADD_INPUT_GROUP */
+/*  MSG_STREAM_ADD_OUTPUT_GROUP */
+
+typedef struct mixart_streaming_group_req mixart_streaming_group_req_t;
+struct mixart_streaming_group_req
+{
+	u32 stream_count;
+	u32 channel_count;
+	u32 user_grp_number;
+	u32 first_phys_audio;
+	u32 latency;
+	mixart_stream_info_t stream_info[32];
+	mixart_uid_t connector;
+	u32 flow_entry[32];
+} __attribute__((packed));
+
+typedef struct mixart_stream_desc mixart_stream_desc_t;
+struct mixart_stream_desc
+{
+	mixart_uid_t stream_uid;
+	u32          stream_desc;
+} __attribute__((packed));
+
+typedef struct mixart_streaming_group mixart_streaming_group_t;
+struct mixart_streaming_group
+{
+	u32                  status;
+	mixart_uid_t         group;
+	u32                  pipe_desc;
+	u32                  stream_count;
+	mixart_stream_desc_t stream[32];
+} __attribute__((packed));
+
+/* MSG_STREAM_DELETE_GROUP */
+
+/* request : mixart_uid_t group */
+
+typedef struct mixart_delete_group_resp mixart_delete_group_resp_t;
+struct mixart_delete_group_resp
+{
+	u32  status;
+	u32  unused[2];
+} __attribute__((packed));
+
+
+/* 	MSG_STREAM_START_INPUT_STAGE_PACKET  = 0x130000 + 7,
+	MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130000 + 8,
+	MSG_STREAM_STOP_INPUT_STAGE_PACKET   = 0x130000 + 10,
+	MSG_STREAM_STOP_OUTPUT_STAGE_PACKET  = 0x130000 + 11,
+ */
+
+typedef struct mixart_fx_couple_uid mixart_fx_couple_uid_t;
+struct mixart_fx_couple_uid
+{
+	mixart_uid_t uid_fx_code;
+	mixart_uid_t uid_fx_data;
+} __attribute__((packed));
+
+typedef struct mixart_txx_stream_desc mixart_txx_stream_desc_t;
+struct mixart_txx_stream_desc
+{
+	mixart_uid_t            uid_pipe;
+	u32                     stream_idx;
+	u32                     fx_number;
+	mixart_fx_couple_uid_t  uid_fx[4];
+} __attribute__((packed));
+
+typedef struct mixart_flow_info mixart_flow_info_t;
+struct mixart_flow_info
+{
+	mixart_txx_stream_desc_t  stream_desc;
+	u32                       flow_entry;
+	u32                       flow_phy_addr;
+} __attribute__((packed));
+
+typedef struct mixart_stream_state_req mixart_stream_state_req_t;
+struct mixart_stream_state_req
+{
+	u32                 delayed;
+	u64                 scheduler;
+	u32                 reserved4np[3];
+	u32                 stream_count;  /* set to 1 for instance */
+	mixart_flow_info_t  stream_info;   /* could be an array[stream_count] */
+} __attribute__((packed));
+
+/* 	MSG_STREAM_START_STREAM_GRP_PACKET   = 0x130000 + 6
+	MSG_STREAM_STOP_STREAM_GRP_PACKET    = 0x130000 + 9
+ */
+
+typedef struct mixart_group_state_req mixart_group_state_req_t;
+struct mixart_group_state_req
+{
+	u32           delayed;
+	u64           scheduler;
+	u32           reserved4np[2];
+	u32           pipe_count;    /* set to 1 for instance */
+	mixart_uid_t  pipe_uid[1];   /* could be an array[pipe_count] */
+} __attribute__((packed));
+
+typedef struct mixart_group_state_resp mixart_group_state_resp_t;
+struct mixart_group_state_resp
+{
+	u32           txx_status;
+	u64           scheduler;
+} __attribute__((packed));
+
+
+
+/* Structures used by the MSG_SERVICES_TIMER_NOTIFY command */
+
+typedef struct mixart_sample_pos mixart_sample_pos_t;
+struct mixart_sample_pos
+{
+	u32   buffer_id;
+	u32   validity;
+	u32   sample_pos_high_part;
+	u32   sample_pos_low_part;
+} __attribute__((packed));
+
+typedef struct mixart_timer_notify mixart_timer_notify_t;
+struct mixart_timer_notify
+{
+	u32                  stream_count;
+	mixart_sample_pos_t  streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS];
+} __attribute__((packed));
+
+
+/*	MSG_CONSOLE_GET_CLOCK_UID            = 0x070003,
+ */
+
+/* request is a uid with desc = MSG_CONSOLE_MANAGER | cardindex */
+
+typedef struct mixart_return_uid mixart_return_uid_t;
+struct mixart_return_uid
+{
+	u32 error_code;
+	mixart_uid_t uid;
+} __attribute__((packed));
+
+/*	MSG_CLOCK_CHECK_PROPERTIES           = 0x200001,
+	MSG_CLOCK_SET_PROPERTIES             = 0x200002,
+*/
+
+enum mixart_clock_generic_type {
+	CGT_NO_CLOCK,
+	CGT_INTERNAL_CLOCK,
+	CGT_PROGRAMMABLE_CLOCK,
+	CGT_INTERNAL_ENSLAVED_CLOCK,
+	CGT_EXTERNAL_CLOCK,
+	CGT_CURRENT_CLOCK
+};
+
+enum mixart_clock_mode {
+	CM_UNDEFINED,
+	CM_MASTER,
+	CM_SLAVE,
+	CM_STANDALONE,
+	CM_NOT_CONCERNED
+};
+
+
+typedef struct mixart_clock_properties mixart_clock_properties_t;
+struct mixart_clock_properties
+{
+	u32 error_code;
+	u32 validation_mask;
+	u32 frequency;
+	u32 reference_frequency;
+	u32 clock_generic_type;
+	u32 clock_mode;
+	mixart_uid_t uid_clock_source;
+	mixart_uid_t uid_event_source;
+	u32 event_mode;
+	u32 synchro_signal_presence;
+	u32 format;
+	u32 board_mask;
+	u32 nb_callers; /* set to 1 (see below) */
+	mixart_uid_t uid_caller[1];
+} __attribute__((packed));
+
+typedef struct mixart_clock_properties_resp mixart_clock_properties_resp_t;
+struct mixart_clock_properties_resp
+{
+	u32 status;
+	u32 clock_mode;
+} __attribute__((packed));
+
+
+/*	MSG_STREAM_SET_INPUT_STAGE_PARAM     = 0x13000F */
+/*	MSG_STREAM_SET_OUTPUT_STAGE_PARAM    = 0x130010 */
+
+enum mixart_coding_type {
+	CT_NOT_DEFINED,
+	CT_LINEAR,
+	CT_MPEG_L1,
+	CT_MPEG_L2,
+	CT_MPEG_L3,
+	CT_MPEG_L3_LSF,
+	CT_GSM
+};
+enum mixart_sample_type {
+	ST_NOT_DEFINED,
+	ST_FLOATING_POINT_32BE,
+	ST_FLOATING_POINT_32LE,
+	ST_FLOATING_POINT_64BE,
+	ST_FLOATING_POINT_64LE,
+	ST_FIXED_POINT_8,
+	ST_FIXED_POINT_16BE,
+	ST_FIXED_POINT_16LE,
+	ST_FIXED_POINT_24BE,
+	ST_FIXED_POINT_24LE,
+	ST_FIXED_POINT_32BE,
+	ST_FIXED_POINT_32LE,
+	ST_INTEGER_8,
+	ST_INTEGER_16BE,
+	ST_INTEGER_16LE,
+	ST_INTEGER_24BE,
+	ST_INTEGER_24LE,
+	ST_INTEGER_32BE,
+	ST_INTEGER_32LE
+};
+
+typedef struct mixart_stream_param_desc mixart_stream_param_desc_t;
+struct mixart_stream_param_desc
+{
+	u32 coding_type;  /* use enum mixart_coding_type */
+	u32 sample_type;  /* use enum mixart_sample_type */
+
+	union {
+		struct {
+			u32 linear_endian_ness;
+			u32 linear_bits;
+			u32 is_signed;
+			u32 is_float;
+		} linear_format_info;
+
+		struct {
+			u32 mpeg_layer;
+			u32 mpeg_mode;
+			u32 mpeg_mode_extension;
+			u32 mpeg_pre_emphasis;
+			u32 mpeg_has_padding_bit;
+			u32 mpeg_has_crc;
+			u32 mpeg_has_extension;
+			u32 mpeg_is_original;
+			u32 mpeg_has_copyright;
+		} mpeg_format_info;
+	} format_info;
+
+	u32 delayed;
+	u64 scheduler;
+	u32 sample_size;
+	u32 has_header;
+	u32 has_suffix;
+	u32 has_bitrate;
+	u32 samples_per_frame;
+	u32 bytes_per_frame;
+	u32 bytes_per_sample;
+	u32 sampling_freq;
+	u32 number_of_channel;
+	u32 stream_number;
+	u32 buffer_size;
+	u32 differed_time;
+	u32 reserved4np[3];
+	u32 pipe_count;                           /* set to 1 (array size !) */
+	u32 stream_count;                         /* set to 1 (array size !) */
+	mixart_txx_stream_desc_t stream_desc[1];  /* only one stream per command, but this could be an array */
+
+} __attribute__((packed));
+
+
+/*	MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL    = 0x050009,
+ */
+
+
+typedef struct mixart_get_out_audio_level mixart_get_out_audio_level_t;
+struct mixart_get_out_audio_level
+{
+	u32 txx_status;
+	u32 digital_level;   /* float */
+	u32 analog_level;    /* float */
+	u32 monitor_level;   /* float */
+	u32 mute;
+	u32 monitor_mute1;
+	u32 monitor_mute2;
+} __attribute__((packed));
+
+
+/*	MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL    = 0x05000A,
+ */
+
+/* used for valid_mask below */
+#define MIXART_AUDIO_LEVEL_ANALOG_MASK	0x01
+#define MIXART_AUDIO_LEVEL_DIGITAL_MASK	0x02
+#define MIXART_AUDIO_LEVEL_MONITOR_MASK	0x04
+#define MIXART_AUDIO_LEVEL_MUTE_MASK	0x08
+#define MIXART_AUDIO_LEVEL_MUTE_M1_MASK	0x10
+#define MIXART_AUDIO_LEVEL_MUTE_M2_MASK	0x20
+
+typedef struct mixart_set_out_audio_level mixart_set_out_audio_level_t;
+struct mixart_set_out_audio_level
+{
+	u32 delayed;
+	u64 scheduler;
+	u32 valid_mask1;
+	u32 valid_mask2;
+	u32 digital_level;   /* float */
+	u32 analog_level;    /* float */
+	u32 monitor_level;   /* float */
+	u32 mute;
+	u32 monitor_mute1;
+	u32 monitor_mute2;
+	u32 reserved4np;
+} __attribute__((packed));
+
+
+/*	MSG_SYSTEM_ENUM_PHYSICAL_IO          = 0x16000E,
+ */
+
+#define MIXART_MAX_PHYS_IO  (MIXART_MAX_CARDS * 2 * 2) /* 4 * (analog+digital) * (playback+capture) */
+
+typedef struct mixart_uid_enumeration mixart_uid_enumeration_t;
+struct mixart_uid_enumeration
+{
+	u32 error_code;
+	u32 first_uid_offset;
+	u32 nb_uid;
+	u32 current_uid_index;
+	mixart_uid_t uid[MIXART_MAX_PHYS_IO];
+} __attribute__((packed));
+
+
+/*	MSG_PHYSICALIO_SET_LEVEL             = 0x0F0008,
+	MSG_PHYSICALIO_GET_LEVEL             = 0x0F000C,
+*/
+
+typedef struct mixart_io_channel_level mixart_io_channel_level_t;
+struct mixart_io_channel_level
+{
+	u32 analog_level;   /* float */
+	u32 unused[2];
+} __attribute__((packed));
+
+typedef struct mixart_io_level mixart_io_level_t;
+struct mixart_io_level
+{
+	s32 channel; /* 0=left, 1=right, -1=both, -2=both same */
+	mixart_io_channel_level_t level[2];
+} __attribute__((packed));
+
+
+/*	MSG_STREAM_SET_IN_AUDIO_LEVEL        = 0x130015,
+ */
+
+typedef struct mixart_in_audio_level_info mixart_in_audio_level_info_t;
+struct mixart_in_audio_level_info
+{
+	mixart_uid_t connector;
+	u32 valid_mask1;
+	u32 valid_mask2;
+	u32 digital_level;
+	u32 analog_level;
+} __attribute__((packed));
+
+typedef struct mixart_set_in_audio_level_req mixart_set_in_audio_level_req_t;
+struct mixart_set_in_audio_level_req
+{
+	u32 delayed;
+	u64 scheduler;
+	u32 audio_count;  /* set to <= 2 */
+	u32 reserved4np;
+	mixart_in_audio_level_info_t level[2];
+} __attribute__((packed));
+
+/* response is a 32 bit status */
+
+
+/*	MSG_STREAM_SET_OUT_STREAM_LEVEL      = 0x130017,
+ */
+
+/* defines used for valid_mask1 */
+#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1		0x01
+#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO2		0x02
+#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO1	0x04
+#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2	0x08
+#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_1		0x10
+#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_2		0x20
+#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_1		0x40
+#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_2		0x80
+
+typedef struct mixart_out_stream_level_info mixart_out_stream_level_info_t;
+struct mixart_out_stream_level_info
+{
+	u32 valid_mask1;
+	u32 valid_mask2;
+	u32 left_to_out1_level;
+	u32 left_to_out2_level;
+	u32 right_to_out1_level;
+	u32 right_to_out2_level;
+	u32 digital_level1;
+	u32 digital_level2;
+	u32 mute1;
+	u32 mute2;
+} __attribute__((packed));
+
+typedef struct mixart_set_out_stream_level mixart_set_out_stream_level_t;
+struct mixart_set_out_stream_level
+{
+	mixart_txx_stream_desc_t desc;
+	mixart_out_stream_level_info_t out_level;
+} __attribute__((packed));
+
+typedef struct mixart_set_out_stream_level_req mixart_set_out_stream_level_req_t;
+struct mixart_set_out_stream_level_req
+{
+	u32 delayed;
+	u64 scheduler;
+	u32 reserved4np[2];
+	u32 nb_of_stream;  /* set to 1 */
+	mixart_set_out_stream_level_t stream_level; /* could be an array */
+} __attribute__((packed));
+
+/* response to this request is a u32 status value */
+
+
+/* exported */
+void snd_mixart_init_mailbox(mixart_mgr_t *mgr);
+void snd_mixart_exit_mailbox(mixart_mgr_t *mgr);
+
+int  snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data);
+int  snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event);
+int  snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request);
+
+irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+void snd_mixart_msg_tasklet( unsigned long arg);
+
+void snd_mixart_reset_board(mixart_mgr_t *mgr);
+
+#endif /* __SOUND_MIXART_CORE_H */
diff -Nru a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart_hwdep.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,571 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * hwdep device manager
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_mixer.h"
+#include "mixart_core.h"
+#include "mixart_hwdep.h"
+
+
+/* miXart hwdep interface id string */
+#define SND_MIXART_HWDEP_ID       "miXart Loader"
+
+static int mixart_hwdep_open(snd_hwdep_t *hw, struct file *file)
+{
+	return 0;
+}
+
+static int mixart_hwdep_release(snd_hwdep_t *hw, struct file *file)
+{
+	return 0;
+}
+
+/**
+ * wait for a value on a peudo register, exit with a timeout
+ *
+ * @param mgr pointer to miXart manager structure
+ * @param offset unsigned pseudo_register base + offset of value
+ * @param value value
+ * @param timeout timeout in centisenconds
+ */
+static int mixart_wait_nice_for_register_value(mixart_mgr_t *mgr, u32 offset, int is_egal, u32 value, unsigned long timeout)
+{
+	unsigned long end_time = jiffies + (timeout * HZ / 100);
+	u32 read;
+
+	do {	/* we may take too long time in this loop.
+		 * so give controls back to kernel if needed.
+		 */
+		cond_resched();
+
+		read = readl_be( MIXART_MEM( mgr, offset ));
+		if(is_egal) {
+			if(read == value) return 0;
+		}
+		else { /* wait for different value */
+			if(read != value) return 0;
+		}
+	} while ( time_after_eq(end_time, jiffies) );
+
+	return -EBUSY;
+}
+
+
+/*
+  structures needed to upload elf code packets 
+ */
+typedef struct snd_mixart_elf32_ehdr snd_mixart_elf32_ehdr_t;
+
+struct snd_mixart_elf32_ehdr {
+	u8      e_ident[16];
+	u16     e_type;
+	u16     e_machine;
+	u32     e_version;
+	u32     e_entry;
+	u32     e_phoff;
+	u32     e_shoff;
+	u32     e_flags;
+	u16     e_ehsize;
+	u16     e_phentsize;
+	u16     e_phnum;
+	u16     e_shentsize;
+	u16     e_shnum;
+	u16     e_shstrndx;
+};
+
+typedef struct snd_mixart_elf32_phdr snd_mixart_elf32_phdr_t;
+
+struct snd_mixart_elf32_phdr {
+	u32     p_type;
+	u32     p_offset;
+	u32     p_vaddr;
+	u32     p_paddr;
+	u32     p_filesz;
+	u32     p_memsz;
+	u32     p_flags;
+	u32     p_align;
+};
+
+static int mixart_load_elf(mixart_mgr_t *mgr, snd_hwdep_dsp_image_t *dsp )
+{
+	char                    elf32_magic_number[4] = {0x7f,'E','L','F'};
+	snd_mixart_elf32_ehdr_t elf_header;
+	int                     i;
+
+	if ( copy_from_user(&elf_header, dsp->image , sizeof(snd_mixart_elf32_ehdr_t)) )
+		return -EFAULT;
+
+	for( i=0; i<4; i++ )
+		if ( elf32_magic_number[i] != elf_header.e_ident[i] )
+			return -EINVAL;
+
+	if( elf_header.e_phoff != 0 ) {
+		snd_mixart_elf32_phdr_t     elf_programheader;
+
+		for( i=0; i < be16_to_cpu(elf_header.e_phnum); i++ ) {
+			u32 pos = be32_to_cpu(elf_header.e_phoff) + (u32)(i * be16_to_cpu(elf_header.e_phentsize));
+
+			if( copy_from_user( &elf_programheader, dsp->image + pos, sizeof(elf_programheader) ) )
+				return -EFAULT;
+
+			if(elf_programheader.p_type != 0) {
+				if( elf_programheader.p_filesz != 0 ) {
+					if(copy_from_user_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
+								dsp->image + be32_to_cpu( elf_programheader.p_offset ),
+								be32_to_cpu( elf_programheader.p_filesz )))
+						return -EFAULT;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
+{
+	mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO);
+
+	strcpy(info->id, "miXart");
+        info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX;
+
+	if (mgr->hwdep->dsp_loaded & (1 <<  MIXART_MOTHERBOARD_ELF_INDEX))
+		info->chip_ready = 1;
+
+	info->version = MIXART_DRIVER_VERSION;
+	return 0;
+}
+
+/*
+ * get basic information and init miXart
+ */
+
+/* audio IDs for request to the board */
+#define MIXART_FIRST_ANA_AUDIO_ID       0
+#define MIXART_FIRST_DIG_AUDIO_ID       8
+
+static int mixart_enum_connectors(mixart_mgr_t *mgr)
+{
+	u32 k;
+	int err;
+	mixart_msg_t request;
+	mixart_enum_connector_resp_t connector;
+	mixart_audio_info_req_t  audio_info_req;
+	mixart_audio_info_resp_t audio_info;
+
+	audio_info_req.line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;
+	audio_info_req.micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX;
+	audio_info_req.cd_max_level = MIXART_FLOAT____0_0_TO_HEX;
+
+	request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR;
+	request.uid = (mixart_uid_t){0,0};  /* board num = 0 */
+	request.data = NULL;
+	request.size = 0;
+
+	err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector);
+	if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
+		snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
+		return -EINVAL;
+	}
+
+	for(k=0; k < connector.uid_count; k++) {
+		mixart_pipe_t* pipe;
+
+		if(k < MIXART_FIRST_DIG_AUDIO_ID) {
+			pipe = &mgr->chip[k/2]->pipe_out_ana;
+		} else {
+			pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;
+		}
+		if(k & 1) {
+			pipe->uid_right_connector = connector.uid[k];   /* odd */
+		} else {
+			pipe->uid_left_connector = connector.uid[k];    /* even */
+		}
+
+		/* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */
+
+		/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
+		request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
+		request.uid = connector.uid[k];
+		request.data = &audio_info_req;
+		request.size = sizeof(audio_info_req);
+
+		err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info);
+		if( err < 0 ) {
+			snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+			return err;
+		}
+		/*snd_printk(KERN_DEBUG "play  analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/
+	}
+
+	request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;
+	request.uid = (mixart_uid_t){0,0};  /* board num = 0 */
+	request.data = NULL;
+	request.size = 0;
+
+	err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector);
+	if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
+		snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
+		return -EINVAL;
+	}
+
+	for(k=0; k < connector.uid_count; k++) {
+		mixart_pipe_t* pipe;
+
+		if(k < MIXART_FIRST_DIG_AUDIO_ID) {
+			pipe = &mgr->chip[k/2]->pipe_in_ana;
+		} else {
+			pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;
+		}
+		if(k & 1) {
+			pipe->uid_right_connector = connector.uid[k];   /* odd */
+		} else {
+			pipe->uid_left_connector = connector.uid[k];    /* even */
+		}
+
+		/* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */
+
+		/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
+		request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
+		request.uid = connector.uid[k];
+		request.data = &audio_info_req;
+		request.size = sizeof(audio_info_req);
+
+		err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info);
+		if( err < 0 ) {
+			snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
+			return err;
+		}
+		/*snd_printk(KERN_DEBUG "rec  analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/
+	}
+
+	return 0;
+}
+
+static int mixart_enum_physio(mixart_mgr_t *mgr)
+{
+	u32 k;
+	int err;
+	mixart_msg_t request;
+	mixart_uid_t get_console_mgr;
+	mixart_return_uid_t console_mgr;
+	mixart_uid_enumeration_t phys_io;
+
+	/* get the uid for the console manager */
+	get_console_mgr.object_id = 0;
+	get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */
+
+	request.message_id = MSG_CONSOLE_GET_CLOCK_UID;
+	request.uid = get_console_mgr;
+	request.data = &get_console_mgr;
+	request.size = sizeof(get_console_mgr);
+
+	err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);
+
+	if( (err < 0) || (console_mgr.error_code != 0) ) {
+		snd_printk(KERN_DEBUG "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr.error_code);
+		return -EINVAL;
+	}
+
+	/* used later for clock issues ! */
+	mgr->uid_console_manager = console_mgr.uid;
+
+	request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO;
+	request.uid = (mixart_uid_t){0,0};
+	request.data = &console_mgr.uid;
+	request.size = sizeof(console_mgr.uid);
+
+	err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);
+	if( (err < 0) || ( phys_io.error_code != 0 ) ) {
+		snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err, phys_io.error_code );
+		return -EINVAL;
+	}
+
+	snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2),  return -EINVAL); /* min 2 phys io per card (analog in + analog out) */
+
+	for(k=0; k<mgr->num_cards; k++) {
+		mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
+		mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k]; 
+	}
+
+	return 0;
+}
+
+
+static int mixart_first_init(mixart_mgr_t *mgr)
+{
+	u32 k;
+	int err;
+	mixart_msg_t request;
+
+	if((err = mixart_enum_connectors(mgr)) < 0) return err;
+
+	if((err = mixart_enum_physio(mgr)) < 0) return err;
+
+	/* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
+	/* though why not here */
+	request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
+	request.uid = (mixart_uid_t){0,0};
+	request.data = NULL;
+	request.size = 0;
+	/* this command has no data. response is a 32 bit status */
+	err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);
+	if( (err < 0) || (k != 0) ) {
+		snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
+		return err == 0 ? -EINVAL : err;
+	}
+
+	return 0;
+}
+
+
+/* firmware base addresses (when hard coded) */
+#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS   0x00600000
+
+static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
+{
+	mixart_mgr_t* mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO);
+	int           err, card_index;
+	u32           status_xilinx, status_elf, status_daught;
+	u32           val;
+
+	/* read motherboard xilinx status */
+	status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+	/* read elf status */
+	status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+	/* read daughterboard xilinx status */
+	status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+	/* motherboard xilinx status 5 will say that the board is performing a reset */
+	if( status_xilinx == 5 ) {
+		snd_printk( KERN_ERR "miXart is resetting !\n");
+		return -EAGAIN; /* try again later */
+	}
+
+	switch (dsp->index)   {
+	case MIXART_MOTHERBOARD_XLX_INDEX:
+
+		/* xilinx already loaded ? */ 
+		if( status_xilinx == 4 ) {
+			snd_printk( KERN_DEBUG "xilinx is already loaded !\n");
+			return 0;
+		}
+		/* the status should be 0 == "idle" */
+		if( status_xilinx != 0 ) {
+			snd_printk( KERN_ERR "xilinx load error ! status = %d\n", status_xilinx);
+			return -EIO; /* modprob -r may help ? */
+		}
+
+		/* check xilinx validity */
+		snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL);
+		snd_assert(dsp->length % 4 == 0, return -EINVAL);
+
+		/* set xilinx status to copying */
+		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+
+		/* setup xilinx base address */
+		writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));
+		/* setup code size for xilinx file */
+		writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));
+
+		/* copy xilinx code */
+		if (copy_from_user_toio(  MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS),  dsp->image,  dsp->length))
+			return -EFAULT;
+    
+		/* set xilinx status to copy finished */
+		writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
+
+		/* return, because no further processing needed */
+		return 0;
+
+	case MIXART_MOTHERBOARD_ELF_INDEX:
+
+		if( status_elf == 4 ) {
+			snd_printk( KERN_DEBUG "elf file already loaded !\n");
+			return 0;
+		}
+
+		/* the status should be 0 == "idle" */
+		if( status_elf != 0 ) {
+			snd_printk( KERN_ERR "elf load error ! status = %d\n", status_elf);
+			return -EIO; /* modprob -r may help ? */
+		}
+
+		/* wait for xilinx status == 4 */
+		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
+		if (err < 0) {
+			snd_printk( KERN_ERR "xilinx was not loaded or could not be started\n");
+			return err;
+		}
+
+		/* init some data on the card */
+		writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */
+		writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) );         /* reset pointer to flow table on miXart */
+
+		/* set elf status to copying */
+		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+
+		/* process the copying of the elf packets */
+		err = mixart_load_elf( mgr, dsp);
+		if (err < 0) return err;
+
+		/* set elf status to copy finished */
+		writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
+
+		/* wait for elf status == 4 */
+		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
+		if (err < 0) {
+			snd_printk( KERN_ERR "elf could not be started\n");
+			return err;
+		}
+
+		/* miXart waits at this point on the pointer to the flow table */
+		writel_be( (u32)mgr->flowinfo_physaddr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */
+
+		return 0;  /* return, another xilinx file has to be loaded before */
+
+	case MIXART_AESEBUBOARD_XLX_INDEX:
+	default:
+
+		/* elf and xilinx should be loaded */
+		if( (status_elf != 4) || (status_xilinx != 4) ) {
+			printk( KERN_ERR "xilinx or elf not successfully loaded\n");
+			return -EIO; /* modprob -r may help ? */
+		}
+
+		/* wait for daughter detection != 0 */
+		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
+		if (err < 0) {
+			snd_printk( KERN_ERR "error starting elf file\n");
+			return err;
+		}
+
+		/* the board type can now be retrieved */
+		mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET)));
+
+		if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE)
+			break;  /* no daughter board; the file does not have to be loaded, continue after the switch */
+
+		/* only if aesebu daughter board presence (elf code must run)  */ 
+		if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES )
+			return -EINVAL;
+
+		/* daughter should be idle */
+		if( status_daught != 0 ) {
+			printk( KERN_ERR "daughter load error ! status = %d\n", status_daught);
+			return -EIO; /* modprob -r may help ? */
+		}
+ 
+		/* check daughterboard xilinx validity */
+		snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL);
+		snd_assert(dsp->length % 4 == 0, return -EINVAL);
+
+		/* inform mixart about the size of the file */
+		writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
+
+		/* set daughterboard status to 1 */
+		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+		/* wait for status == 2 */
+		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
+		if (err < 0) {
+			snd_printk( KERN_ERR "daughter board load error\n");
+			return err;
+		}
+
+		/* get the address where to write the file */
+		val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));
+		snd_assert(val != 0, return -EINVAL);
+
+		/* copy daughterboard xilinx code */
+		if (copy_from_user_toio(  MIXART_MEM( mgr, val),  dsp->image,  dsp->length))
+			return -EFAULT;
+
+		/* set daughterboard status to 4 */
+		writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
+
+		/* continue with init */
+		break;
+	} /* end of switch file index*/
+
+        /* wait for daughter status == 3 */
+        err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
+        if (err < 0) {
+		snd_printk( KERN_ERR "daughter board could not be initialised\n");
+		return err;
+	}
+
+	/* init mailbox (communication with embedded) */
+	snd_mixart_init_mailbox(mgr);
+
+	/* first communication with embedded */
+	err = mixart_first_init(mgr);
+        if (err < 0) {
+		snd_printk( KERN_ERR "miXart could not be set up\n");
+		return err;
+	}
+
+       	/* create devices and mixer in accordance with HW options*/
+        for (card_index = 0; card_index < mgr->num_cards; card_index++) {
+		mixart_t *chip = mgr->chip[card_index];
+
+		if ((err = snd_mixart_create_pcm(chip)) < 0)
+			return err;
+
+		if (card_index == 0) {
+			if ((err = snd_mixart_create_mixer(chip->mgr)) < 0)
+	        		return err;
+		}
+
+		if ((err = snd_card_register(chip->card)) < 0)
+			return err;
+	};
+
+	snd_printdd("miXart firmware downloaded and successfully set up\n");
+
+	return 0;
+}
+
+
+int snd_mixart_hwdep_new(mixart_mgr_t *mgr)
+{
+	int err;
+	snd_hwdep_t *hw;
+
+	/* only create hwdep interface for first cardX (see "index" module parameter)*/
+	if ((err = snd_hwdep_new(mgr->chip[0]->card, SND_MIXART_HWDEP_ID, 0, &hw)) < 0)
+		return err;
+
+	hw->iface = SNDRV_HWDEP_IFACE_MIXART;
+	hw->private_data = mgr;
+	hw->ops.open = mixart_hwdep_open;
+	hw->ops.release = mixart_hwdep_release;
+	hw->ops.dsp_status = mixart_hwdep_dsp_status;
+	hw->ops.dsp_load = mixart_hwdep_dsp_load;
+	hw->exclusive = 1;
+	sprintf(hw->name,  SND_MIXART_HWDEP_ID);
+	mgr->hwdep = hw;
+	mgr->hwdep->dsp_loaded = 0;
+	return 0;
+}
diff -Nru a/sound/pci/mixart/mixart_hwdep.h b/sound/pci/mixart/mixart_hwdep.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart_hwdep.h	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,146 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * definitions and makros for basic card access
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_HWDEP_H
+#define __SOUND_MIXART_HWDEP_H
+
+#include <sound/hwdep.h>
+
+#define readl_be(x) be32_to_cpu(__raw_readl(x))
+#define writel_be(data,addr) __raw_writel(cpu_to_be32(data),addr)
+
+#define readl_le(x) le32_to_cpu(__raw_readl(x))
+#define writel_le(data,addr) __raw_writel(cpu_to_le32(data),addr)
+
+#define MIXART_MEM(mgr,x)	((mgr)->mem[0].virt + (x))
+#define MIXART_REG(mgr,x)	((mgr)->mem[1].virt + (x))
+
+
+/* Daughter board Type */
+#define DAUGHTER_TYPE_MASK     0x0F 
+#define DAUGHTER_VER_MASK      0xF0 
+#define DAUGHTER_TYPEVER_MASK  (DAUGHTER_TYPE_MASK|DAUGHTER_VER_MASK)
+ 
+#define MIXART_DAUGHTER_TYPE_NONE     0x00 
+#define MIXART_DAUGHTER_TYPE_COBRANET 0x08 
+#define MIXART_DAUGHTER_TYPE_AES      0x0E
+
+
+
+#define MIXART_BA0_SIZE 	(16 * 1024 * 1024) /* 16M */
+#define MIXART_BA1_SIZE 	(4  * 1024)        /* 4k */
+
+/*
+ * -----------BAR 0 --------------------------------------------------------------------------------------------------------
+ */
+#define  MIXART_PSEUDOREG                          0x2000                    /* base address for pseudoregister */
+
+#define  MIXART_PSEUDOREG_BOARDNUMBER              MIXART_PSEUDOREG+0        /* board number */
+
+/* perfmeter (available when elf loaded)*/
+#define  MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET  MIXART_PSEUDOREG+0x70     /* streaming load */
+#define  MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET  MIXART_PSEUDOREG+0x78     /* system load (reference)*/
+#define  MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET  MIXART_PSEUDOREG+0x7C     /* mailbox load */
+#define  MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET  MIXART_PSEUDOREG+0x74     /* interrupt handling  load */
+
+/* motherboard xilinx loader info */
+#define  MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET    MIXART_PSEUDOREG+0x9C     /* 0x00600000 */ 
+#define  MIXART_PSEUDOREG_MXLX_SIZE_OFFSET         MIXART_PSEUDOREG+0xA0     /* xilinx size in bytes */ 
+#define  MIXART_PSEUDOREG_MXLX_STATUS_OFFSET       MIXART_PSEUDOREG+0xA4     /* status = EMBEBBED_STAT_XXX */ 
+
+/* elf loader info */
+#define  MIXART_PSEUDOREG_ELF_STATUS_OFFSET        MIXART_PSEUDOREG+0xB0     /* status = EMBEBBED_STAT_XXX */ 
+
+/* 
+*  after the elf code is loaded, and the flowtable info was passed to it,
+*  the driver polls on this address, until it shows 1 (presence) or 2 (absence)
+*  once it is non-zero, the daughter board type may be read
+*/
+#define  MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET     MIXART_PSEUDOREG+0x990   
+
+/* Global info structure */
+#define  MIXART_PSEUDOREG_DBRD_TYPE_OFFSET         MIXART_PSEUDOREG+0x994    /* Type and version of daughterboard  */
+
+
+/* daughterboard xilinx loader info */
+#define  MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET    MIXART_PSEUDOREG+0x998    /* get the address here where to write the file */ 
+#define  MIXART_PSEUDOREG_DXLX_SIZE_OFFSET         MIXART_PSEUDOREG+0x99C    /* xilinx size in bytes */ 
+#define  MIXART_PSEUDOREG_DXLX_STATUS_OFFSET       MIXART_PSEUDOREG+0x9A0    /* status = EMBEBBED_STAT_XXX */ 
+
+/*  */
+#define  MIXART_FLOWTABLE_PTR                      0x3000                    /* pointer to flow table */
+
+/* mailbox addresses  */
+
+/* message DRV -> EMB */
+#define MSG_INBOUND_POST_HEAD       0x010008	/* DRV posts MF + increment4 */
+#define	MSG_INBOUND_POST_TAIL       0x01000C	/* EMB gets MF + increment4 */
+/* message EMB -> DRV */
+#define	MSG_OUTBOUND_POST_TAIL      0x01001C	/* DRV gets MF + increment4 */
+#define	MSG_OUTBOUND_POST_HEAD      0x010018	/* EMB posts MF + increment4 */
+/* Get Free Frames */
+#define MSG_INBOUND_FREE_TAIL       0x010004	/* DRV gets MFA + increment4 */
+#define MSG_OUTBOUND_FREE_TAIL      0x010014	/* EMB gets MFA + increment4 */
+/* Put Free Frames */
+#define MSG_OUTBOUND_FREE_HEAD      0x010010	/* DRV puts MFA + increment4 */
+#define MSG_INBOUND_FREE_HEAD       0x010000    /* EMB puts MFA + increment4 */
+
+/* firmware addresses of the message fifos */
+#define MSG_BOUND_STACK_SIZE        0x004000    /* size of each following stack */
+/* posted messages */
+#define MSG_OUTBOUND_POST_STACK     0x108000    /* stack of messages to the DRV */
+#define MSG_INBOUND_POST_STACK      0x104000    /* stack of messages to the EMB */
+/* available empty messages */
+#define MSG_OUTBOUND_FREE_STACK     0x10C000    /* stack of free enveloped for EMB */
+#define MSG_INBOUND_FREE_STACK      0x100000    /* stack of free enveloped for DRV */
+
+
+/* defines for mailbox message frames */
+#define MSG_FRAME_OFFSET            0x64
+#define MSG_FRAME_SIZE              0x6400
+#define MSG_FRAME_NUMBER            32
+#define MSG_FROM_AGENT_ITMF_OFFSET  (MSG_FRAME_OFFSET + (MSG_FRAME_SIZE * MSG_FRAME_NUMBER))
+#define MSG_TO_AGENT_ITMF_OFFSET    (MSG_FROM_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
+#define MSG_HOST_RSC_PROTECTION     (MSG_TO_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
+#define MSG_AGENT_RSC_PROTECTION    (MSG_HOST_RSC_PROTECTION + 4)
+
+
+/*
+ * -----------BAR 1 --------------------------------------------------------------------------------------------------------
+ */
+
+/* interrupt addresses and constants */
+#define MIXART_PCI_OMIMR_OFFSET                 0x34    /* outbound message interrupt mask register */
+#define MIXART_PCI_OMISR_OFFSET                 0x30    /* outbound message interrupt status register */
+#define MIXART_PCI_ODBR_OFFSET                  0x60    /* outbound doorbell register */
+
+#define MIXART_BA1_BRUTAL_RESET_OFFSET          0x68    /* write 1 in LSBit to reset board */
+
+#define MIXART_HOST_ALL_INTERRUPT_MASKED        0x02B   /* 0000 0010 1011 */
+#define MIXART_ALLOW_OUTBOUND_DOORBELL          0x023   /* 0000 0010 0011 */
+#define MIXART_OIDI                             0x008   /* 0000 0000 1000 */
+
+
+/* exported */
+int snd_mixart_hwdep_new(mixart_mgr_t *mgr);
+
+#endif /* __SOUND_MIXART_HWDEP_H */
diff -Nru a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart_mixer.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,1138 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * mixer callbacks
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "mixart.h"
+#include "mixart_core.h"
+#include "mixart_hwdep.h"
+#include <sound/control.h>
+#include "mixart_mixer.h"
+
+#define chip_t mixart_t
+
+static u32 mixart_analog_level[256] = {
+	0xc2c00000,		/* [000] -96.0 dB */
+	0xc2bf0000,		/* [001] -95.5 dB */
+	0xc2be0000,		/* [002] -95.0 dB */
+	0xc2bd0000,		/* [003] -94.5 dB */
+	0xc2bc0000,		/* [004] -94.0 dB */
+	0xc2bb0000,		/* [005] -93.5 dB */
+	0xc2ba0000,		/* [006] -93.0 dB */
+	0xc2b90000,		/* [007] -92.5 dB */
+	0xc2b80000,		/* [008] -92.0 dB */
+	0xc2b70000,		/* [009] -91.5 dB */
+	0xc2b60000,		/* [010] -91.0 dB */
+	0xc2b50000,		/* [011] -90.5 dB */
+	0xc2b40000,		/* [012] -90.0 dB */
+	0xc2b30000,		/* [013] -89.5 dB */
+	0xc2b20000,		/* [014] -89.0 dB */
+	0xc2b10000,		/* [015] -88.5 dB */
+	0xc2b00000,		/* [016] -88.0 dB */
+	0xc2af0000,		/* [017] -87.5 dB */
+	0xc2ae0000,		/* [018] -87.0 dB */
+	0xc2ad0000,		/* [019] -86.5 dB */
+	0xc2ac0000,		/* [020] -86.0 dB */
+	0xc2ab0000,		/* [021] -85.5 dB */
+	0xc2aa0000,		/* [022] -85.0 dB */
+	0xc2a90000,		/* [023] -84.5 dB */
+	0xc2a80000,		/* [024] -84.0 dB */
+	0xc2a70000,		/* [025] -83.5 dB */
+	0xc2a60000,		/* [026] -83.0 dB */
+	0xc2a50000,		/* [027] -82.5 dB */
+	0xc2a40000,		/* [028] -82.0 dB */
+	0xc2a30000,		/* [029] -81.5 dB */
+	0xc2a20000,		/* [030] -81.0 dB */
+	0xc2a10000,		/* [031] -80.5 dB */
+	0xc2a00000,		/* [032] -80.0 dB */
+	0xc29f0000,		/* [033] -79.5 dB */
+	0xc29e0000,		/* [034] -79.0 dB */
+	0xc29d0000,		/* [035] -78.5 dB */
+	0xc29c0000,		/* [036] -78.0 dB */
+	0xc29b0000,		/* [037] -77.5 dB */
+	0xc29a0000,		/* [038] -77.0 dB */
+	0xc2990000,		/* [039] -76.5 dB */
+	0xc2980000,		/* [040] -76.0 dB */
+	0xc2970000,		/* [041] -75.5 dB */
+	0xc2960000,		/* [042] -75.0 dB */
+	0xc2950000,		/* [043] -74.5 dB */
+	0xc2940000,		/* [044] -74.0 dB */
+	0xc2930000,		/* [045] -73.5 dB */
+	0xc2920000,		/* [046] -73.0 dB */
+	0xc2910000,		/* [047] -72.5 dB */
+	0xc2900000,		/* [048] -72.0 dB */
+	0xc28f0000,		/* [049] -71.5 dB */
+	0xc28e0000,		/* [050] -71.0 dB */
+	0xc28d0000,		/* [051] -70.5 dB */
+	0xc28c0000,		/* [052] -70.0 dB */
+	0xc28b0000,		/* [053] -69.5 dB */
+	0xc28a0000,		/* [054] -69.0 dB */
+	0xc2890000,		/* [055] -68.5 dB */
+	0xc2880000,		/* [056] -68.0 dB */
+	0xc2870000,		/* [057] -67.5 dB */
+	0xc2860000,		/* [058] -67.0 dB */
+	0xc2850000,		/* [059] -66.5 dB */
+	0xc2840000,		/* [060] -66.0 dB */
+	0xc2830000,		/* [061] -65.5 dB */
+	0xc2820000,		/* [062] -65.0 dB */
+	0xc2810000,		/* [063] -64.5 dB */
+	0xc2800000,		/* [064] -64.0 dB */
+	0xc27e0000,		/* [065] -63.5 dB */
+	0xc27c0000,		/* [066] -63.0 dB */
+	0xc27a0000,		/* [067] -62.5 dB */
+	0xc2780000,		/* [068] -62.0 dB */
+	0xc2760000,		/* [069] -61.5 dB */
+	0xc2740000,		/* [070] -61.0 dB */
+	0xc2720000,		/* [071] -60.5 dB */
+	0xc2700000,		/* [072] -60.0 dB */
+	0xc26e0000,		/* [073] -59.5 dB */
+	0xc26c0000,		/* [074] -59.0 dB */
+	0xc26a0000,		/* [075] -58.5 dB */
+	0xc2680000,		/* [076] -58.0 dB */
+	0xc2660000,		/* [077] -57.5 dB */
+	0xc2640000,		/* [078] -57.0 dB */
+	0xc2620000,		/* [079] -56.5 dB */
+	0xc2600000,		/* [080] -56.0 dB */
+	0xc25e0000,		/* [081] -55.5 dB */
+	0xc25c0000,		/* [082] -55.0 dB */
+	0xc25a0000,		/* [083] -54.5 dB */
+	0xc2580000,		/* [084] -54.0 dB */
+	0xc2560000,		/* [085] -53.5 dB */
+	0xc2540000,		/* [086] -53.0 dB */
+	0xc2520000,		/* [087] -52.5 dB */
+	0xc2500000,		/* [088] -52.0 dB */
+	0xc24e0000,		/* [089] -51.5 dB */
+	0xc24c0000,		/* [090] -51.0 dB */
+	0xc24a0000,		/* [091] -50.5 dB */
+	0xc2480000,		/* [092] -50.0 dB */
+	0xc2460000,		/* [093] -49.5 dB */
+	0xc2440000,		/* [094] -49.0 dB */
+	0xc2420000,		/* [095] -48.5 dB */
+	0xc2400000,		/* [096] -48.0 dB */
+	0xc23e0000,		/* [097] -47.5 dB */
+	0xc23c0000,		/* [098] -47.0 dB */
+	0xc23a0000,		/* [099] -46.5 dB */
+	0xc2380000,		/* [100] -46.0 dB */
+	0xc2360000,		/* [101] -45.5 dB */
+	0xc2340000,		/* [102] -45.0 dB */
+	0xc2320000,		/* [103] -44.5 dB */
+	0xc2300000,		/* [104] -44.0 dB */
+	0xc22e0000,		/* [105] -43.5 dB */
+	0xc22c0000,		/* [106] -43.0 dB */
+	0xc22a0000,		/* [107] -42.5 dB */
+	0xc2280000,		/* [108] -42.0 dB */
+	0xc2260000,		/* [109] -41.5 dB */
+	0xc2240000,		/* [110] -41.0 dB */
+	0xc2220000,		/* [111] -40.5 dB */
+	0xc2200000,		/* [112] -40.0 dB */
+	0xc21e0000,		/* [113] -39.5 dB */
+	0xc21c0000,		/* [114] -39.0 dB */
+	0xc21a0000,		/* [115] -38.5 dB */
+	0xc2180000,		/* [116] -38.0 dB */
+	0xc2160000,		/* [117] -37.5 dB */
+	0xc2140000,		/* [118] -37.0 dB */
+	0xc2120000,		/* [119] -36.5 dB */
+	0xc2100000,		/* [120] -36.0 dB */
+	0xc20e0000,		/* [121] -35.5 dB */
+	0xc20c0000,		/* [122] -35.0 dB */
+	0xc20a0000,		/* [123] -34.5 dB */
+	0xc2080000,		/* [124] -34.0 dB */
+	0xc2060000,		/* [125] -33.5 dB */
+	0xc2040000,		/* [126] -33.0 dB */
+	0xc2020000,		/* [127] -32.5 dB */
+	0xc2000000,		/* [128] -32.0 dB */
+	0xc1fc0000,		/* [129] -31.5 dB */
+	0xc1f80000,		/* [130] -31.0 dB */
+	0xc1f40000,		/* [131] -30.5 dB */
+	0xc1f00000,		/* [132] -30.0 dB */
+	0xc1ec0000,		/* [133] -29.5 dB */
+	0xc1e80000,		/* [134] -29.0 dB */
+	0xc1e40000,		/* [135] -28.5 dB */
+	0xc1e00000,		/* [136] -28.0 dB */
+	0xc1dc0000,		/* [137] -27.5 dB */
+	0xc1d80000,		/* [138] -27.0 dB */
+	0xc1d40000,		/* [139] -26.5 dB */
+	0xc1d00000,		/* [140] -26.0 dB */
+	0xc1cc0000,		/* [141] -25.5 dB */
+	0xc1c80000,		/* [142] -25.0 dB */
+	0xc1c40000,		/* [143] -24.5 dB */
+	0xc1c00000,		/* [144] -24.0 dB */
+	0xc1bc0000,		/* [145] -23.5 dB */
+	0xc1b80000,		/* [146] -23.0 dB */
+	0xc1b40000,		/* [147] -22.5 dB */
+	0xc1b00000,		/* [148] -22.0 dB */
+	0xc1ac0000,		/* [149] -21.5 dB */
+	0xc1a80000,		/* [150] -21.0 dB */
+	0xc1a40000,		/* [151] -20.5 dB */
+	0xc1a00000,		/* [152] -20.0 dB */
+	0xc19c0000,		/* [153] -19.5 dB */
+	0xc1980000,		/* [154] -19.0 dB */
+	0xc1940000,		/* [155] -18.5 dB */
+	0xc1900000,		/* [156] -18.0 dB */
+	0xc18c0000,		/* [157] -17.5 dB */
+	0xc1880000,		/* [158] -17.0 dB */
+	0xc1840000,		/* [159] -16.5 dB */
+	0xc1800000,		/* [160] -16.0 dB */
+	0xc1780000,		/* [161] -15.5 dB */
+	0xc1700000,		/* [162] -15.0 dB */
+	0xc1680000,		/* [163] -14.5 dB */
+	0xc1600000,		/* [164] -14.0 dB */
+	0xc1580000,		/* [165] -13.5 dB */
+	0xc1500000,		/* [166] -13.0 dB */
+	0xc1480000,		/* [167] -12.5 dB */
+	0xc1400000,		/* [168] -12.0 dB */
+	0xc1380000,		/* [169] -11.5 dB */
+	0xc1300000,		/* [170] -11.0 dB */
+	0xc1280000,		/* [171] -10.5 dB */
+	0xc1200000,		/* [172] -10.0 dB */
+	0xc1180000,		/* [173] -9.5 dB */
+	0xc1100000,		/* [174] -9.0 dB */
+	0xc1080000,		/* [175] -8.5 dB */
+	0xc1000000,		/* [176] -8.0 dB */
+	0xc0f00000,		/* [177] -7.5 dB */
+	0xc0e00000,		/* [178] -7.0 dB */
+	0xc0d00000,		/* [179] -6.5 dB */
+	0xc0c00000,		/* [180] -6.0 dB */
+	0xc0b00000,		/* [181] -5.5 dB */
+	0xc0a00000,		/* [182] -5.0 dB */
+	0xc0900000,		/* [183] -4.5 dB */
+	0xc0800000,		/* [184] -4.0 dB */
+	0xc0600000,		/* [185] -3.5 dB */
+	0xc0400000,		/* [186] -3.0 dB */
+	0xc0200000,		/* [187] -2.5 dB */
+	0xc0000000,		/* [188] -2.0 dB */
+	0xbfc00000,		/* [189] -1.5 dB */
+	0xbf800000,		/* [190] -1.0 dB */
+	0xbf000000,		/* [191] -0.5 dB */
+	0x00000000,		/* [192] 0.0 dB */
+	0x3f000000,		/* [193] 0.5 dB */
+	0x3f800000,		/* [194] 1.0 dB */
+	0x3fc00000,		/* [195] 1.5 dB */
+	0x40000000,		/* [196] 2.0 dB */
+	0x40200000,		/* [197] 2.5 dB */
+	0x40400000,		/* [198] 3.0 dB */
+	0x40600000,		/* [199] 3.5 dB */
+	0x40800000,		/* [200] 4.0 dB */
+	0x40900000,		/* [201] 4.5 dB */
+	0x40a00000,		/* [202] 5.0 dB */
+	0x40b00000,		/* [203] 5.5 dB */
+	0x40c00000,		/* [204] 6.0 dB */
+	0x40d00000,		/* [205] 6.5 dB */
+	0x40e00000,		/* [206] 7.0 dB */
+	0x40f00000,		/* [207] 7.5 dB */
+	0x41000000,		/* [208] 8.0 dB */
+	0x41080000,		/* [209] 8.5 dB */
+	0x41100000,		/* [210] 9.0 dB */
+	0x41180000,		/* [211] 9.5 dB */
+	0x41200000,		/* [212] 10.0 dB */
+	0x41280000,		/* [213] 10.5 dB */
+	0x41300000,		/* [214] 11.0 dB */
+	0x41380000,		/* [215] 11.5 dB */
+	0x41400000,		/* [216] 12.0 dB */
+	0x41480000,		/* [217] 12.5 dB */
+	0x41500000,		/* [218] 13.0 dB */
+	0x41580000,		/* [219] 13.5 dB */
+	0x41600000,		/* [220] 14.0 dB */
+	0x41680000,		/* [221] 14.5 dB */
+	0x41700000,		/* [222] 15.0 dB */
+	0x41780000,		/* [223] 15.5 dB */
+	0x41800000,		/* [224] 16.0 dB */
+	0x41840000,		/* [225] 16.5 dB */
+	0x41880000,		/* [226] 17.0 dB */
+	0x418c0000,		/* [227] 17.5 dB */
+	0x41900000,		/* [228] 18.0 dB */
+	0x41940000,		/* [229] 18.5 dB */
+	0x41980000,		/* [230] 19.0 dB */
+	0x419c0000,		/* [231] 19.5 dB */
+	0x41a00000,		/* [232] 20.0 dB */
+	0x41a40000,		/* [233] 20.5 dB */
+	0x41a80000,		/* [234] 21.0 dB */
+	0x41ac0000,		/* [235] 21.5 dB */
+	0x41b00000,		/* [236] 22.0 dB */
+	0x41b40000,		/* [237] 22.5 dB */
+	0x41b80000,		/* [238] 23.0 dB */
+	0x41bc0000,		/* [239] 23.5 dB */
+	0x41c00000,		/* [240] 24.0 dB */
+	0x41c40000,		/* [241] 24.5 dB */
+	0x41c80000,		/* [242] 25.0 dB */
+	0x41cc0000,		/* [243] 25.5 dB */
+	0x41d00000,		/* [244] 26.0 dB */
+	0x41d40000,		/* [245] 26.5 dB */
+	0x41d80000,		/* [246] 27.0 dB */
+	0x41dc0000,		/* [247] 27.5 dB */
+	0x41e00000,		/* [248] 28.0 dB */
+	0x41e40000,		/* [249] 28.5 dB */
+	0x41e80000,		/* [250] 29.0 dB */
+	0x41ec0000,		/* [251] 29.5 dB */
+	0x41f00000,		/* [252] 30.0 dB */
+	0x41f40000,		/* [253] 30.5 dB */
+	0x41f80000,		/* [254] 31.0 dB */
+	0x41fc0000,		/* [255] 31.5 dB */
+};
+
+#define MIXART_ANALOG_CAPTURE_LEVEL_MIN   0      /* -96.0 dB + 8.0 dB = -88.0 dB */
+#define MIXART_ANALOG_CAPTURE_LEVEL_MAX   255    /*  31.5 dB + 8.0 dB =  39.5 dB */
+#define MIXART_ANALOG_CAPTURE_ZERO_LEVEL  176    /*  -8.0 dB + 8.0 dB =  0.0 dB */
+
+#define MIXART_ANALOG_PLAYBACK_LEVEL_MIN  0      /* -96.0 dB + 1.5 dB = -94.5 dB (possible is down to (-114.0+1.5)dB) */
+#define MIXART_ANALOG_PLAYBACK_LEVEL_MAX  192    /*   0.0 dB + 1.5 dB =  1.5 dB */
+#define MIXART_ANALOG_PLAYBACK_ZERO_LEVEL 189    /*  -1.5 dB + 1.5 dB =  0.0 dB */
+
+static int mixart_update_analog_audio_level(mixart_t* chip, int is_capture)
+{
+	int i, err;
+	mixart_msg_t request;
+	mixart_io_level_t io_level;
+	mixart_return_uid_t resp;
+
+	memset(&io_level, 0, sizeof(io_level));
+	io_level.channel = -1; /* left and right */
+
+	for(i=0; i<2; i++) {
+		if(is_capture) {
+			io_level.level[i].analog_level = mixart_analog_level[chip->analog_capture_volume[i]];
+		} else {
+			if(chip->analog_playback_active[i])
+				io_level.level[i].analog_level = mixart_analog_level[chip->analog_playback_volume[i]];
+			else
+				io_level.level[i].analog_level = mixart_analog_level[MIXART_ANALOG_PLAYBACK_LEVEL_MIN];
+		}
+	}
+
+	if(is_capture)	request.uid = chip->uid_in_analog_physio;
+	else		request.uid = chip->uid_out_analog_physio;
+	request.message_id = MSG_PHYSICALIO_SET_LEVEL;
+	request.data = &io_level;
+	request.size = sizeof(io_level);
+
+	err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+	if((err<0) || (resp.error_code)) {
+		snd_printk(KERN_DEBUG "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n", chip->chip_idx, is_capture, resp.error_code);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * analog level control
+ */
+static int mixart_analog_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	if(kcontrol->private_value == 0) {	/* playback */
+		uinfo->value.integer.min = MIXART_ANALOG_PLAYBACK_LEVEL_MIN;  /* -96 dB */
+		uinfo->value.integer.max = MIXART_ANALOG_PLAYBACK_LEVEL_MAX;  /* 0 dB */
+	} else {				/* capture */
+		uinfo->value.integer.min = MIXART_ANALOG_CAPTURE_LEVEL_MIN;   /* -96 dB */
+		uinfo->value.integer.max = MIXART_ANALOG_CAPTURE_LEVEL_MAX;   /* 31.5 dB */
+	}
+	return 0;
+}
+
+static int mixart_analog_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	down(&chip->mgr->mixer_mutex);
+	if(kcontrol->private_value == 0) {	/* playback */
+		ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
+		ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
+	} else {				/* capture */
+		ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
+		ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
+	}
+	up(&chip->mgr->mixer_mutex);
+	return 0;
+}
+
+static int mixart_analog_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int changed = 0;
+	int is_capture, i;
+
+	down(&chip->mgr->mixer_mutex);
+	is_capture = (kcontrol->private_value != 0);
+	for(i=0; i<2; i++) {
+		int  new_volume = ucontrol->value.integer.value[i];
+		int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i];
+		if(*stored_volume != new_volume) {
+			*stored_volume = new_volume;
+			changed = 1;
+		}
+	}
+	if(changed)	mixart_update_analog_audio_level(chip, is_capture);
+	up(&chip->mgr->mixer_mutex);
+	return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_analog_level = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	/* name will be filled later */
+	.info =		mixart_analog_vol_info,
+	.get =		mixart_analog_vol_get,
+	.put =		mixart_analog_vol_put,
+};
+
+/* shared */
+static int mixart_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int mixart_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+
+	down(&chip->mgr->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->analog_playback_active[0];
+	ucontrol->value.integer.value[1] = chip->analog_playback_active[1];
+	up(&chip->mgr->mixer_mutex);
+	return 0;
+}
+
+static int mixart_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int i, changed = 0;
+	down(&chip->mgr->mixer_mutex);
+	for(i=0; i<2; i++) {
+		if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
+			chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
+			changed = 1;
+		}
+	}
+	if(changed)	mixart_update_analog_audio_level(chip, 0); /* update playback levels */
+	up(&chip->mgr->mixer_mutex);
+	return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_output_switch = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Master Playback Switch",
+	.info =         mixart_sw_info,		/* shared */
+	.get =          mixart_audio_sw_get,
+	.put =          mixart_audio_sw_put
+};
+
+static u32 mixart_digital_level[256] = {
+	0x00000000,		/* [000] = 0.00e+000 = mute if <= -109.5dB */
+	0x366e1c7a,		/* [001] = 3.55e-006 = pow(10.0, 0.05 * -109.0dB) */
+	0x367c3860,		/* [002] = 3.76e-006 = pow(10.0, 0.05 * -108.5dB) */
+	0x36859525,		/* [003] = 3.98e-006 = pow(10.0, 0.05 * -108.0dB) */
+	0x368d7f74,		/* [004] = 4.22e-006 = pow(10.0, 0.05 * -107.5dB) */
+	0x3695e1d4,		/* [005] = 4.47e-006 = pow(10.0, 0.05 * -107.0dB) */
+	0x369ec362,		/* [006] = 4.73e-006 = pow(10.0, 0.05 * -106.5dB) */
+	0x36a82ba8,		/* [007] = 5.01e-006 = pow(10.0, 0.05 * -106.0dB) */
+	0x36b222a0,		/* [008] = 5.31e-006 = pow(10.0, 0.05 * -105.5dB) */
+	0x36bcb0c1,		/* [009] = 5.62e-006 = pow(10.0, 0.05 * -105.0dB) */
+	0x36c7defd,		/* [010] = 5.96e-006 = pow(10.0, 0.05 * -104.5dB) */
+	0x36d3b6d3,		/* [011] = 6.31e-006 = pow(10.0, 0.05 * -104.0dB) */
+	0x36e0424e,		/* [012] = 6.68e-006 = pow(10.0, 0.05 * -103.5dB) */
+	0x36ed8c14,		/* [013] = 7.08e-006 = pow(10.0, 0.05 * -103.0dB) */
+	0x36fb9f6c,		/* [014] = 7.50e-006 = pow(10.0, 0.05 * -102.5dB) */
+	0x37054423,		/* [015] = 7.94e-006 = pow(10.0, 0.05 * -102.0dB) */
+	0x370d29a5,		/* [016] = 8.41e-006 = pow(10.0, 0.05 * -101.5dB) */
+	0x371586f0,		/* [017] = 8.91e-006 = pow(10.0, 0.05 * -101.0dB) */
+	0x371e631b,		/* [018] = 9.44e-006 = pow(10.0, 0.05 * -100.5dB) */
+	0x3727c5ac,		/* [019] = 1.00e-005 = pow(10.0, 0.05 * -100.0dB) */
+	0x3731b69a,		/* [020] = 1.06e-005 = pow(10.0, 0.05 * -99.5dB) */
+	0x373c3e53,		/* [021] = 1.12e-005 = pow(10.0, 0.05 * -99.0dB) */
+	0x374765c8,		/* [022] = 1.19e-005 = pow(10.0, 0.05 * -98.5dB) */
+	0x3753366f,		/* [023] = 1.26e-005 = pow(10.0, 0.05 * -98.0dB) */
+	0x375fba4f,		/* [024] = 1.33e-005 = pow(10.0, 0.05 * -97.5dB) */
+	0x376cfc07,		/* [025] = 1.41e-005 = pow(10.0, 0.05 * -97.0dB) */
+	0x377b06d5,		/* [026] = 1.50e-005 = pow(10.0, 0.05 * -96.5dB) */
+	0x3784f352,		/* [027] = 1.58e-005 = pow(10.0, 0.05 * -96.0dB) */
+	0x378cd40b,		/* [028] = 1.68e-005 = pow(10.0, 0.05 * -95.5dB) */
+	0x37952c42,		/* [029] = 1.78e-005 = pow(10.0, 0.05 * -95.0dB) */
+	0x379e030e,		/* [030] = 1.88e-005 = pow(10.0, 0.05 * -94.5dB) */
+	0x37a75fef,		/* [031] = 2.00e-005 = pow(10.0, 0.05 * -94.0dB) */
+	0x37b14ad5,		/* [032] = 2.11e-005 = pow(10.0, 0.05 * -93.5dB) */
+	0x37bbcc2c,		/* [033] = 2.24e-005 = pow(10.0, 0.05 * -93.0dB) */
+	0x37c6ecdd,		/* [034] = 2.37e-005 = pow(10.0, 0.05 * -92.5dB) */
+	0x37d2b65a,		/* [035] = 2.51e-005 = pow(10.0, 0.05 * -92.0dB) */
+	0x37df32a3,		/* [036] = 2.66e-005 = pow(10.0, 0.05 * -91.5dB) */
+	0x37ec6c50,		/* [037] = 2.82e-005 = pow(10.0, 0.05 * -91.0dB) */
+	0x37fa6e9b,		/* [038] = 2.99e-005 = pow(10.0, 0.05 * -90.5dB) */
+	0x3804a2b3,		/* [039] = 3.16e-005 = pow(10.0, 0.05 * -90.0dB) */
+	0x380c7ea4,		/* [040] = 3.35e-005 = pow(10.0, 0.05 * -89.5dB) */
+	0x3814d1cc,		/* [041] = 3.55e-005 = pow(10.0, 0.05 * -89.0dB) */
+	0x381da33c,		/* [042] = 3.76e-005 = pow(10.0, 0.05 * -88.5dB) */
+	0x3826fa6f,		/* [043] = 3.98e-005 = pow(10.0, 0.05 * -88.0dB) */
+	0x3830df51,		/* [044] = 4.22e-005 = pow(10.0, 0.05 * -87.5dB) */
+	0x383b5a49,		/* [045] = 4.47e-005 = pow(10.0, 0.05 * -87.0dB) */
+	0x3846743b,		/* [046] = 4.73e-005 = pow(10.0, 0.05 * -86.5dB) */
+	0x38523692,		/* [047] = 5.01e-005 = pow(10.0, 0.05 * -86.0dB) */
+	0x385eab48,		/* [048] = 5.31e-005 = pow(10.0, 0.05 * -85.5dB) */
+	0x386bdcf1,		/* [049] = 5.62e-005 = pow(10.0, 0.05 * -85.0dB) */
+	0x3879d6bc,		/* [050] = 5.96e-005 = pow(10.0, 0.05 * -84.5dB) */
+	0x38845244,		/* [051] = 6.31e-005 = pow(10.0, 0.05 * -84.0dB) */
+	0x388c2971,		/* [052] = 6.68e-005 = pow(10.0, 0.05 * -83.5dB) */
+	0x3894778d,		/* [053] = 7.08e-005 = pow(10.0, 0.05 * -83.0dB) */
+	0x389d43a4,		/* [054] = 7.50e-005 = pow(10.0, 0.05 * -82.5dB) */
+	0x38a6952c,		/* [055] = 7.94e-005 = pow(10.0, 0.05 * -82.0dB) */
+	0x38b0740f,		/* [056] = 8.41e-005 = pow(10.0, 0.05 * -81.5dB) */
+	0x38bae8ac,		/* [057] = 8.91e-005 = pow(10.0, 0.05 * -81.0dB) */
+	0x38c5fbe2,		/* [058] = 9.44e-005 = pow(10.0, 0.05 * -80.5dB) */
+	0x38d1b717,		/* [059] = 1.00e-004 = pow(10.0, 0.05 * -80.0dB) */
+	0x38de2440,		/* [060] = 1.06e-004 = pow(10.0, 0.05 * -79.5dB) */
+	0x38eb4de8,		/* [061] = 1.12e-004 = pow(10.0, 0.05 * -79.0dB) */
+	0x38f93f3a,		/* [062] = 1.19e-004 = pow(10.0, 0.05 * -78.5dB) */
+	0x39040206,		/* [063] = 1.26e-004 = pow(10.0, 0.05 * -78.0dB) */
+	0x390bd472,		/* [064] = 1.33e-004 = pow(10.0, 0.05 * -77.5dB) */
+	0x39141d84,		/* [065] = 1.41e-004 = pow(10.0, 0.05 * -77.0dB) */
+	0x391ce445,		/* [066] = 1.50e-004 = pow(10.0, 0.05 * -76.5dB) */
+	0x39263027,		/* [067] = 1.58e-004 = pow(10.0, 0.05 * -76.0dB) */
+	0x3930090d,		/* [068] = 1.68e-004 = pow(10.0, 0.05 * -75.5dB) */
+	0x393a7753,		/* [069] = 1.78e-004 = pow(10.0, 0.05 * -75.0dB) */
+	0x394583d2,		/* [070] = 1.88e-004 = pow(10.0, 0.05 * -74.5dB) */
+	0x395137ea,		/* [071] = 2.00e-004 = pow(10.0, 0.05 * -74.0dB) */
+	0x395d9d8a,		/* [072] = 2.11e-004 = pow(10.0, 0.05 * -73.5dB) */
+	0x396abf37,		/* [073] = 2.24e-004 = pow(10.0, 0.05 * -73.0dB) */
+	0x3978a814,		/* [074] = 2.37e-004 = pow(10.0, 0.05 * -72.5dB) */
+	0x3983b1f8,		/* [075] = 2.51e-004 = pow(10.0, 0.05 * -72.0dB) */
+	0x398b7fa6,		/* [076] = 2.66e-004 = pow(10.0, 0.05 * -71.5dB) */
+	0x3993c3b2,		/* [077] = 2.82e-004 = pow(10.0, 0.05 * -71.0dB) */
+	0x399c8521,		/* [078] = 2.99e-004 = pow(10.0, 0.05 * -70.5dB) */
+	0x39a5cb5f,		/* [079] = 3.16e-004 = pow(10.0, 0.05 * -70.0dB) */
+	0x39af9e4d,		/* [080] = 3.35e-004 = pow(10.0, 0.05 * -69.5dB) */
+	0x39ba063f,		/* [081] = 3.55e-004 = pow(10.0, 0.05 * -69.0dB) */
+	0x39c50c0b,		/* [082] = 3.76e-004 = pow(10.0, 0.05 * -68.5dB) */
+	0x39d0b90a,		/* [083] = 3.98e-004 = pow(10.0, 0.05 * -68.0dB) */
+	0x39dd1726,		/* [084] = 4.22e-004 = pow(10.0, 0.05 * -67.5dB) */
+	0x39ea30db,		/* [085] = 4.47e-004 = pow(10.0, 0.05 * -67.0dB) */
+	0x39f81149,		/* [086] = 4.73e-004 = pow(10.0, 0.05 * -66.5dB) */
+	0x3a03621b,		/* [087] = 5.01e-004 = pow(10.0, 0.05 * -66.0dB) */
+	0x3a0b2b0d,		/* [088] = 5.31e-004 = pow(10.0, 0.05 * -65.5dB) */
+	0x3a136a16,		/* [089] = 5.62e-004 = pow(10.0, 0.05 * -65.0dB) */
+	0x3a1c2636,		/* [090] = 5.96e-004 = pow(10.0, 0.05 * -64.5dB) */
+	0x3a2566d5,		/* [091] = 6.31e-004 = pow(10.0, 0.05 * -64.0dB) */
+	0x3a2f33cd,		/* [092] = 6.68e-004 = pow(10.0, 0.05 * -63.5dB) */
+	0x3a399570,		/* [093] = 7.08e-004 = pow(10.0, 0.05 * -63.0dB) */
+	0x3a44948c,		/* [094] = 7.50e-004 = pow(10.0, 0.05 * -62.5dB) */
+	0x3a503a77,		/* [095] = 7.94e-004 = pow(10.0, 0.05 * -62.0dB) */
+	0x3a5c9112,		/* [096] = 8.41e-004 = pow(10.0, 0.05 * -61.5dB) */
+	0x3a69a2d7,		/* [097] = 8.91e-004 = pow(10.0, 0.05 * -61.0dB) */
+	0x3a777ada,		/* [098] = 9.44e-004 = pow(10.0, 0.05 * -60.5dB) */
+	0x3a83126f,		/* [099] = 1.00e-003 = pow(10.0, 0.05 * -60.0dB) */
+	0x3a8ad6a8,		/* [100] = 1.06e-003 = pow(10.0, 0.05 * -59.5dB) */
+	0x3a9310b1,		/* [101] = 1.12e-003 = pow(10.0, 0.05 * -59.0dB) */
+	0x3a9bc784,		/* [102] = 1.19e-003 = pow(10.0, 0.05 * -58.5dB) */
+	0x3aa50287,		/* [103] = 1.26e-003 = pow(10.0, 0.05 * -58.0dB) */
+	0x3aaec98e,		/* [104] = 1.33e-003 = pow(10.0, 0.05 * -57.5dB) */
+	0x3ab924e5,		/* [105] = 1.41e-003 = pow(10.0, 0.05 * -57.0dB) */
+	0x3ac41d56,		/* [106] = 1.50e-003 = pow(10.0, 0.05 * -56.5dB) */
+	0x3acfbc31,		/* [107] = 1.58e-003 = pow(10.0, 0.05 * -56.0dB) */
+	0x3adc0b51,		/* [108] = 1.68e-003 = pow(10.0, 0.05 * -55.5dB) */
+	0x3ae91528,		/* [109] = 1.78e-003 = pow(10.0, 0.05 * -55.0dB) */
+	0x3af6e4c6,		/* [110] = 1.88e-003 = pow(10.0, 0.05 * -54.5dB) */
+	0x3b02c2f2,		/* [111] = 2.00e-003 = pow(10.0, 0.05 * -54.0dB) */
+	0x3b0a8276,		/* [112] = 2.11e-003 = pow(10.0, 0.05 * -53.5dB) */
+	0x3b12b782,		/* [113] = 2.24e-003 = pow(10.0, 0.05 * -53.0dB) */
+	0x3b1b690d,		/* [114] = 2.37e-003 = pow(10.0, 0.05 * -52.5dB) */
+	0x3b249e76,		/* [115] = 2.51e-003 = pow(10.0, 0.05 * -52.0dB) */
+	0x3b2e5f8f,		/* [116] = 2.66e-003 = pow(10.0, 0.05 * -51.5dB) */
+	0x3b38b49f,		/* [117] = 2.82e-003 = pow(10.0, 0.05 * -51.0dB) */
+	0x3b43a669,		/* [118] = 2.99e-003 = pow(10.0, 0.05 * -50.5dB) */
+	0x3b4f3e37,		/* [119] = 3.16e-003 = pow(10.0, 0.05 * -50.0dB) */
+	0x3b5b85e0,		/* [120] = 3.35e-003 = pow(10.0, 0.05 * -49.5dB) */
+	0x3b6887cf,		/* [121] = 3.55e-003 = pow(10.0, 0.05 * -49.0dB) */
+	0x3b764f0e,		/* [122] = 3.76e-003 = pow(10.0, 0.05 * -48.5dB) */
+	0x3b8273a6,		/* [123] = 3.98e-003 = pow(10.0, 0.05 * -48.0dB) */
+	0x3b8a2e77,		/* [124] = 4.22e-003 = pow(10.0, 0.05 * -47.5dB) */
+	0x3b925e89,		/* [125] = 4.47e-003 = pow(10.0, 0.05 * -47.0dB) */
+	0x3b9b0ace,		/* [126] = 4.73e-003 = pow(10.0, 0.05 * -46.5dB) */
+	0x3ba43aa2,		/* [127] = 5.01e-003 = pow(10.0, 0.05 * -46.0dB) */
+	0x3badf5d1,		/* [128] = 5.31e-003 = pow(10.0, 0.05 * -45.5dB) */
+	0x3bb8449c,		/* [129] = 5.62e-003 = pow(10.0, 0.05 * -45.0dB) */
+	0x3bc32fc3,		/* [130] = 5.96e-003 = pow(10.0, 0.05 * -44.5dB) */
+	0x3bcec08a,		/* [131] = 6.31e-003 = pow(10.0, 0.05 * -44.0dB) */
+	0x3bdb00c0,		/* [132] = 6.68e-003 = pow(10.0, 0.05 * -43.5dB) */
+	0x3be7facc,		/* [133] = 7.08e-003 = pow(10.0, 0.05 * -43.0dB) */
+	0x3bf5b9b0,		/* [134] = 7.50e-003 = pow(10.0, 0.05 * -42.5dB) */
+	0x3c02248a,		/* [135] = 7.94e-003 = pow(10.0, 0.05 * -42.0dB) */
+	0x3c09daac,		/* [136] = 8.41e-003 = pow(10.0, 0.05 * -41.5dB) */
+	0x3c1205c6,		/* [137] = 8.91e-003 = pow(10.0, 0.05 * -41.0dB) */
+	0x3c1aacc8,		/* [138] = 9.44e-003 = pow(10.0, 0.05 * -40.5dB) */
+	0x3c23d70a,		/* [139] = 1.00e-002 = pow(10.0, 0.05 * -40.0dB) */
+	0x3c2d8c52,		/* [140] = 1.06e-002 = pow(10.0, 0.05 * -39.5dB) */
+	0x3c37d4dd,		/* [141] = 1.12e-002 = pow(10.0, 0.05 * -39.0dB) */
+	0x3c42b965,		/* [142] = 1.19e-002 = pow(10.0, 0.05 * -38.5dB) */
+	0x3c4e4329,		/* [143] = 1.26e-002 = pow(10.0, 0.05 * -38.0dB) */
+	0x3c5a7bf1,		/* [144] = 1.33e-002 = pow(10.0, 0.05 * -37.5dB) */
+	0x3c676e1e,		/* [145] = 1.41e-002 = pow(10.0, 0.05 * -37.0dB) */
+	0x3c7524ac,		/* [146] = 1.50e-002 = pow(10.0, 0.05 * -36.5dB) */
+	0x3c81d59f,		/* [147] = 1.58e-002 = pow(10.0, 0.05 * -36.0dB) */
+	0x3c898712,		/* [148] = 1.68e-002 = pow(10.0, 0.05 * -35.5dB) */
+	0x3c91ad39,		/* [149] = 1.78e-002 = pow(10.0, 0.05 * -35.0dB) */
+	0x3c9a4efc,		/* [150] = 1.88e-002 = pow(10.0, 0.05 * -34.5dB) */
+	0x3ca373af,		/* [151] = 2.00e-002 = pow(10.0, 0.05 * -34.0dB) */
+	0x3cad2314,		/* [152] = 2.11e-002 = pow(10.0, 0.05 * -33.5dB) */
+	0x3cb76563,		/* [153] = 2.24e-002 = pow(10.0, 0.05 * -33.0dB) */
+	0x3cc24350,		/* [154] = 2.37e-002 = pow(10.0, 0.05 * -32.5dB) */
+	0x3ccdc614,		/* [155] = 2.51e-002 = pow(10.0, 0.05 * -32.0dB) */
+	0x3cd9f773,		/* [156] = 2.66e-002 = pow(10.0, 0.05 * -31.5dB) */
+	0x3ce6e1c6,		/* [157] = 2.82e-002 = pow(10.0, 0.05 * -31.0dB) */
+	0x3cf49003,		/* [158] = 2.99e-002 = pow(10.0, 0.05 * -30.5dB) */
+	0x3d0186e2,		/* [159] = 3.16e-002 = pow(10.0, 0.05 * -30.0dB) */
+	0x3d0933ac,		/* [160] = 3.35e-002 = pow(10.0, 0.05 * -29.5dB) */
+	0x3d1154e1,		/* [161] = 3.55e-002 = pow(10.0, 0.05 * -29.0dB) */
+	0x3d19f169,		/* [162] = 3.76e-002 = pow(10.0, 0.05 * -28.5dB) */
+	0x3d231090,		/* [163] = 3.98e-002 = pow(10.0, 0.05 * -28.0dB) */
+	0x3d2cba15,		/* [164] = 4.22e-002 = pow(10.0, 0.05 * -27.5dB) */
+	0x3d36f62b,		/* [165] = 4.47e-002 = pow(10.0, 0.05 * -27.0dB) */
+	0x3d41cd81,		/* [166] = 4.73e-002 = pow(10.0, 0.05 * -26.5dB) */
+	0x3d4d494a,		/* [167] = 5.01e-002 = pow(10.0, 0.05 * -26.0dB) */
+	0x3d597345,		/* [168] = 5.31e-002 = pow(10.0, 0.05 * -25.5dB) */
+	0x3d6655c3,		/* [169] = 5.62e-002 = pow(10.0, 0.05 * -25.0dB) */
+	0x3d73fbb4,		/* [170] = 5.96e-002 = pow(10.0, 0.05 * -24.5dB) */
+	0x3d813856,		/* [171] = 6.31e-002 = pow(10.0, 0.05 * -24.0dB) */
+	0x3d88e078,		/* [172] = 6.68e-002 = pow(10.0, 0.05 * -23.5dB) */
+	0x3d90fcbf,		/* [173] = 7.08e-002 = pow(10.0, 0.05 * -23.0dB) */
+	0x3d99940e,		/* [174] = 7.50e-002 = pow(10.0, 0.05 * -22.5dB) */
+	0x3da2adad,		/* [175] = 7.94e-002 = pow(10.0, 0.05 * -22.0dB) */
+	0x3dac5156,		/* [176] = 8.41e-002 = pow(10.0, 0.05 * -21.5dB) */
+	0x3db68738,		/* [177] = 8.91e-002 = pow(10.0, 0.05 * -21.0dB) */
+	0x3dc157fb,		/* [178] = 9.44e-002 = pow(10.0, 0.05 * -20.5dB) */
+	0x3dcccccd,		/* [179] = 1.00e-001 = pow(10.0, 0.05 * -20.0dB) */
+	0x3dd8ef67,		/* [180] = 1.06e-001 = pow(10.0, 0.05 * -19.5dB) */
+	0x3de5ca15,		/* [181] = 1.12e-001 = pow(10.0, 0.05 * -19.0dB) */
+	0x3df367bf,		/* [182] = 1.19e-001 = pow(10.0, 0.05 * -18.5dB) */
+	0x3e00e9f9,		/* [183] = 1.26e-001 = pow(10.0, 0.05 * -18.0dB) */
+	0x3e088d77,		/* [184] = 1.33e-001 = pow(10.0, 0.05 * -17.5dB) */
+	0x3e10a4d3,		/* [185] = 1.41e-001 = pow(10.0, 0.05 * -17.0dB) */
+	0x3e1936ec,		/* [186] = 1.50e-001 = pow(10.0, 0.05 * -16.5dB) */
+	0x3e224b06,		/* [187] = 1.58e-001 = pow(10.0, 0.05 * -16.0dB) */
+	0x3e2be8d7,		/* [188] = 1.68e-001 = pow(10.0, 0.05 * -15.5dB) */
+	0x3e361887,		/* [189] = 1.78e-001 = pow(10.0, 0.05 * -15.0dB) */
+	0x3e40e2bb,		/* [190] = 1.88e-001 = pow(10.0, 0.05 * -14.5dB) */
+	0x3e4c509b,		/* [191] = 2.00e-001 = pow(10.0, 0.05 * -14.0dB) */
+	0x3e586bd9,		/* [192] = 2.11e-001 = pow(10.0, 0.05 * -13.5dB) */
+	0x3e653ebb,		/* [193] = 2.24e-001 = pow(10.0, 0.05 * -13.0dB) */
+	0x3e72d424,		/* [194] = 2.37e-001 = pow(10.0, 0.05 * -12.5dB) */
+	0x3e809bcc,		/* [195] = 2.51e-001 = pow(10.0, 0.05 * -12.0dB) */
+	0x3e883aa8,		/* [196] = 2.66e-001 = pow(10.0, 0.05 * -11.5dB) */
+	0x3e904d1c,		/* [197] = 2.82e-001 = pow(10.0, 0.05 * -11.0dB) */
+	0x3e98da02,		/* [198] = 2.99e-001 = pow(10.0, 0.05 * -10.5dB) */
+	0x3ea1e89b,		/* [199] = 3.16e-001 = pow(10.0, 0.05 * -10.0dB) */
+	0x3eab8097,		/* [200] = 3.35e-001 = pow(10.0, 0.05 * -9.5dB) */
+	0x3eb5aa1a,		/* [201] = 3.55e-001 = pow(10.0, 0.05 * -9.0dB) */
+	0x3ec06dc3,		/* [202] = 3.76e-001 = pow(10.0, 0.05 * -8.5dB) */
+	0x3ecbd4b4,		/* [203] = 3.98e-001 = pow(10.0, 0.05 * -8.0dB) */
+	0x3ed7e89b,		/* [204] = 4.22e-001 = pow(10.0, 0.05 * -7.5dB) */
+	0x3ee4b3b6,		/* [205] = 4.47e-001 = pow(10.0, 0.05 * -7.0dB) */
+	0x3ef240e2,		/* [206] = 4.73e-001 = pow(10.0, 0.05 * -6.5dB) */
+	0x3f004dce,		/* [207] = 5.01e-001 = pow(10.0, 0.05 * -6.0dB) */
+	0x3f07e80b,		/* [208] = 5.31e-001 = pow(10.0, 0.05 * -5.5dB) */
+	0x3f0ff59a,		/* [209] = 5.62e-001 = pow(10.0, 0.05 * -5.0dB) */
+	0x3f187d50,		/* [210] = 5.96e-001 = pow(10.0, 0.05 * -4.5dB) */
+	0x3f21866c,		/* [211] = 6.31e-001 = pow(10.0, 0.05 * -4.0dB) */
+	0x3f2b1896,		/* [212] = 6.68e-001 = pow(10.0, 0.05 * -3.5dB) */
+	0x3f353bef,		/* [213] = 7.08e-001 = pow(10.0, 0.05 * -3.0dB) */
+	0x3f3ff911,		/* [214] = 7.50e-001 = pow(10.0, 0.05 * -2.5dB) */
+	0x3f4b5918,		/* [215] = 7.94e-001 = pow(10.0, 0.05 * -2.0dB) */
+	0x3f5765ac,		/* [216] = 8.41e-001 = pow(10.0, 0.05 * -1.5dB) */
+	0x3f642905,		/* [217] = 8.91e-001 = pow(10.0, 0.05 * -1.0dB) */
+	0x3f71adf9,		/* [218] = 9.44e-001 = pow(10.0, 0.05 * -0.5dB) */
+	0x3f800000,		/* [219] = 1.00e+000 = pow(10.0, 0.05 * 0.0dB) */
+	0x3f8795a0,		/* [220] = 1.06e+000 = pow(10.0, 0.05 * 0.5dB) */
+	0x3f8f9e4d,		/* [221] = 1.12e+000 = pow(10.0, 0.05 * 1.0dB) */
+	0x3f9820d7,		/* [222] = 1.19e+000 = pow(10.0, 0.05 * 1.5dB) */
+	0x3fa12478,		/* [223] = 1.26e+000 = pow(10.0, 0.05 * 2.0dB) */
+	0x3faab0d5,		/* [224] = 1.33e+000 = pow(10.0, 0.05 * 2.5dB) */
+	0x3fb4ce08,		/* [225] = 1.41e+000 = pow(10.0, 0.05 * 3.0dB) */
+	0x3fbf84a6,		/* [226] = 1.50e+000 = pow(10.0, 0.05 * 3.5dB) */
+	0x3fcaddc8,		/* [227] = 1.58e+000 = pow(10.0, 0.05 * 4.0dB) */
+	0x3fd6e30d,		/* [228] = 1.68e+000 = pow(10.0, 0.05 * 4.5dB) */
+	0x3fe39ea9,		/* [229] = 1.78e+000 = pow(10.0, 0.05 * 5.0dB) */
+	0x3ff11b6a,		/* [230] = 1.88e+000 = pow(10.0, 0.05 * 5.5dB) */
+	0x3fff64c1,		/* [231] = 2.00e+000 = pow(10.0, 0.05 * 6.0dB) */
+	0x40074368,		/* [232] = 2.11e+000 = pow(10.0, 0.05 * 6.5dB) */
+	0x400f4735,		/* [233] = 2.24e+000 = pow(10.0, 0.05 * 7.0dB) */
+	0x4017c496,		/* [234] = 2.37e+000 = pow(10.0, 0.05 * 7.5dB) */
+	0x4020c2bf,		/* [235] = 2.51e+000 = pow(10.0, 0.05 * 8.0dB) */
+	0x402a4952,		/* [236] = 2.66e+000 = pow(10.0, 0.05 * 8.5dB) */
+	0x40346063,		/* [237] = 2.82e+000 = pow(10.0, 0.05 * 9.0dB) */
+	0x403f1082,		/* [238] = 2.99e+000 = pow(10.0, 0.05 * 9.5dB) */
+	0x404a62c2,		/* [239] = 3.16e+000 = pow(10.0, 0.05 * 10.0dB) */
+	0x405660bd,		/* [240] = 3.35e+000 = pow(10.0, 0.05 * 10.5dB) */
+	0x406314a0,		/* [241] = 3.55e+000 = pow(10.0, 0.05 * 11.0dB) */
+	0x40708933,		/* [242] = 3.76e+000 = pow(10.0, 0.05 * 11.5dB) */
+	0x407ec9e1,		/* [243] = 3.98e+000 = pow(10.0, 0.05 * 12.0dB) */
+	0x4086f161,		/* [244] = 4.22e+000 = pow(10.0, 0.05 * 12.5dB) */
+	0x408ef052,		/* [245] = 4.47e+000 = pow(10.0, 0.05 * 13.0dB) */
+	0x4097688d,		/* [246] = 4.73e+000 = pow(10.0, 0.05 * 13.5dB) */
+	0x40a06142,		/* [247] = 5.01e+000 = pow(10.0, 0.05 * 14.0dB) */
+	0x40a9e20e,		/* [248] = 5.31e+000 = pow(10.0, 0.05 * 14.5dB) */
+	0x40b3f300,		/* [249] = 5.62e+000 = pow(10.0, 0.05 * 15.0dB) */
+	0x40be9ca5,		/* [250] = 5.96e+000 = pow(10.0, 0.05 * 15.5dB) */
+	0x40c9e807,		/* [251] = 6.31e+000 = pow(10.0, 0.05 * 16.0dB) */
+	0x40d5debc,		/* [252] = 6.68e+000 = pow(10.0, 0.05 * 16.5dB) */
+	0x40e28aeb,		/* [253] = 7.08e+000 = pow(10.0, 0.05 * 17.0dB) */
+	0x40eff755,		/* [254] = 7.50e+000 = pow(10.0, 0.05 * 17.5dB) */
+	0x40fe2f5e,		/* [255] = 7.94e+000 = pow(10.0, 0.05 * 18.0dB) */
+};
+
+#define MIXART_DIGITAL_LEVEL_MIN   0      /* -109.5 dB */
+#define MIXART_DIGITAL_LEVEL_MAX   255    /*  18.0 dB */
+#define MIXART_DIGITAL_ZERO_LEVEL  219    /*  0.0 dB */
+
+
+int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx)
+{
+	int err, i;
+	int volume[2];
+	mixart_msg_t request;
+	mixart_set_out_stream_level_req_t set_level;
+	u32 status;
+	mixart_pipe_t *pipe;
+
+	memset(&set_level, 0, sizeof(set_level));
+	set_level.nb_of_stream = 1;
+	set_level.stream_level.desc.stream_idx = idx;
+
+	if(is_aes) {
+		pipe = &chip->pipe_out_dig;	/* AES playback */
+		idx += MIXART_PLAYBACK_STREAMS;
+	} else {
+		pipe = &chip->pipe_out_ana;	/* analog playback */
+	}
+
+	/* only when pipe exists ! */
+	if(pipe->status == PIPE_UNDEFINED)
+		return 0;
+
+	set_level.stream_level.desc.uid_pipe = pipe->group_uid;
+
+	for(i=0; i<2; i++) {
+		if(chip->digital_playback_active[idx][i])
+			volume[i] = chip->digital_playback_volume[idx][i];
+		else
+			volume[i] = MIXART_DIGITAL_LEVEL_MIN;
+	}
+
+	set_level.stream_level.out_level.valid_mask1 = MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 | MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2;
+	set_level.stream_level.out_level.left_to_out1_level = mixart_digital_level[volume[0]];
+	set_level.stream_level.out_level.right_to_out2_level = mixart_digital_level[volume[1]];
+
+	request.message_id = MSG_STREAM_SET_OUT_STREAM_LEVEL;
+	request.uid = (mixart_uid_t){0,0};
+	request.data = &set_level;
+	request.size = sizeof(set_level);
+
+	err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
+	if((err<0) || status) {
+		snd_printk(KERN_DEBUG "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int mixart_update_capture_stream_level(mixart_t* chip, int is_aes)
+{
+	int err, i, idx;
+	mixart_pipe_t* pipe;
+	mixart_msg_t request;
+	mixart_set_in_audio_level_req_t set_level;
+	u32 status;
+
+	if(is_aes) {
+		idx = 1;
+		pipe = &chip->pipe_in_dig;
+	} else {
+		idx = 0;
+		pipe = &chip->pipe_in_ana;
+	}
+
+	/* only when pipe exists ! */
+	if(pipe->status == PIPE_UNDEFINED)
+		return 0;
+
+	memset(&set_level, 0, sizeof(set_level));
+	set_level.audio_count = 2;
+	set_level.level[0].connector = pipe->uid_left_connector;
+	set_level.level[1].connector = pipe->uid_right_connector;
+
+	for(i=0; i<2; i++) {
+		set_level.level[i].valid_mask1 = MIXART_AUDIO_LEVEL_DIGITAL_MASK;
+		set_level.level[i].digital_level = mixart_digital_level[chip->digital_capture_volume[idx][i]];
+	}
+
+	request.message_id = MSG_STREAM_SET_IN_AUDIO_LEVEL;
+	request.uid = (mixart_uid_t){0,0};
+	request.data = &set_level;
+	request.size = sizeof(set_level);
+
+	err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
+	if((err<0) || status) {
+		snd_printk(KERN_DEBUG "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/* shared */
+static int mixart_digital_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = MIXART_DIGITAL_LEVEL_MIN;   /* -109.5 dB */
+	uinfo->value.integer.max = MIXART_DIGITAL_LEVEL_MAX;   /*   18.0 dB */
+	return 0;
+}
+
+#define MIXART_VOL_REC_MASK	1
+#define MIXART_VOL_AES_MASK	2
+
+static int mixart_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+	int *stored_volume;
+	int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
+	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+	down(&chip->mgr->mixer_mutex);
+	if(is_capture) {
+		if(is_aes)	stored_volume = chip->digital_capture_volume[1];	/* AES capture */
+		else		stored_volume = chip->digital_capture_volume[0];	/* analog capture */
+	} else {
+		snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+		if(is_aes)	stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
+		else		stored_volume = chip->digital_playback_volume[idx];	/* analog playback */
+	}
+	ucontrol->value.integer.value[0] = stored_volume[0];
+	ucontrol->value.integer.value[1] = stored_volume[1];
+	up(&chip->mgr->mixer_mutex);
+	return 0;
+}
+
+static int mixart_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+	int changed = 0;
+	int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
+	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+	int* stored_volume;
+	int i;
+	down(&chip->mgr->mixer_mutex);
+	if(is_capture) {
+		if(is_aes)	stored_volume = chip->digital_capture_volume[1];	/* AES capture */
+		else		stored_volume = chip->digital_capture_volume[0];	/* analog capture */
+	} else {
+		snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+		if(is_aes)	stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
+		else		stored_volume = chip->digital_playback_volume[idx];	/* analog playback */
+	}
+	for(i=0; i<2; i++) {
+		if(stored_volume[i] != ucontrol->value.integer.value[i]) {
+			stored_volume[i] = ucontrol->value.integer.value[i];
+			changed = 1;
+		}
+	}
+	if(changed) {
+		if(is_capture)	mixart_update_capture_stream_level(chip, is_aes);
+		else		mixart_update_playback_stream_level(chip, is_aes, idx);
+	}
+	up(&chip->mgr->mixer_mutex);
+	return changed;
+}
+
+static snd_kcontrol_new_t snd_mixart_pcm_vol =
+{
+	.iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+	/* name will be filled later */
+	/* count will be filled later */
+	.info =         mixart_digital_vol_info,		/* shared */
+	.get =          mixart_pcm_vol_get,
+	.put =          mixart_pcm_vol_put,
+};
+
+
+static int mixart_pcm_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+	snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+	down(&chip->mgr->mixer_mutex);
+	if(kcontrol->private_value & MIXART_VOL_AES_MASK)	/* AES playback */
+		idx += MIXART_PLAYBACK_STREAMS;
+	ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0];
+	ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1];
+	up(&chip->mgr->mixer_mutex);
+	return 0;
+}
+
+static int mixart_pcm_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int changed = 0;
+	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+	int i, j;
+	snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+	down(&chip->mgr->mixer_mutex);
+	j = idx;
+	if(is_aes)	j += MIXART_PLAYBACK_STREAMS;
+	for(i=0; i<2; i++) {
+		if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
+			chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
+			changed = 1;
+		}
+	}
+	if(changed)	mixart_update_playback_stream_level(chip, is_aes, idx);
+	up(&chip->mgr->mixer_mutex);
+	return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_pcm_switch = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	/* name will be filled later */
+	.count =        MIXART_PLAYBACK_STREAMS,
+	.info =         mixart_sw_info,		/* shared */
+	.get =          mixart_pcm_sw_get,
+	.put =          mixart_pcm_sw_put
+};
+
+static int mixart_update_monitoring(mixart_t* chip, int channel)
+{
+	int err;
+	mixart_msg_t request;
+	mixart_set_out_audio_level_t audio_level;
+	u32 resp;
+
+	if(chip->pipe_out_ana.status == PIPE_UNDEFINED)
+		return -EINVAL; /* no pipe defined */
+
+	if(!channel)	request.uid = chip->pipe_out_ana.uid_left_connector;
+	else		request.uid = chip->pipe_out_ana.uid_right_connector;
+	request.message_id = MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL;
+	request.data = &audio_level;
+	request.size = sizeof(audio_level);
+
+	memset(&audio_level, 0, sizeof(audio_level));
+	audio_level.valid_mask1 = MIXART_AUDIO_LEVEL_MONITOR_MASK | MIXART_AUDIO_LEVEL_MUTE_M1_MASK;
+	audio_level.monitor_level = mixart_digital_level[chip->monitoring_volume[channel!=0]];
+	audio_level.monitor_mute1 = !chip->monitoring_active[channel!=0];
+
+	err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
+	if((err<0) || resp) {
+		snd_printk(KERN_DEBUG "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n", chip->chip_idx, resp);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * monitoring level control
+ */
+
+static int mixart_monitor_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	down(&chip->mgr->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->monitoring_volume[0];
+	ucontrol->value.integer.value[1] = chip->monitoring_volume[1];
+	up(&chip->mgr->mixer_mutex);
+	return 0;
+}
+
+static int mixart_monitor_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int changed = 0;
+	int i;
+	down(&chip->mgr->mixer_mutex);
+	for(i=0; i<2; i++) {
+		if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
+			chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
+			mixart_update_monitoring(chip, i);
+			changed = 1;
+		}
+	}
+	up(&chip->mgr->mixer_mutex);
+	return changed;
+}
+
+static snd_kcontrol_new_t mixart_control_monitor_vol = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Monitoring Volume",
+	.info =		mixart_digital_vol_info,		/* shared */
+	.get =		mixart_monitor_vol_get,
+	.put =		mixart_monitor_vol_put,
+};
+
+/*
+ * monitoring switch control
+ */
+
+static int mixart_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	down(&chip->mgr->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->monitoring_active[0];
+	ucontrol->value.integer.value[1] = chip->monitoring_active[1];
+	up(&chip->mgr->mixer_mutex);
+	return 0;
+}
+
+static int mixart_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	mixart_t *chip = snd_kcontrol_chip(kcontrol);
+	int changed = 0;
+	int i;
+	down(&chip->mgr->mixer_mutex);
+	for(i=0; i<2; i++) {
+		if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
+			chip->monitoring_active[i] = ucontrol->value.integer.value[i];
+			changed |= (1<<i); /* mask 0x01 ans 0x02 */
+		}
+	}
+	if(changed) {
+		/* allocate or release resources for monitoring */
+		int allocate = chip->monitoring_active[0] || chip->monitoring_active[1];
+		if(allocate) {
+			snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1);	/* allocate the playback pipe for monitoring */
+			snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1);	/* allocate the capture pipe for monitoring */
+		}
+		if(changed & 0x01)	mixart_update_monitoring(chip, 0);
+		if(changed & 0x02)	mixart_update_monitoring(chip, 1);
+		if(!allocate) {
+			snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1);	/* release the capture pipe for monitoring */
+			snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1);	/* release the playback pipe for monitoring */
+		}
+	}
+
+	up(&chip->mgr->mixer_mutex);
+	return (changed != 0);
+}
+
+static snd_kcontrol_new_t mixart_control_monitor_sw = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Monitoring Switch",
+	.info =         mixart_sw_info,		/* shared */
+	.get =          mixart_monitor_sw_get,
+	.put =          mixart_monitor_sw_put
+};
+
+
+static void mixart_reset_audio_levels(mixart_t *chip)
+{
+	/* analog volumes can be set even if there is no pipe */
+	mixart_update_analog_audio_level(chip, 0);
+	/* analog levels for capture only on the first two chips */
+	if(chip->chip_idx < 2) {
+		mixart_update_analog_audio_level(chip, 1);
+	}
+	return;
+}
+
+
+int snd_mixart_create_mixer(mixart_mgr_t *mgr)
+{
+	mixart_t *chip;
+	int err, i;
+
+	init_MUTEX(&mgr->mixer_mutex); /* can be in another place */
+
+	for(i=0; i<mgr->num_cards; i++) {
+		snd_kcontrol_new_t temp;
+		chip = mgr->chip[i];
+
+		/* analog output level control */
+		temp = mixart_control_analog_level;
+		temp.name = "Master Playback Volume";
+		temp.private_value = 0; /* playback */
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+		/* output mute controls */
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip))) < 0)
+			return err;
+
+		/* analog input level control only on first two chips !*/
+		if(i<2) {
+			temp = mixart_control_analog_level;
+			temp.name = "Master Capture Volume";
+			temp.private_value = 1; /* capture */
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+				return err;
+		}
+
+		temp = snd_mixart_pcm_vol;
+		temp.name = "PCM Playback Volume";
+		temp.count = MIXART_PLAYBACK_STREAMS;
+		temp.private_value = 0; /* playback analog */
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+
+		temp.name = "PCM Capture Volume";
+		temp.count = 1;
+		temp.private_value = MIXART_VOL_REC_MASK; /* capture analog */
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+
+		if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+			temp.name = "AES Playback Volume";
+			temp.count = MIXART_PLAYBACK_STREAMS;
+			temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+				return err;
+
+			temp.name = "AES Capture Volume";
+			temp.count = 0;
+			temp.private_value = MIXART_VOL_REC_MASK | MIXART_VOL_AES_MASK; /* capture AES/EBU */
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+				return err;
+		}
+		temp = mixart_control_pcm_switch;
+		temp.name = "PCM Playback Switch";
+		temp.private_value = 0; /* playback analog */
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+
+		if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
+			temp.name = "AES Playback Switch";
+			temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+				return err;
+		}
+
+		/* monitoring */
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip))) < 0)
+			return err;
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip))) < 0)
+			return err;
+
+		/* init all mixer data and program the master volumes/switches */
+		mixart_reset_audio_levels(chip);
+	}
+	return 0;
+}
diff -Nru a/sound/pci/mixart/mixart_mixer.h b/sound/pci/mixart/mixart_mixer.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/mixart/mixart_mixer.h	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,31 @@
+/*
+ * Driver for Digigram miXart soundcards
+ *
+ * include file for mixer
+ *
+ * Copyright (c) 2003 by Digigram <alsa@digigram.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SOUND_MIXART_MIXER_H
+#define __SOUND_MIXART_MIXER_H
+
+/* exported */
+int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx);
+int mixart_update_capture_stream_level(mixart_t* chip, int is_aes);
+int snd_mixart_create_mixer(mixart_mgr_t* mgr);
+
+#endif /* __SOUND_MIXART_MIXER_H */
diff -Nru a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
--- a/sound/pci/rme9652/hdsp.c	Sun Feb 29 12:58:38 2004
+++ b/sound/pci/rme9652/hdsp.c	Sun Feb 29 12:58:38 2004
@@ -3810,7 +3810,7 @@
 {
 	int mapped_channel;
 
-        snd_assert(channel >= 0 || channel < hdsp->max_channels, return NULL);
+        snd_assert(channel >= 0 && channel < hdsp->max_channels, return NULL);
         
 	if ((mapped_channel = hdsp->channel_map[channel]) < 0) {
 		return NULL;
@@ -3833,7 +3833,8 @@
 
 	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
 	snd_assert(channel_buf != NULL, return -EIO);
-	copy_from_user(channel_buf + pos * 4, src, count * 4);
+	if (copy_from_user(channel_buf + pos * 4, src, count * 4))
+		return -EFAULT;
 	return count;
 }
 
@@ -3847,7 +3848,8 @@
 
 	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
 	snd_assert(channel_buf != NULL, return -EIO);
-	copy_to_user(dst, channel_buf + pos * 4, count * 4);
+	if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
+		return -EFAULT;
 	return count;
 }
 
diff -Nru a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
--- a/sound/pci/trident/trident_main.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/trident/trident_main.c	Sun Feb 29 12:58:39 2004
@@ -3052,29 +3052,49 @@
 	}
 	if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
 
-		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_control, trident))) < 0)
+		kctl = snd_ctl_new1(&snd_trident_spdif_control, trident);
+		if (kctl == NULL) {
+			err = -ENOMEM;
 			goto __out;
+		}
 		if (trident->ac97->ext_id & AC97_EI_SPDIF)
 			kctl->id.index++;
 		if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF))
 			kctl->id.index++;
 		idx = kctl->id.index;
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			goto __out;
 		kctl->put(kctl, uctl);
 
-		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_default, trident))) < 0)
+		kctl = snd_ctl_new1(&snd_trident_spdif_default, trident);
+		if (kctl == NULL) {
+			err = -ENOMEM;
 			goto __out;
+		}
 		kctl->id.index = idx;
 		kctl->id.device = pcm_spdif_device;
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			goto __out;
 
-		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident))) < 0)
+		kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident);
+		if (kctl == NULL) {
+			err = -ENOMEM;
 			goto __out;
+		}
 		kctl->id.index = idx;
 		kctl->id.device = pcm_spdif_device;
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			goto __out;
 
-		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident))) < 0)
+		kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident);
+		if (kctl == NULL) {
+			err = -ENOMEM;
 			goto __out;
+		}
 		kctl->id.index = idx;
 		kctl->id.device = pcm_spdif_device;
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			goto __out;
 		trident->spdif_pcm_ctl = kctl;
 	}
 
diff -Nru a/sound/pci/via82xx.c b/sound/pci/via82xx.c
--- a/sound/pci/via82xx.c	Sun Feb 29 12:58:39 2004
+++ b/sound/pci/via82xx.c	Sun Feb 29 12:58:39 2004
@@ -922,12 +922,10 @@
 				  chip->no_vra ? 48000 : runtime->rate);
 		snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
 	}
-#if 0
-	if (chip->revision == VIA_REV_8233A)
-		rbits = 0;
+	if (runtime->rate == 48000)
+		rbits = 0xfffff;
 	else
-#endif
-		rbits = (0xfffff / 48000) * runtime->rate + ((0xfffff % 48000) * runtime->rate) / 48000;
+		rbits = (0x100000 / 48000) * runtime->rate + ((0x100000 % 48000) * runtime->rate) / 48000;
 	snd_assert((rbits & ~0xfffff) == 0, return -EINVAL);
 	snd_via82xx_channel_reset(chip, viadev);
 	snd_via82xx_set_table_ptr(chip, viadev);
@@ -1987,13 +1985,16 @@
 		{ .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ 
 		{ .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */
 		{ .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
+		{ .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */
 		{ .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
 		{ .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
-		{ .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
+		{ .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_NO_VRA }, /* Gigabyte GA-7VAXP (FIXME: or DXS_ENABLE?) */
 		{ .vendor = 0x147b, .device = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */
 		{ .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
 		{ .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
 		{ .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
+		{ .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
+		{ .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
 		{ .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
 		{ .vendor = 0x1695, .device = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
 		{ .vendor = 0x1849, .device = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
diff -Nru a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
--- a/sound/pcmcia/Kconfig	Sun Feb 29 12:58:39 2004
+++ b/sound/pcmcia/Kconfig	Sun Feb 29 12:58:39 2004
@@ -15,4 +15,10 @@
 	help
 	  Say 'Y' or 'M' to include support for Digigram VXpocket 440 soundcard.
 
+config SND_PDAUDIOCF
+	tristate "Sound Core PDAudioCF"
+	depends on SND && PCMCIA && ISA
+	help
+	  Say 'Y' or 'M' to include support for Sound Core PDAudioCF soundcard.
+
 endmenu
diff -Nru a/sound/pcmcia/Makefile b/sound/pcmcia/Makefile
--- a/sound/pcmcia/Makefile	Sun Feb 29 12:58:38 2004
+++ b/sound/pcmcia/Makefile	Sun Feb 29 12:58:38 2004
@@ -3,6 +3,4 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
 #
 
-obj-$(CONFIG_SND) += vx/
-
-
+obj-$(CONFIG_SND) += vx/ pdaudiocf/
diff -Nru a/sound/pcmcia/pdaudiocf/Makefile b/sound/pcmcia/pdaudiocf/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pcmcia/pdaudiocf/Makefile	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2004 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-pdaudiocf-objs := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o
+
+obj-$(CONFIG_SND_PDAUDIOCF) += snd-pdaudiocf.o
diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,426 @@
+/*
+ * Driver for Sound Core PDAudioCF soundcard
+ *
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include <pcmcia/version.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/cisreg.h>
+#include "pdaudiocf.h"
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+/*
+ */
+
+#define CARD_NAME	"PDAudio-CF"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Sound Core " CARD_NAME);
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Sound Core," CARD_NAME "}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+static unsigned int irq_mask = 0xffff;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM_DESC(irq_mask, "IRQ bitmask for " CARD_NAME " soundcard.");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM_DESC(irq_list, "List of Available interrupts for " CARD_NAME " soundcard.");
+ 
+
+/*
+ */
+
+static dev_info_t dev_info = "snd-pdaudiocf";
+static snd_card_t *card_list[SNDRV_CARDS];
+static dev_link_t *dev_list;
+
+/*
+ * prototypes
+ */
+static void pdacf_config(dev_link_t *link);
+static int pdacf_event(event_t event, int priority, event_callback_args_t *args);
+static void snd_pdacf_detach(dev_link_t *link);
+
+static void pdacf_release(dev_link_t *link)
+{
+	if (link->state & DEV_CONFIG) {
+		/* release cs resources */
+		pcmcia_release_configuration(link->handle);
+		pcmcia_release_io(link->handle, &link->io);
+		pcmcia_release_irq(link->handle, &link->irq);
+		link->state &= ~DEV_CONFIG;
+	}
+}
+
+/*
+ * destructor
+ */
+static int snd_pdacf_free(pdacf_t *pdacf)
+{
+	dev_link_t *link = &pdacf->link;
+
+	pdacf_release(link);
+
+	/* Break the link with Card Services */
+	if (link->handle)
+		pcmcia_deregister_client(link->handle);
+
+	card_list[pdacf->index] = NULL;
+	pdacf->card = NULL;
+
+	snd_magic_kfree(pdacf);
+	return 0;
+}
+
+static int snd_pdacf_dev_free(snd_device_t *device)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, device->device_data, return -ENXIO);
+	return snd_pdacf_free(chip);
+}
+
+/*
+ * snd_pdacf_attach - attach callback for cs
+ */
+static dev_link_t *snd_pdacf_attach(void)
+{
+	client_reg_t client_reg;	/* Register with cardmgr */
+	dev_link_t *link;		/* Info for cardmgr */
+	int i, ret;
+	pdacf_t *pdacf;
+	snd_card_t *card;
+	static snd_device_ops_t ops = {
+		.dev_free =	snd_pdacf_dev_free,
+	};
+
+	snd_printdd(KERN_DEBUG "pdacf_attach called\n");
+	/* find an empty slot from the card list */
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (! card_list[i])
+			break;
+	}
+	if (i >= SNDRV_CARDS) {
+		snd_printk(KERN_ERR "pdacf: too many cards found\n");
+		return NULL;
+	}
+	if (! enable[i])
+		return NULL; /* disabled explicitly */
+
+	/* ok, create a card instance */
+	card = snd_card_new(index[i], id[i], THIS_MODULE, 0);
+	if (card == NULL) {
+		snd_printk(KERN_ERR "pdacf: cannot create a card instance\n");
+		return NULL;
+	}
+
+	pdacf = snd_pdacf_create(card);
+	if (! pdacf)
+		return NULL;
+
+	if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, pdacf, &ops) < 0) {
+		snd_magic_kfree(pdacf);
+		snd_card_free(card);
+		return NULL;
+	}
+
+	pdacf->index = i;
+	card_list[i] = card;
+
+	link = &pdacf->link;
+	link->priv = pdacf;
+
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	link->io.NumPorts1 = 16;
+
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT | IRQ_FORCED_PULSE;
+	// link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID /* | IRQ_LEVEL_ID */;
+	if (irq_list[0] == -1)
+		link->irq.IRQInfo2 = irq_mask;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << irq_list[i];
+	link->irq.Handler = pdacf_interrupt;
+	link->irq.Instance = pdacf;
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+	link->conf.ConfigIndex = 1;
+	link->conf.Present = PRESENT_OPTION;
+
+	/* Chain drivers */
+	link->next = dev_list;
+	dev_list = link;
+
+	/* Register with Card Services */
+	client_reg.dev_info = &dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask = 
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL
+#ifdef CONFIG_PM
+		| CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET
+		| CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME
+#endif
+		;
+	client_reg.event_handler = &pdacf_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = pcmcia_register_client(&link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		snd_pdacf_detach(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+
+/**
+ * snd_pdacf_assign_resources - initialize the hardware and card instance.
+ * @port: i/o port for the card
+ * @irq: irq number for the card
+ *
+ * this function assigns the specified port and irq, boot the card,
+ * create pcm and control instances, and initialize the rest hardware.
+ *
+ * returns 0 if successful, or a negative error code.
+ */
+static int snd_pdacf_assign_resources(pdacf_t *pdacf, int port, int irq)
+{
+	int err;
+	snd_card_t *card = pdacf->card;
+
+	snd_printdd(KERN_DEBUG "pdacf assign resources: port = 0x%x, irq = %d\n", port, irq);
+	pdacf->port = port;
+	pdacf->irq = irq;
+	pdacf->chip_status |= PDAUDIOCF_STAT_IS_CONFIGURED;
+
+	err = snd_pdacf_ak4117_create(pdacf);
+	if (err < 0)
+		return err;	
+
+	strcpy(card->driver, "PDAudio-CF");
+	sprintf(card->shortname, "Core Sound %s", card->driver);
+	sprintf(card->longname, "%s at 0x%x, irq %i",
+		card->shortname, port, irq);
+
+	err = snd_pdacf_pcm_new(pdacf);
+	if (err < 0)
+		return err;
+
+#ifdef CONFIG_PM
+	card->power_state_private_data = pdacf;
+	card->set_power_state = snd_pdacf_set_power_state;
+#endif
+
+	if ((err = snd_card_register(card)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+/*
+ * snd_pdacf_detach - detach callback for cs
+ */
+static void snd_pdacf_detach(dev_link_t *link)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, link->priv, return);
+
+	snd_printdd(KERN_DEBUG "pdacf_detach called\n");
+	/* Remove the interface data from the linked list */
+	{
+		dev_link_t **linkp;
+		/* Locate device structure */
+		for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+			if (*linkp == link)
+				break;
+		if (*linkp)
+			*linkp = link->next;
+	}
+	if (chip->chip_status & PDAUDIOCF_STAT_IS_CONFIGURED)
+		snd_pdacf_powerdown(chip);
+	chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */
+	snd_card_disconnect(chip->card);
+	snd_card_free_in_thread(chip->card);
+}
+
+/*
+ * snd_pdacf_detach_all - detach all instances linked to the hw
+ */
+static void snd_pdacf_detach_all(void)
+{
+	while (dev_list != NULL)
+		snd_pdacf_detach(dev_list);
+}
+
+/*
+ * configuration callback
+ */
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void pdacf_config(dev_link_t *link)
+{
+	client_handle_t handle = link->handle;
+	pdacf_t *pdacf = snd_magic_cast(pdacf_t, link->priv, return);
+	tuple_t tuple;
+	cisparse_t parse;
+	config_info_t conf;
+	u_short buf[32];
+	int last_fn, last_ret;
+
+	snd_printdd(KERN_DEBUG "pdacf_config called\n");
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	tuple.Attributes = 0;
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleDataMax = sizeof(buf);
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.ConfigIndex = 0x5;
+
+	CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+	link->conf.Vcc = conf.Vcc;
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+
+	CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io));
+	CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+	if (snd_pdacf_assign_resources(pdacf, link->io.BasePort1, link->irq.AssignedIRQ) < 0)
+		goto failed;
+
+	link->dev = &pdacf->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+failed:
+	pcmcia_release_configuration(link->handle);
+	pcmcia_release_io(link->handle, &link->io);
+	pcmcia_release_irq(link->handle, &link->irq);
+}
+
+/*
+ * event callback
+ */
+static int pdacf_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	pdacf_t *chip = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n");
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			chip->chip_status |= PDAUDIOCF_STAT_IS_STALE;
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		snd_printdd(KERN_DEBUG "CARD_INSERTION..\n");
+		link->state |= DEV_PRESENT;
+		pdacf_config(link);
+		break;
+#ifdef CONFIG_PM
+	case CS_EVENT_PM_SUSPEND:
+		snd_printdd(KERN_DEBUG "SUSPEND\n");
+		link->state |= DEV_SUSPEND;
+		if (chip) {
+			snd_printdd(KERN_DEBUG "snd_pdacf_suspend calling\n");
+			snd_pdacf_suspend(chip);
+		}
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n");
+		if (link->state & DEV_CONFIG)
+			pcmcia_release_configuration(link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		snd_printdd(KERN_DEBUG "RESUME\n");
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		snd_printdd(KERN_DEBUG "CARD_RESET\n");
+		if (DEV_OK(link)) {
+			snd_printdd(KERN_DEBUG "requestconfig...\n");
+			pcmcia_request_configuration(link->handle, &link->conf);
+			if (chip) {
+				snd_printdd(KERN_DEBUG "calling snd_pdacf_resume\n");
+				snd_pdacf_resume(chip);
+			}
+		}
+		snd_printdd(KERN_DEBUG "resume done!\n");
+		break;
+#endif
+	}
+	return 0;
+}
+
+/*
+ * Module entry points
+ */
+static struct pcmcia_driver pdacf_cs_driver = {
+	.owner          = THIS_MODULE,
+	.drv            = {
+		.name   = "snd-pdaudiocf",
+	},
+	.attach         = snd_pdacf_attach,
+	.detach         = snd_pdacf_detach
+};
+
+static int __init init_pdacf(void)
+{
+	return pcmcia_register_driver(&pdacf_cs_driver);
+}
+
+static void __exit exit_pdacf(void)
+{
+	pcmcia_unregister_driver(&pdacf_cs_driver);
+	snd_pdacf_detach_all();
+}
+
+module_init(init_pdacf);
+module_exit(exit_pdacf);
diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,147 @@
+/*
+ * Driver for Sound Cors PDAudioCF soundcard
+ *
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __PDAUDIOCF_H
+#define __PDAUDIOCF_H
+
+#include <sound/pcm.h>
+#include <linux/interrupt.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include <sound/ak4117.h>
+
+/* PDAUDIOCF registers */
+#define PDAUDIOCF_REG_MD	0x00	/* music data, R/O */
+#define PDAUDIOCF_REG_WDP	0x02	/* write data pointer / 2, R/O */
+#define PDAUDIOCF_REG_RDP	0x04	/* read data pointer / 2, R/O */
+#define PDAUDIOCF_REG_TCR	0x06	/* test control register W/O */
+#define PDAUDIOCF_REG_SCR	0x08	/* status and control, R/W (see bit description) */
+#define PDAUDIOCF_REG_ISR	0x0a	/* interrupt status, R/O */
+#define PDAUDIOCF_REG_IER	0x0c	/* interrupt enable, R/W */
+#define PDAUDIOCF_REG_AK_IFR	0x0e	/* AK interface register, R/W */
+
+/* PDAUDIOCF_REG_TCR */
+#define PDAUDIOCF_ELIMAKMBIT	(1<<0)	/* simulate AKM music data */
+#define PDAUDIOCF_TESTDATASEL	(1<<1)	/* test data selection, 0 = 0x55, 1 = pseudo-random */
+
+/* PDAUDIOCF_REG_SCR */
+#define PDAUDIOCF_AK_SBP	(1<<0)	/* serial port busy flag */
+#define PDAUDIOCF_RST		(1<<2)	/* FPGA, AKM + SRAM buffer reset */
+#define PDAUDIOCF_PDN		(1<<3)	/* power down bit */
+#define PDAUDIOCF_CLKDIV0	(1<<4)	/* choose 24.576Mhz clock divided by 1,2,3 or 4 */
+#define PDAUDIOCF_CLKDIV1	(1<<5)
+#define PDAUDIOCF_RECORD	(1<<6)	/* start capturing to SRAM */
+#define PDAUDIOCF_AK_SDD	(1<<7)	/* music data detected */
+#define PDAUDIOCF_RED_LED_OFF	(1<<8)	/* red LED off override */
+#define PDAUDIOCF_BLUE_LED_OFF	(1<<9)	/* blue LED off override */
+#define PDAUDIOCF_DATAFMT0	(1<<10)	/* data format bits: 00 = 16-bit, 01 = 18-bit */
+#define PDAUDIOCF_DATAFMT1	(1<<11)	/* 10 = 20-bit, 11 = 24-bit, all right justified */
+#define PDAUDIOCF_FPGAREV(x)	((x>>12)&0x0f) /* FPGA revision */
+
+/* PDAUDIOCF_REG_ISR */
+#define PDAUDIOCF_IRQLVL	(1<<0)	/* Buffer level IRQ */
+#define PDAUDIOCF_IRQOVR	(1<<1)	/* Overrun IRQ */
+#define PDAUDIOCF_IRQAKM	(1<<2)	/* AKM IRQ */
+
+/* PDAUDIOCF_REG_IER */
+#define PDAUDIOCF_IRQLVLEN0	(1<<0)	/* fill threshold levels; 00 = none, 01 = 1/8th of buffer */
+#define PDAUDIOCF_IRQLVLEN1	(1<<1)	/* 10 = 1/4th of buffer, 11 = 1/2th of buffer */
+#define PDAUDIOCF_IRQOVREN	(1<<2)	/* enable overrun IRQ */
+#define PDAUDIOCF_IRQAKMEN	(1<<3)	/* enable AKM IRQ */
+#define PDAUDIOCF_BLUEDUTY0	(1<<8)	/* blue LED duty cycle; 00 = 100%, 01 = 50% */
+#define PDAUDIOCF_BLUEDUTY1	(1<<9)	/* 02 = 25%, 11 = 12% */
+#define PDAUDIOCF_REDDUTY0	(1<<10)	/* red LED duty cycle; 00 = 100%, 01 = 50% */
+#define PDAUDIOCF_REDDUTY1	(1<<11)	/* 02 = 25%, 11 = 12% */
+#define PDAUDIOCF_BLUESDD	(1<<12)	/* blue LED against SDD bit */
+#define PDAUDIOCF_BLUEMODULATE	(1<<13)	/* save power when 100% duty cycle selected */
+#define PDAUDIOCF_REDMODULATE	(1<<14)	/* save power when 100% duty cycle selected */
+#define PDAUDIOCF_HALFRATE	(1<<15)	/* slow both LED blinks by half (also spdif detect rate) */
+
+/* chip status */
+#define PDAUDIOCF_STAT_IS_STALE	(1<<0)
+#define PDAUDIOCF_STAT_IS_CONFIGURED (1<<1)
+#define PDAUDIOCF_STAT_IS_SUSPENDED (1<<2)
+
+typedef struct {
+	snd_card_t *card;
+	int index;
+
+	unsigned long port;
+	int irq;
+
+	spinlock_t reg_lock;
+	unsigned short regmap[8];
+	unsigned short suspend_reg_scr;
+	struct tasklet_struct tq;
+
+	spinlock_t ak4117_lock;
+	ak4117_t *ak4117;
+
+	unsigned int chip_status;
+
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *pcm_substream;
+	unsigned int pcm_running: 1;
+	unsigned int pcm_channels;
+	unsigned int pcm_swab;
+	unsigned int pcm_little;
+	unsigned int pcm_frame;
+	unsigned int pcm_sample;
+	unsigned int pcm_xor;
+	unsigned int pcm_size;
+	unsigned int pcm_period;
+	unsigned int pcm_tdone;
+	unsigned int pcm_hwptr;
+	void *pcm_area;
+	
+	/* pcmcia stuff */
+	dev_link_t link;
+	dev_node_t node;
+} pdacf_t;
+
+static inline void pdacf_reg_write(pdacf_t *chip, unsigned char reg, unsigned short val)
+{
+	outw(chip->regmap[reg>>1] = val, chip->port + reg);
+}
+
+static inline unsigned short pdacf_reg_read(pdacf_t *chip, unsigned char reg)
+{
+	return inw(chip->port + reg);
+}
+
+unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg);
+void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val);
+pdacf_t *snd_pdacf_create(snd_card_t *card);
+int snd_pdacf_ak4117_create(pdacf_t *pdacf);
+void snd_pdacf_powerdown(pdacf_t *chip);
+#ifdef CONFIG_PM
+void snd_pdacf_suspend(pdacf_t *chip);
+void snd_pdacf_resume(pdacf_t *chip);
+int snd_pdacf_set_power_state(snd_card_t *card, unsigned int power_state);
+#endif
+int snd_pdacf_pcm_new(pdacf_t *chip);
+void pdacf_interrupt(int irq, void *dev, struct pt_regs *regs);
+void pdacf_tasklet(unsigned long private_data);
+void pdacf_reinit(pdacf_t *chip, int resume);
+
+#endif /* __PDAUDIOCF_H */
diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,317 @@
+/*
+ * Driver for Sound Core PDAudioCF soundcard
+ *
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include "pdaudiocf.h"
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+/*
+ *
+ */
+unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return 0);
+	unsigned long timeout;
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&chip->ak4117_lock, flags);
+	timeout = 1000;
+	while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) {
+		udelay(5);
+		if (--timeout == 0) {
+			spin_unlock_irqrestore(&chip->ak4117_lock, flags);
+			snd_printk(KERN_ERR "AK4117 ready timeout (read)\n");
+			return 0;
+		}
+	}
+	pdacf_reg_write(chip, PDAUDIOCF_REG_AK_IFR, (u16)reg << 8);
+	timeout = 1000;
+	while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) {
+		udelay(5);
+		if (--timeout == 0) {
+			spin_unlock_irqrestore(&chip->ak4117_lock, flags);
+			snd_printk(KERN_ERR "AK4117 read timeout (read2)\n");
+			return 0;
+		}
+	}
+	res = (unsigned char)pdacf_reg_read(chip, PDAUDIOCF_REG_AK_IFR);
+	spin_unlock_irqrestore(&chip->ak4117_lock, flags);
+	return res;
+}
+
+void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return);
+	unsigned long timeout;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->ak4117_lock, flags);
+	timeout = 1000;
+	while (inw(chip->port + PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) {
+		udelay(5);
+		if (--timeout == 0) {
+			spin_unlock_irqrestore(&chip->ak4117_lock, flags);
+			snd_printk(KERN_ERR "AK4117 ready timeout (write)\n");
+			return;
+		}
+	}
+	outw((u16)reg << 8 | val | (1<<13), chip->port + PDAUDIOCF_REG_AK_IFR);
+	spin_unlock_irqrestore(&chip->ak4117_lock, flags);
+}
+
+#if 0
+void pdacf_dump(pdacf_t *chip)
+{
+	printk("PDAUDIOCF DUMP (0x%lx):\n", chip->port);
+	printk("WPD         : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_WDP));
+	printk("RDP         : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_RDP));
+	printk("TCR         : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_TCR));
+	printk("SCR         : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_SCR));
+	printk("ISR         : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_ISR));
+	printk("IER         : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_IER));
+	printk("AK_IFR      : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_AK_IFR));
+}
+#endif
+
+static int pdacf_reset(pdacf_t *chip, int powerdown)
+{
+	u16 val;
+	
+	val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
+	val |= PDAUDIOCF_PDN;
+	val &= ~PDAUDIOCF_RECORD;		/* for sure */
+	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
+	udelay(5);
+	val |= PDAUDIOCF_RST;
+	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
+	udelay(200);
+	val &= ~PDAUDIOCF_RST;
+	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
+	udelay(5);
+	if (!powerdown) {
+		val &= ~PDAUDIOCF_PDN;
+		pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
+		udelay(200);
+	}
+	return 0;
+}
+
+void pdacf_reinit(pdacf_t *chip, int resume)
+{
+	pdacf_reset(chip, 0);
+	if (resume)
+		pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, chip->suspend_reg_scr);
+	snd_ak4117_reinit(chip->ak4117);
+	pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, chip->regmap[PDAUDIOCF_REG_TCR>>1]);
+	pdacf_reg_write(chip, PDAUDIOCF_REG_IER, chip->regmap[PDAUDIOCF_REG_IER>>1]);
+}
+
+static void pdacf_proc_read(snd_info_entry_t * entry,
+                            snd_info_buffer_t * buffer)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, entry->private_data, return);
+	u16 tmp;
+
+	snd_iprintf(buffer, "PDAudioCF\n\n");
+	tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
+	snd_iprintf(buffer, "FPGA revision      : 0x%x\n", PDAUDIOCF_FPGAREV(tmp));
+	                                   
+}
+
+static void pdacf_proc_init(pdacf_t *chip)
+{
+	snd_info_entry_t *entry;
+
+	if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry))
+		snd_info_set_text_ops(entry, chip, 1024, pdacf_proc_read);
+}
+
+pdacf_t *snd_pdacf_create(snd_card_t *card)
+{
+	pdacf_t *chip;
+
+	chip = snd_magic_kcalloc(pdacf_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return NULL;
+	chip->card = card;
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->ak4117_lock);
+	tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip);
+	card->private_data = chip;
+
+	pdacf_proc_init(chip);
+	return chip;
+}
+
+static void snd_pdacf_ak4117_change(ak4117_t *ak4117, unsigned char c0, unsigned char c1)
+{
+	pdacf_t *chip = ak4117->change_callback_private;
+	unsigned long flags;
+	u16 val;
+
+	if (!(c0 & AK4117_UNLCK))
+		return;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = chip->regmap[PDAUDIOCF_REG_SCR>>1];
+	if (ak4117->rcs0 & AK4117_UNLCK)
+		val |= PDAUDIOCF_BLUE_LED_OFF;
+	else
+		val &= ~PDAUDIOCF_BLUE_LED_OFF;
+	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+int snd_pdacf_ak4117_create(pdacf_t *chip)
+{
+	int err;
+	u16 val;
+	/* design note: if we unmask PLL unlock, parity, valid, audio or auto bit interrupts */
+	/* from AK4117 then INT1 pin from AK4117 will be high all time, because PCMCIA interrupts are */
+	/* egde based and FPGA does logical OR for all interrupt sources, we cannot use these */
+	/* high-rate sources */
+	static unsigned char pgm[5] = {
+		AK4117_XTL_24_576M | AK4117_EXCT,				/* AK4117_REG_PWRDN */
+		AK4117_CM_PLL_XTAL | AK4117_PKCS_128fs | AK4117_XCKS_128fs,	/* AK4117_REQ_CLOCK */
+		AK4117_EFH_1024LRCLK | AK4117_DIF_24R | AK4117_IPS,		/* AK4117_REG_IO */
+		0xff,								/* AK4117_REG_INT0_MASK */
+		AK4117_MAUTO | AK4117_MAUD | AK4117_MULK | AK4117_MPAR | AK4117_MV, /* AK4117_REG_INT1_MASK */
+	};
+
+	err = pdacf_reset(chip, 0);
+	if (err < 0)
+		return err;
+	err = snd_ak4117_create(chip->card, pdacf_ak4117_read, pdacf_ak4117_write, pgm, chip, &chip->ak4117);
+	if (err < 0)
+		return err;
+
+	val = pdacf_reg_read(chip, PDAUDIOCF_REG_TCR);
+#if 1 /* normal operation */
+	val &= ~(PDAUDIOCF_ELIMAKMBIT|PDAUDIOCF_TESTDATASEL);
+#else /* debug */
+	val |= PDAUDIOCF_ELIMAKMBIT;
+	val &= ~PDAUDIOCF_TESTDATASEL;
+#endif
+	pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, val);
+	
+	/* setup the FPGA to match AK4117 setup */
+	val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
+	val &= ~(PDAUDIOCF_CLKDIV0 | PDAUDIOCF_CLKDIV1);		/* use 24.576Mhz clock */
+	val &= ~(PDAUDIOCF_RED_LED_OFF|PDAUDIOCF_BLUE_LED_OFF);
+	val |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1;			/* 24-bit data */
+	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
+
+	/* setup LEDs and IRQ */
+	val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER);
+	val &= ~(PDAUDIOCF_IRQLVLEN0 | PDAUDIOCF_IRQLVLEN1);
+	val &= ~(PDAUDIOCF_BLUEDUTY0 | PDAUDIOCF_REDDUTY0 | PDAUDIOCF_REDDUTY1);
+	val |= PDAUDIOCF_BLUEDUTY1 | PDAUDIOCF_HALFRATE;
+	val |= PDAUDIOCF_IRQOVREN | PDAUDIOCF_IRQAKMEN;
+	pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val);
+
+	chip->ak4117->change_callback_private = chip;
+	chip->ak4117->change_callback = snd_pdacf_ak4117_change;
+
+	/* update LED status */
+	snd_pdacf_ak4117_change(chip->ak4117, AK4117_UNLCK, 0);
+
+	return 0;
+}
+
+void snd_pdacf_powerdown(pdacf_t *chip)
+{
+	u16 val;
+
+	val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
+	chip->suspend_reg_scr = val;
+	val |= PDAUDIOCF_RED_LED_OFF | PDAUDIOCF_BLUE_LED_OFF;
+	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val);
+	/* disable interrupts, but use direct write to preserve old register value in chip->regmap */
+	val = inw(chip->port + PDAUDIOCF_REG_IER);
+	val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1);
+	outw(val, chip->port + PDAUDIOCF_REG_IER);
+	pdacf_reset(chip, 1);
+}
+
+#ifdef CONFIG_PM
+
+void snd_pdacf_suspend(pdacf_t *chip)
+{
+	snd_card_t *card = chip->card;
+	u16 val;
+	
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		return;
+	snd_pcm_suspend_all(chip->pcm);
+	/* disable interrupts, but use direct write to preserve old register value in chip->regmap */
+	val = inw(chip->port + PDAUDIOCF_REG_IER);
+	val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1);
+	outw(val, chip->port + PDAUDIOCF_REG_IER);
+	chip->chip_status |= PDAUDIOCF_STAT_IS_SUSPENDED;	/* ignore interrupts from now */
+	snd_pdacf_powerdown(chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+}
+
+static inline int check_signal(pdacf_t *chip)
+{
+	return (chip->ak4117->rcs0 & AK4117_UNLCK) == 0;
+}
+
+void snd_pdacf_resume(pdacf_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int timeout = 40;
+
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		return;
+	pdacf_reinit(chip, 1);
+	/* wait for AK4117's PLL */
+	while (timeout-- > 0 &&
+	       (snd_ak4117_external_rate(chip->ak4117) <= 0 || !check_signal(chip)))
+		mdelay(1);
+	chip->chip_status &= ~PDAUDIOCF_STAT_IS_SUSPENDED;
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+}
+
+int snd_pdacf_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, card->power_state_private_data, return -ENXIO);
+
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		snd_pdacf_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		snd_pdacf_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif
diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,325 @@
+/*
+ * Driver for Sound Core PDAudioCF soundcard
+ *
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include "pdaudiocf.h"
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+/*
+ *
+ */
+void pdacf_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, dev, return);
+	unsigned short stat;
+
+	if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|
+				  PDAUDIOCF_STAT_IS_CONFIGURED|
+				  PDAUDIOCF_STAT_IS_SUSPENDED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
+		return;
+
+	stat = inw(chip->port + PDAUDIOCF_REG_ISR);
+	if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) {
+		if (stat & PDAUDIOCF_IRQOVR)	/* should never happen */
+			snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
+		if (chip->pcm_substream)
+			tasklet_hi_schedule(&chip->tq);
+		if (!(stat & PDAUDIOCF_IRQAKM))
+			stat |= PDAUDIOCF_IRQAKM;	/* check rate */
+	}
+	if (regs != NULL)
+		snd_ak4117_check_rate_and_errors(chip->ak4117, 0);
+}
+
+static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
+{
+	while (size-- > 0) {
+		*dst++ = inw(rdp_port) ^ xor;
+		inw(rdp_port);
+	}
+}
+
+static inline void pdacf_transfer_mono32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		inw(rdp_port);
+		*dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
+	}
+}
+
+static inline void pdacf_transfer_stereo16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
+{
+	while (size-- > 0) {
+		*dst++ = inw(rdp_port) ^ xor;
+		*dst++ = inw(rdp_port) ^ xor;
+	}
+}
+
+static inline void pdacf_transfer_stereo32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2, val3;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		val3 = inw(rdp_port);
+		*dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
+		*dst++ = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
+	}
+}
+
+static inline void pdacf_transfer_mono16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
+{
+	while (size-- > 0) {
+		*dst++ = swab16(inw(rdp_port) ^ xor);
+		inw(rdp_port);
+	}
+}
+
+static inline void pdacf_transfer_mono32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		inw(rdp_port);
+		*dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor);
+	}
+}
+
+static inline void pdacf_transfer_stereo16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
+{
+	while (size-- > 0) {
+		*dst++ = swab16(inw(rdp_port) ^ xor);
+		*dst++ = swab16(inw(rdp_port) ^ xor);
+	}
+}
+
+static inline void pdacf_transfer_stereo32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2, val3;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		val3 = inw(rdp_port);
+		*dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor);
+		*dst++ = swab32((((u32)val3 << 16) | (val2 & 0xff00)) ^ xor);
+	}
+}
+
+static inline void pdacf_transfer_mono24le(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2;
+	register u32 xval1;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		inw(rdp_port);
+		xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor;
+		*dst++ = (u8)(xval1 >> 8);
+		*dst++ = (u8)(xval1 >> 16);
+		*dst++ = (u8)(xval1 >> 24);
+	}
+}
+
+static inline void pdacf_transfer_mono24be(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2;
+	register u32 xval1;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		inw(rdp_port);
+		xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor;
+		*dst++ = (u8)(xval1 >> 24);
+		*dst++ = (u8)(xval1 >> 16);
+		*dst++ = (u8)(xval1 >> 8);
+	}
+}
+
+static inline void pdacf_transfer_stereo24le(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2, val3;
+	register u32 xval1, xval2;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		val3 = inw(rdp_port);
+		xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
+		xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
+		*dst++ = (u8)(xval1 >> 8);
+		*dst++ = (u8)(xval1 >> 16);
+		*dst++ = (u8)(xval1 >> 24);
+		*dst++ = (u8)(xval2 >> 8);
+		*dst++ = (u8)(xval2 >> 16);
+		*dst++ = (u8)(xval2 >> 24);
+	}
+}
+
+static inline void pdacf_transfer_stereo24be(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
+{
+	register u16 val1, val2, val3;
+	register u32 xval1, xval2;
+
+	while (size-- > 0) {
+		val1 = inw(rdp_port);
+		val2 = inw(rdp_port);
+		val3 = inw(rdp_port);
+		xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
+		xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
+		*dst++ = (u8)(xval1 >> 24);
+		*dst++ = (u8)(xval1 >> 16);
+		*dst++ = (u8)(xval1 >> 8);
+		*dst++ = (u8)(xval2 >> 24);
+		*dst++ = (u8)(xval2 >> 16);
+		*dst++ = (u8)(xval2 >> 8);
+	}
+}
+
+static void pdacf_transfer(pdacf_t *chip, unsigned int size, unsigned int off)
+{
+	unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
+	unsigned int xor = chip->pcm_xor;
+
+	if (chip->pcm_sample == 3) {
+		if (chip->pcm_little) {
+			if (chip->pcm_channels == 1) {
+				pdacf_transfer_mono24le((char *)chip->pcm_area + (off * 3), xor, size, rdp_port);
+			} else {
+				pdacf_transfer_stereo24le((char *)chip->pcm_area + (off * 6), xor, size, rdp_port);
+			}
+		} else {
+			if (chip->pcm_channels == 1) {
+				pdacf_transfer_mono24be((char *)chip->pcm_area + (off * 3), xor, size, rdp_port);
+			} else {
+				pdacf_transfer_stereo24be((char *)chip->pcm_area + (off * 6), xor, size, rdp_port);
+			}			
+		}
+		return;
+	}
+	if (chip->pcm_swab == 0) {
+		if (chip->pcm_channels == 1) {
+			if (chip->pcm_frame == 2) {
+				pdacf_transfer_mono16((u16 *)chip->pcm_area + off, xor, size, rdp_port);
+			} else {
+				pdacf_transfer_mono32((u32 *)chip->pcm_area + off, xor, size, rdp_port);
+			}
+		} else {
+			if (chip->pcm_frame == 2) {
+				pdacf_transfer_stereo16((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
+			} else {
+				pdacf_transfer_stereo32((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
+			}
+		}
+	} else {
+		if (chip->pcm_channels == 1) {
+			if (chip->pcm_frame == 2) {
+				pdacf_transfer_mono16sw((u16 *)chip->pcm_area + off, xor, size, rdp_port);
+			} else {
+				pdacf_transfer_mono32sw((u32 *)chip->pcm_area + off, xor, size, rdp_port);
+			}
+		} else {
+			if (chip->pcm_frame == 2) {
+				pdacf_transfer_stereo16sw((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
+			} else {
+				pdacf_transfer_stereo32sw((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
+			}
+		}
+	}
+}
+
+void pdacf_tasklet(unsigned long private_data)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, (void *)private_data, return);
+	int size, off, cont, rdp, wdp;
+
+	if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
+		return;
+	
+	if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream))
+		return;
+
+	rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
+	wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
+	// printk("TASKLET: rdp = %x, wdp = %x\n", rdp, wdp);
+	size = wdp - rdp;
+	if (size < 0)
+		size += 0x10000;
+	if (size == 0)
+		size = 0x10000;
+	size /= chip->pcm_frame;
+	if (size > 64)
+		size -= 32;
+
+#if 0
+	chip->pcm_hwptr += size;
+	chip->pcm_hwptr %= chip->pcm_size;
+	chip->pcm_tdone += size;
+	if (chip->pcm_frame == 2) {
+		unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
+		while (size-- > 0) {
+			inw(rdp_port);
+			inw(rdp_port);
+		}
+	} else {
+		unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
+		while (size-- > 0) {
+			inw(rdp_port);
+			inw(rdp_port);
+			inw(rdp_port);
+		}
+	}
+#else
+	off = chip->pcm_hwptr + chip->pcm_tdone;
+	off %= chip->pcm_size;
+	chip->pcm_tdone += size;
+	while (size > 0) {
+		cont = chip->pcm_size - off;
+		if (cont > size)
+			cont = size;
+		pdacf_transfer(chip, cont, off);
+		off += cont;
+		off %= chip->pcm_size;
+		size -= cont;
+	}
+#endif
+	spin_lock(&chip->reg_lock);
+	while (chip->pcm_tdone >= chip->pcm_period) {
+		chip->pcm_hwptr += chip->pcm_period;
+		chip->pcm_hwptr %= chip->pcm_size;
+		chip->pcm_tdone -= chip->pcm_period;
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(chip->pcm_substream);
+		spin_lock(&chip->reg_lock);
+	}
+	spin_unlock(&chip->reg_lock);
+	// printk("TASKLET: end\n");
+}
diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c	Sun Feb 29 12:58:39 2004
@@ -0,0 +1,363 @@
+/*
+ * Driver for Sound Core PDAudioCF soundcards
+ *
+ * PCM part
+ *
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/asoundef.h>
+#include "pdaudiocf.h"
+
+#define chip_t	pdacf_t
+
+
+/*
+ * we use a vmalloc'ed (sg-)buffer
+ */
+
+/* get the physical page pointer on the given offset */
+static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset)
+{
+	void *pageptr = subs->runtime->dma_area + offset;
+	return vmalloc_to_page(pageptr);
+}
+
+/*
+ * hw_params callback
+ * NOTE: this may be called not only once per pcm open!
+ */
+static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	if (runtime->dma_area) {
+		if (runtime->dma_bytes >= size)
+			return 0; /* already enough large */
+		vfree_nocheck(runtime->dma_area);
+	}
+	runtime->dma_area = vmalloc_nocheck(size);
+	if (! runtime->dma_area)
+		return -ENOMEM;
+	runtime->dma_bytes = size;
+	return 0;
+}
+
+/*
+ * hw_free callback
+ * NOTE: this may be called not only once per pcm open!
+ */
+static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	if (runtime->dma_area) {
+		vfree_nocheck(runtime->dma_area);
+		runtime->dma_area = NULL;
+	}
+	return 0;
+}
+
+/*
+ * clear the SRAM contents
+ */
+static int pdacf_pcm_clear_sram(pdacf_t *chip)
+{
+	int max_loop = 64 * 1024;
+
+	while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) {
+		if (max_loop-- < 0)
+			return -EIO;
+		inw(chip->port + PDAUDIOCF_REG_MD);
+	}
+	return 0;
+}
+
+/*
+ * pdacf_pcm_trigger - trigger callback for capture
+ */
+static int pdacf_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+	pdacf_t *chip = snd_pcm_substream_chip(subs);
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	int inc, ret = 0, rate;
+	unsigned short mask, val, tmp;
+
+	if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
+		return -EBUSY;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		chip->pcm_hwptr = 0;
+		chip->pcm_tdone = 0;
+		/* fall thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		mask = 0;
+		val = PDAUDIOCF_RECORD;
+		inc = 1;
+		rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mask = PDAUDIOCF_RECORD;
+		val = 0;
+		inc = -1;
+		rate = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	spin_lock(&chip->reg_lock);
+	chip->pcm_running += inc;
+	tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
+	if (chip->pcm_running) {
+		if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) {
+			chip->pcm_running -= inc;
+			ret = -EIO;
+			goto __end;
+		}
+	}
+	tmp &= ~mask;
+	tmp |= val;
+	pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp);
+      __end:
+	spin_unlock(&chip->reg_lock);
+	snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE);
+	return ret;
+}
+
+/*
+ * pdacf_pcm_hw_params - hw_params callback for playback and capture
+ */
+static int pdacf_pcm_hw_params(snd_pcm_substream_t *subs,
+				     snd_pcm_hw_params_t *hw_params)
+{
+	return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params));
+}
+
+/*
+ * pdacf_pcm_hw_free - hw_free callback for playback and capture
+ */
+static int pdacf_pcm_hw_free(snd_pcm_substream_t *subs)
+{
+	return snd_pcm_free_vmalloc_buffer(subs);
+}
+
+/*
+ * pdacf_pcm_prepare - prepare callback for playback and capture
+ */
+static int pdacf_pcm_prepare(snd_pcm_substream_t *subs)
+{
+	pdacf_t *chip = snd_pcm_substream_chip(subs);
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	u16 val, nval, aval;
+
+	if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
+		return -EBUSY;
+
+	chip->pcm_channels = runtime->channels;
+
+	chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0;
+#ifdef SNDRV_LITTLE_ENDIAN
+	chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0;
+#else
+	chip->pcm_swab = chip->pcm_little;
+#endif
+
+	if (snd_pcm_format_unsigned(runtime->format))
+		chip->pcm_xor = 0x80008000;
+
+	if (pdacf_pcm_clear_sram(chip) < 0)
+		return -EIO;
+	
+	val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
+	nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1);
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+		break;
+	default: /* 24-bit */
+		nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1;
+		break;
+	}
+	aval = 0;
+	chip->pcm_sample = 4;
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+		aval = AK4117_DIF_16R;
+		chip->pcm_frame = 2;
+		chip->pcm_sample = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+	case SNDRV_PCM_FORMAT_S24_3BE:
+		chip->pcm_sample = 3;
+		/* fall trough */
+	default: /* 24-bit */
+		aval = AK4117_DIF_24R;
+		chip->pcm_frame = 3;
+		chip->pcm_xor &= 0xffff0000;
+		break;
+	}
+
+	if (val != nval) {
+		snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval);
+		pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval);
+	}
+
+	val = pdacf_reg_read(chip,  PDAUDIOCF_REG_IER);
+	val &= ~(PDAUDIOCF_IRQLVLEN1);
+	val |= PDAUDIOCF_IRQLVLEN0;
+	pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val);
+
+	chip->pcm_size = runtime->buffer_size;
+	chip->pcm_period = runtime->period_size;
+	chip->pcm_area = runtime->dma_area;
+
+	return 0;
+}
+
+
+/*
+ * capture hw information
+ */
+
+static snd_pcm_hardware_t pdacf_pcm_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+				SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
+				SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
+	.rates =		SNDRV_PCM_RATE_32000 |
+				SNDRV_PCM_RATE_44100 |
+				SNDRV_PCM_RATE_48000 |
+				SNDRV_PCM_RATE_88200 |
+				SNDRV_PCM_RATE_96000 |
+				SNDRV_PCM_RATE_176400 |
+				SNDRV_PCM_RATE_192000,
+	.rate_min =		32000,
+	.rate_max =		192000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(512*1024),
+	.period_bytes_min =	8*1024,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		128,
+	.fifo_size =		0,
+};
+
+
+/*
+ * pdacf_pcm_capture_open - open callback for capture
+ */
+static int pdacf_pcm_capture_open(snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	pdacf_t *chip = snd_pcm_substream_chip(subs);
+
+	if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
+		return -EBUSY;
+
+	runtime->hw = pdacf_pcm_capture_hw;
+	runtime->private_data = chip;
+	chip->pcm_substream = subs;
+
+	return 0;
+}
+
+/*
+ * pdacf_pcm_capture_close - close callback for capture
+ */
+static int pdacf_pcm_capture_close(snd_pcm_substream_t *subs)
+{
+	pdacf_t *chip = snd_pcm_substream_chip(subs);
+
+	if (!chip)
+		return -EINVAL;
+	pdacf_reinit(chip, 0);
+	chip->pcm_substream = NULL;
+	return 0;
+}
+
+
+/*
+ * pdacf_pcm_capture_pointer - pointer callback for capture
+ */
+static snd_pcm_uframes_t pdacf_pcm_capture_pointer(snd_pcm_substream_t *subs)
+{
+	pdacf_t *chip = snd_pcm_substream_chip(subs);
+	return chip->pcm_hwptr;
+}
+
+/*
+ * operators for PCM capture
+ */
+static snd_pcm_ops_t pdacf_pcm_capture_ops = {
+	.open =		pdacf_pcm_capture_open,
+	.close =	pdacf_pcm_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	pdacf_pcm_hw_params,
+	.hw_free =	pdacf_pcm_hw_free,
+	.prepare =	pdacf_pcm_prepare,
+	.trigger =	pdacf_pcm_trigger,
+	.pointer =	pdacf_pcm_capture_pointer,
+	.page =		snd_pcm_get_vmalloc_page,
+};
+
+
+/*
+ * free callback for pcm
+ */
+static void snd_pdacf_pcm_free(snd_pcm_t *pcm)
+{
+	pdacf_t *chip = snd_magic_cast(pdacf_t, pcm->private_data, return);
+	chip->pcm = NULL;
+}
+
+/*
+ * snd_pdacf_pcm_new - create and initialize a pcm
+ */
+int snd_pdacf_pcm_new(pdacf_t *chip)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm);
+	if (err < 0)
+		return err;
+		
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_pdacf_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcm = pcm;
+	
+	err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
diff -Nru a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
--- a/sound/ppc/tumbler.c	Sun Feb 29 12:58:38 2004
+++ b/sound/ppc/tumbler.c	Sun Feb 29 12:58:38 2004
@@ -929,8 +929,8 @@
 		snapper_set_mix_vol(mix, VOL_IDX_PCM);
 		snapper_set_mix_vol(mix, VOL_IDX_PCM2);
 		snapper_set_mix_vol(mix, VOL_IDX_ADC);
-		tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
-		tumbler_set_mono_volume(mix, &tumbler_treble_vol_info);
+		tumbler_set_mono_volume(mix, &snapper_bass_vol_info);
+		tumbler_set_mono_volume(mix, &snapper_treble_vol_info);
 		snapper_set_drc(mix);
 	}
 	tumbler_set_master_volume(mix);
diff -Nru a/sound/synth/Makefile b/sound/synth/Makefile
--- a/sound/synth/Makefile	Sun Feb 29 12:58:38 2004
+++ b/sound/synth/Makefile	Sun Feb 29 12:58:38 2004
@@ -5,10 +5,16 @@
 
 snd-util-mem-objs := util_mem.o
 
+#
+# this function returns:
+#   "m" - CONFIG_SND_SEQUENCER is m
+#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+#   otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o
 obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o
-ifdef CONFIG_SND_SEQUENCER
-  obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o
-  obj-$(CONFIG_SND) += emux/
-endif
+obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-util-mem.o
+obj-$(call sequencer,$(CONFIG_SND)) += emux/
diff -Nru a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
--- a/sound/usb/usbaudio.c	Sun Feb 29 12:58:39 2004
+++ b/sound/usb/usbaudio.c	Sun Feb 29 12:58:39 2004
@@ -45,7 +45,6 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/usb.h>
-#include <linux/usb_ch9.h>
 #include <sound/core.h>
 #include <sound/info.h>
 #include <sound/pcm.h>
@@ -117,7 +116,7 @@
 	struct list_head list;
 	snd_pcm_format_t format;	/* format type */
 	unsigned int channels;		/* # channels */
-	unsigned int nonaudio: 1;	/* non-audio (type II) */
+	unsigned int fmt_type;		/* USB audio format type (1-3) */
 	unsigned int frame_size;	/* samples per frame for non-audio */
 	int iface;			/* interface number */
 	unsigned char altsetting;	/* corresponding alternate setting */
@@ -171,7 +170,7 @@
 	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
 	unsigned int curframesize;	/* current packet size in frames (for capture) */
 	unsigned int fill_max: 1;	/* fill max packet size always */
-	unsigned int nonaudio: 1;	/* Type II format (MPEG, AC3) */
+	unsigned int fmt_type;		/* USB audio format type (1-3) */
 
 	unsigned int running: 1;	/* running status */
 
@@ -201,6 +200,7 @@
 	snd_usb_audio_t *chip;
 	snd_pcm_t *pcm;
 	int pcm_index;
+	unsigned int fmt_type;		/* USB audio format type (1-3) */
 	snd_usb_substream_t substream[2];
 	struct list_head list;
 };
@@ -477,7 +477,7 @@
 		subs->transfer_sched += counts;
 		if (subs->transfer_sched >= runtime->period_size) {
 			subs->transfer_sched -= runtime->period_size;
-			if (subs->nonaudio) {
+			if (subs->fmt_type == USB_FORMAT_TYPE_II) {
 				if (subs->transfer_sched > 0) {
 					/* FIXME: fill-max mode is not supported yet */
 					offs -= subs->transfer_sched;
@@ -894,7 +894,7 @@
 		u->subs = subs;
 		u->transfer = 0;
 		u->packets = npacks[i];
-		if (subs->nonaudio)
+		if (subs->fmt_type == USB_FORMAT_TYPE_II)
 			u->packets++; /* for transfer delimiter */
 		if (! is_playback) {
 			/* allocate a capture buffer per urb */
@@ -1588,7 +1588,7 @@
 			runtime->hw.channels_min = fp->channels;
 		if (runtime->hw.channels_max < fp->channels)
 			runtime->hw.channels_max = fp->channels;
-		if (fp->nonaudio && fp->frame_size > 0) {
+		if (fp->fmt_type == USB_FORMAT_TYPE_II && fp->frame_size > 0) {
 			/* FIXME: there might be more than one audio formats... */
 			runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
 				fp->frame_size;
@@ -1895,7 +1895,7 @@
 	subs->formats |= 1ULL << fp->format;
 	subs->endpoint = fp->endpoint;
 	subs->num_formats++;
-	subs->nonaudio = fp->nonaudio;
+	subs->fmt_type = fp->fmt_type;
 }
 
 
@@ -1954,17 +1954,12 @@
 
 	list_for_each(p, &chip->pcm_list) {
 		as = list_entry(p, snd_usb_stream_t, list);
+		if (as->fmt_type != fp->fmt_type)
+			continue;
 		subs = &as->substream[stream];
 		if (! subs->endpoint)
-			break;
+			continue;
 		if (subs->endpoint == fp->endpoint) {
-			if (fp->nonaudio) {
-				if (!subs->nonaudio || subs->formats != (1ULL << fp->format))
-					continue; /* non-linear formats are handled exclusively */
-			} else {
-				if (subs->nonaudio)
-					continue;
-			}
 			list_add_tail(&fp->list, &subs->fmt_list);
 			subs->num_formats++;
 			subs->formats |= 1ULL << fp->format;
@@ -1974,6 +1969,8 @@
 	/* look for an empty stream */
 	list_for_each(p, &chip->pcm_list) {
 		as = list_entry(p, snd_usb_stream_t, list);
+		if (as->fmt_type != fp->fmt_type)
+			continue;
 		subs = &as->substream[stream];
 		if (subs->endpoint)
 			continue;
@@ -1991,6 +1988,7 @@
 	memset(as, 0, sizeof(*as));
 	as->pcm_index = chip->pcm_devs;
 	as->chip = chip;
+	as->fmt_type = fp->fmt_type;
 	err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
 			  stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
 			  stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
@@ -2216,7 +2214,6 @@
 		break;
 	}
 	fp->channels = 1;
-	fp->nonaudio = 1;
 	brate = combine_word(&fmt[4]); 	/* fmt[4,5] : wMaxBitRate (in kbps) */
 	framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */
 	snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
@@ -2242,6 +2239,7 @@
 			   dev->devnum, fp->iface, fp->altsetting, fmt[3]);
 		return -1;
 	}
+	fp->fmt_type = fmt[3];
 	if (err < 0)
 		return err;
 #if 1
diff -Nru a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
--- a/sound/usb/usbmidi.c	Sun Feb 29 12:58:39 2004
+++ b/sound/usb/usbmidi.c	Sun Feb 29 12:58:39 2004
@@ -1,7 +1,7 @@
 /*
  * usbmidi.c - ALSA USB MIDI driver
  *
- * Copyright (c) 2002 Clemens Ladisch
+ * Copyright (c) 2002-2004 Clemens Ladisch
  * All rights reserved.
  *
  * Based on the OSS usb-midi driver by NAGANO Daisuke,
@@ -727,18 +727,92 @@
 	return NULL;
 }
 
+/*
+ * This list specifies names for ports that do not fit into the standard
+ * "(product) MIDI (n)" schema because they aren't external MIDI ports,
+ * such as internal control or synthesizer ports.
+ */
+static struct {
+	__u16 vendor;
+	__u16 product;
+	int port;
+	const char *name_format;
+} snd_usbmidi_port_names[] = {
+	/* Roland UA-100 */
+	{0x0582, 0x0000, 2, "%s Control"},
+	/* Roland SC-8850 */
+	{0x0582, 0x0003, 0, "%s Part A"},
+	{0x0582, 0x0003, 1, "%s Part B"},
+	{0x0582, 0x0003, 2, "%s Part C"},
+	{0x0582, 0x0003, 3, "%s Part D"},
+	{0x0582, 0x0003, 4, "%s MIDI 1"},
+	{0x0582, 0x0003, 5, "%s MIDI 2"},
+	/* Roland U-8 */
+	{0x0582, 0x0004, 1, "%s Control"},
+	/* Roland SC-8820 */
+	{0x0582, 0x0007, 0, "%s Part A"},
+	{0x0582, 0x0007, 1, "%s Part B"},
+	{0x0582, 0x0007, 2, "%s MIDI"},
+	/* Roland SK-500 */
+	{0x0582, 0x000b, 0, "%s Part A"},
+	{0x0582, 0x000b, 1, "%s Part B"},
+	{0x0582, 0x000b, 2, "%s MIDI"},
+	/* Roland SC-D70 */
+	{0x0582, 0x000c, 0, "%s Part A"},
+	{0x0582, 0x000c, 1, "%s Part B"},
+	{0x0582, 0x000c, 2, "%s MIDI"},
+	/* Edirol UM-880 */
+	{0x0582, 0x0014, 8, "%s Control"},
+	/* Edirol SD-90 */
+	{0x0582, 0x0016, 0, "%s Part A"},
+	{0x0582, 0x0016, 1, "%s Part B"},
+	{0x0582, 0x0016, 2, "%s MIDI 1"},
+	{0x0582, 0x0016, 3, "%s MIDI 2"},
+	/* Edirol UM-550 */
+	{0x0582, 0x0023, 5, "%s Control"},
+	/* Edirol SD-20 */
+	{0x0582, 0x0027, 0, "%s Part A"},
+	{0x0582, 0x0027, 1, "%s Part B"},
+	{0x0582, 0x0027, 2, "%s MIDI"},
+	/* Edirol SD-80 */
+	{0x0582, 0x0029, 0, "%s Part A"},
+	{0x0582, 0x0029, 1, "%s Part B"},
+	{0x0582, 0x0029, 2, "%s MIDI 1"},
+	{0x0582, 0x0029, 3, "%s MIDI 2"},
+	/* M-Audio MidiSport 8x8 */
+	{0x0763, 0x1031, 8, "%s Control"},
+	{0x0763, 0x1033, 8, "%s Control"},
+};
+
 static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi,
 				       int stream, int number,
 				       snd_rawmidi_substream_t** rsubstream)
 {
+	int i;
+	__u16 vendor, product;
+	const char *name_format;
+
 	snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number);
 	if (!substream) {
 		snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number);
 		return;
 	}
+
 	/* TODO: read port name from jack descriptor */
+	name_format = "%s MIDI %d";
+	vendor = umidi->chip->dev->descriptor.idVendor;
+	product = umidi->chip->dev->descriptor.idProduct;
+	for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) {
+		if (snd_usbmidi_port_names[i].vendor == vendor &&
+		    snd_usbmidi_port_names[i].product == product &&
+		    snd_usbmidi_port_names[i].port == number) {
+			name_format = snd_usbmidi_port_names[i].name_format;
+			break;
+		}
+	}
 	snprintf(substream->name, sizeof(substream->name),
-		 "%s Port %d", umidi->chip->card->shortname, number);
+		 name_format, umidi->chip->card->shortname, number + 1);
+
 	*rsubstream = substream;
 }
 
diff -Nru a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
--- a/sound/usb/usbquirks.h	Sun Feb 29 12:58:39 2004
+++ b/sound/usb/usbquirks.h	Sun Feb 29 12:58:39 2004
@@ -502,6 +502,32 @@
 	}
 },
 {
+	/*
+	 * This quirk is for the "Advanced Driver" mode. If off, the UA-3FX
+	 * is standard compliant, but has only 16-bit PCM.
+	 */
+	USB_DEVICE(0x0582, 0x0050),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UA-3FX",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = & (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
+{
 	USB_DEVICE(0x0582, 0x0052),
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "EDIROL",
@@ -686,6 +712,16 @@
 		.ifnum = QUIRK_NO_INTERFACE
 	}
 	
+},
+
+{
+	USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0013),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Terratec",
+		.product_name = "PHASE 26",
+		.ifnum = 3,
+		.type = QUIRK_MIDI_STANDARD_INTERFACE
+	}
 },
 
 #undef USB_DEVICE_VENDOR_SPEC