From c4eeeab42ef7f73f7ba01ec9579d1c762e5f2e33 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@suse.cz>
Date: Tue, 1 Oct 2002 02:09:28 -0700
Subject: [PATCH] [PATCH] ALSA update [8/12] - 2002/09/06

  - VIA686 and VIA8233 driver merge to VIA82xx
  - ioctl32 fixes
  - fixed OOPS in snd_pcm_sgbuf_delete (not initialized)
  - I2C - call hw_stop() before returning at the error pointer
  - AC'97 codec - added more AC97 IDs by Laszlo Melis
  - CS46xx - mutex initialization fix
  - ENS1371 - added one more card to S/PDIF capabilities
  - intel8x0
    - fixed secondary and third codec indexes
  - PPC Keywest - initialize MCS in loop until it succeeds
  - PPC Tumbler - the initial support for snapper (TAS3004) on some tibook
  - USB Audio - mixer fixes
---
 include/sound/sndmagic.h           |    3 +-
 include/sound/version.h            |    2 +-
 sound/core/Makefile                |    3 +-
 sound/core/ioctl32/hwdep32.c       |    2 -
 sound/core/ioctl32/ioctl32.c       |  118 +--
 sound/core/ioctl32/ioctl32.h       |   22 +-
 sound/core/ioctl32/pcm32.c         |  104 +--
 sound/core/oss/mixer_oss.c         |    8 +-
 sound/core/pcm_sgbuf.c             |    3 +
 sound/core/seq/Makefile            |    2 +-
 sound/core/seq/oss/seq_oss_synth.c |    4 +
 sound/drivers/mpu401/Makefile      |    2 +-
 sound/i2c/i2c.c                    |   21 +-
 sound/pci/Config.help              |    7 +-
 sound/pci/Config.in                |    3 +-
 sound/pci/Makefile                 |    6 +-
 sound/pci/ac97/Makefile            |    3 +-
 sound/pci/ac97/ac97_codec.c        |   12 +
 sound/pci/ac97/ac97_id.h           |    1 +
 sound/pci/cs4281.c                 |   17 +-
 sound/pci/cs46xx/cs46xx_lib.c      |    3 +
 sound/pci/ens1370.c                |    1 +
 sound/pci/es1938.c                 |    4 +-
 sound/pci/es1968.c                 |    4 +-
 sound/pci/ice1712.c                |  129 ++-
 sound/pci/intel8x0.c               |   27 +-
 sound/pci/rme32.c                  |    8 +-
 sound/pci/via686.c                 | 1232 -------------------------
 sound/pci/via8233.c                | 1022 ---------------------
 sound/pci/via82xx.c                | 1345 ++++++++++++++++++++++++++++
 sound/ppc/keywest.c                |    5 +-
 sound/ppc/pmac.c                   |   12 +-
 sound/ppc/pmac.h                   |    3 +-
 sound/ppc/powermac.c               |    6 +-
 sound/ppc/tumbler.c                |  299 ++++++-
 sound/ppc/tumbler_volume.h         |   66 +-
 sound/usb/usbaudio.c               |   39 +-
 sound/usb/usbmixer.c               |   66 +-
 38 files changed, 2047 insertions(+), 2567 deletions(-)
 delete mode 100644 sound/pci/via686.c
 delete mode 100644 sound/pci/via8233.c
 create mode 100644 sound/pci/via82xx.c

diff --git a/include/sound/sndmagic.h b/include/sound/sndmagic.h
index 0911f1fe0865..83495c4ce9e5 100644
--- a/include/sound/sndmagic.h
+++ b/include/sound/sndmagic.h
@@ -113,7 +113,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic)
 #define intel8x0_t_magic			0xa15a2a01
 #define es1968_t_magic				0xa15a2b01
 #define esschan_t_magic				0xa15a2b02
-#define via686a_t_magic				0xa15a2c01
+#define via82xx_t_magic				0xa15a2c01
 #define pdplus_t_magic				0xa15a2d01
 #define cmipci_t_magic				0xa15a2e01
 #define ymfpci_t_magic				0xa15a2f01
@@ -126,7 +126,6 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic)
 #define m3_dma_t_magic				0xa15a3202
 #define nm256_t_magic				0xa15a3301
 #define nm256_dma_t_magic			0xa15a3302
-#define via8233_t_magic				0xa15a3401
 #define pmac_t_magic				0xa15a3501
 #define ali_t_magic				0xa15a3601
 #define mtpav_t_magic				0xa15a3701
diff --git a/include/sound/version.h b/include/sound/version.h
index 2ce8305b4f6c..99faca4263dc 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated automatically by configure.  */
 #define CONFIG_SND_VERSION "0.9.0rc3"
-#define CONFIG_SND_DATE " (Mon Aug 26 16:28:35 2002 UTC)"
+#define CONFIG_SND_DATE " (Fri Sep 06 15:06:56 2002 UTC)"
diff --git a/sound/core/Makefile b/sound/core/Makefile
index cf1af3e38260..4dfd3908ccef 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -78,8 +78,7 @@ obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o
 obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd.o
 obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
-obj-$(CONFIG_SND_VIA686) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
-obj-$(CONFIG_SND_VIA8233) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-pcm.o
 obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
diff --git a/sound/core/ioctl32/hwdep32.c b/sound/core/ioctl32/hwdep32.c
index 19d189ae88c9..cf968a6c5382 100644
--- a/sound/core/ioctl32/hwdep32.c
+++ b/sound/core/ioctl32/hwdep32.c
@@ -28,7 +28,5 @@
 struct ioctl32_mapper hwdep_mappers[] = {
 	{ SNDRV_HWDEP_IOCTL_PVERSION, NULL },
 	{ SNDRV_HWDEP_IOCTL_INFO, NULL },
-	{ SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL },
-	{ SNDRV_CTL_IOCTL_HWDEP_INFO, NULL },
 	{ 0 },
 };
diff --git a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c
index 7f52251fecdf..8002791080ec 100644
--- a/sound/core/ioctl32/ioctl32.c
+++ b/sound/core/ioctl32/ioctl32.c
@@ -47,14 +47,10 @@ int snd_ioctl32_register(struct ioctl32_mapper *mappers)
 	int err;
 	struct ioctl32_mapper *m;
 
-	lock_kernel();
 	for (m = mappers; m->cmd; m++) {
 		err = register_ioctl32_conversion(m->cmd, m->handler);
-		if (err < 0) {
-			unlock_kernel();
-			return err;
-		}
-		m->registered++;
+		if (err >= 0)
+			m->registered++;
 	}
 	return 0;
 }
@@ -63,14 +59,12 @@ void snd_ioctl32_unregister(struct ioctl32_mapper *mappers)
 {
 	struct ioctl32_mapper *m;
 
-	lock_kernel();
 	for (m = mappers; m->cmd; m++) {
 		if (m->registered) {
 			unregister_ioctl32_conversion(m->cmd);
 			m->registered = 0;
 		}
 	}
-	unlock_kernel();
 }
 
 
@@ -100,36 +94,32 @@ static int _snd_ioctl32_ctl_elem_list(unsigned int fd, unsigned int cmd, unsigne
 {
 	struct sndrv_ctl_elem_list32 data32;
 	struct sndrv_ctl_elem_list data;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 	int err;
 
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.offset = data32.offset;
 	data.space = data32.space;
 	data.used = data32.used;
 	data.count = data32.count;
 	data.pids = A(data32.pids);
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* copy the result */
 	data32.offset = data.offset;
 	data32.space = data.space;
 	data32.used = data.used;
 	data32.count = data.count;
 	//data.pids = data.pids;
-	if (copy_to_user((void*)arg, &data32, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
- __err:
-	set_fs(oldseg);
-	return err;
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return -EFAULT;
+	return 0;
 }
 
 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST);
@@ -171,22 +161,22 @@ static int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigne
 	struct sndrv_ctl_elem_info data;
 	struct sndrv_ctl_elem_info32 data32;
 	int err;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.id = data32.id;
 	/* we need to copy the item index.
 	 * hope this doesn't break anything..
 	 */
 	data.value.enumerated.item = data32.value.enumerated.item;
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* restore info to 32bit */
 	data32.id = data.id;
 	data32.type = data.type;
@@ -215,10 +205,8 @@ static int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigne
 		break;
 	}
 	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
-		err = -EFAULT;
- __err:
-	set_fs(oldseg);
-	return err;
+		return -EFAULT;
+	return 0;
 }
 
 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO);
@@ -281,26 +269,20 @@ static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsign
 	struct sndrv_ctl_elem_value32 data32;
 	int err, i;
 	int type;
-	mm_segment_t oldseg = get_fs();
-
-	set_fs(KERNEL_DS);
+	mm_segment_t oldseg;
 
 	/* FIXME: check the sane ioctl.. */
 
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.id = data32.id;
 	data.indirect = data32.indirect;
 	if (data.indirect) /* FIXME: this is not correct for long arrays */
 		data.value.integer.value_ptr = (void*)TO_PTR(data32.value.integer.value_ptr);
 	type = get_ctl_type(file, &data.id);
-	if (type < 0) {
-		err = type;
-		goto __err;
-	}
+	if (type < 0)
+		return type;
 	if (! data.indirect) {
 		switch (type) {
 		case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
@@ -329,9 +311,12 @@ static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsign
 		}
 	}
 
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* restore info to 32bit */
 	if (! data.indirect) {
 		switch (type) {
@@ -360,10 +345,8 @@ static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsign
 		}
 	}
 	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
-		err = -EFAULT;
- __err:
-	set_fs(oldseg);
-	return err;
+		return -EFAULT;
+	return 0;
 }
 
 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_read, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_READ);
@@ -392,6 +375,7 @@ static struct ioctl32_mapper control_mappers[] = {
 	{ SNDRV_CTL_IOCTL_ELEM_LOCK, NULL },
 	{ SNDRV_CTL_IOCTL_ELEM_UNLOCK, NULL },
 	{ SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, NULL },
+	{ SNDRV_CTL_IOCTL_HWDEP_INFO, NULL },
 	{ SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL },
 	{ SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL },
 	{ SNDRV_CTL_IOCTL_PCM_INFO, NULL },
@@ -427,37 +411,13 @@ static void snd_ioctl32_done(void)
 
 static int __init snd_ioctl32_init(void)
 {
-	int err;
-	
-	err = snd_ioctl32_register(control_mappers);
-	if (err < 0)
-		return err;
-	err = snd_ioctl32_register(pcm_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
-	err = snd_ioctl32_register(rawmidi_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
-	err = snd_ioctl32_register(timer_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
-	err = snd_ioctl32_register(hwdep_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
+	snd_ioctl32_register(control_mappers);
+	snd_ioctl32_register(pcm_mappers);
+	snd_ioctl32_register(rawmidi_mappers);
+	snd_ioctl32_register(timer_mappers);
+	snd_ioctl32_register(hwdep_mappers);
 #ifdef CONFIG_SND_SEQUENCER
-	err = snd_ioctl32_register(seq_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
+	snd_ioctl32_register(seq_mappers);
 #endif
 	return 0;
 }
diff --git a/sound/core/ioctl32/ioctl32.h b/sound/core/ioctl32/ioctl32.h
index a485e172074b..30cd42921435 100644
--- a/sound/core/ioctl32/ioctl32.h
+++ b/sound/core/ioctl32/ioctl32.h
@@ -60,27 +60,23 @@ static int _snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long
 {\
 	struct sndrv_##type##32 data32;\
 	struct sndrv_##type data;\
-	mm_segment_t oldseg = get_fs();\
+	mm_segment_t oldseg;\
 	int err;\
-	set_fs(KERNEL_DS);\
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {\
-		err = -EFAULT;\
-		goto __err;\
-	}\
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\
+		return -EFAULT;\
 	memset(&data, 0, sizeof(data));\
 	convert_from_32(type, &data, &data32);\
+	oldseg = get_fs();\
+	set_fs(KERNEL_DS);\
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);\
 	if (err < 0) \
-		goto __err;\
+		return err;\
 	if (native_ctl & (_IOC_READ << _IOC_DIRSHIFT)) {\
 		convert_to_32(type, &data32, &data);\
-		if (copy_to_user((void*)arg, &data32, sizeof(data32))) {\
-			err = -EFAULT;\
-			goto __err;\
-		}\
+		if (copy_to_user((void*)arg, &data32, sizeof(data32)))\
+			return -EFAULT;\
 	}\
- __err: set_fs(oldseg);\
-	return err;\
+	return 0;\
 }
 
 #define DEFINE_ALSA_IOCTL_ENTRY(name,type,native_ctl) \
diff --git a/sound/core/ioctl32/pcm32.c b/sound/core/ioctl32/pcm32.c
index 2a501fa92c95..66b8a1bf3565 100644
--- a/sound/core/ioctl32/pcm32.c
+++ b/sound/core/ioctl32/pcm32.c
@@ -189,30 +189,26 @@ static int _snd_ioctl32_xferi(unsigned int fd, unsigned int cmd, unsigned long a
 {
 	struct sndrv_xferi32 data32;
 	struct sndrv_xferi data;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 	int err;
 
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.result = data32.result;
 	data.buf = A(data32.buf);
 	data.frames = data32.frames;
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* copy the result */
 	data32.result = data.result;
-	if (copy_to_user((void*)arg, &data32, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
- __err:
-	set_fs(oldseg);
-	return err;
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return -EFAULT;
+	return 0;
 }
 
 
@@ -237,9 +233,7 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a
 	void *bufs[128];
 	int err = 0, ch, i;
 	u32 *bufptr;
-	mm_segment_t oldseg = get_fs();
-
-	set_fs(KERNEL_DS);
+	mm_segment_t oldseg;
 
 	/* FIXME: need to check whether fop->ioctl is sane */
 
@@ -250,41 +244,31 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a
 	/* check validty of the command */
 	switch (native_ctl) {
 	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
-		if (substream->stream  != SNDRV_PCM_STREAM_PLAYBACK) {
-			err = -EINVAL;
-			goto __err;
-		}
-		if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-			err = -EBADFD;
-			goto __err;
-		}
+		if (substream->stream  != SNDRV_PCM_STREAM_PLAYBACK)
+			return -EINVAL;
+		if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+			return -EBADFD;
 		break;
 	case SNDRV_PCM_IOCTL_READN_FRAMES:
-		if (substream->stream  != SNDRV_PCM_STREAM_CAPTURE) {
-			err = -EINVAL;
-			goto __err;
-		}
+		if (substream->stream  != SNDRV_PCM_STREAM_CAPTURE)
+			return -EINVAL;
 		break;
 	}
-	if ((ch = substream->runtime->channels) > 128) {
-		err = -EINVAL;
-		goto __err;
-	}
-	if (get_user(data32.frames, &srcptr->frames)) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if ((ch = substream->runtime->channels) > 128)
+		return -EINVAL;
+	if (get_user(data32.frames, &srcptr->frames))
+		return -EFAULT;
 	__get_user(data32.bufs, &srcptr->bufs);
 	bufptr = (u32*)TO_PTR(data32.bufs);
 	for (i = 0; i < ch; i++) {
 		u32 ptr;
-		if (get_user(ptr, bufptr)) {
-			err = -EFAULT;
-			goto __err;
-		}
+		if (get_user(ptr, bufptr))
+			return -EFAULT;
 		bufs[ch] = (void*)TO_PTR(ptr);
 		bufptr++;
 	}
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	switch (native_ctl) {
 	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
 		err = snd_pcm_lib_writev(substream, bufs, data32.frames);
@@ -293,14 +277,12 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a
 		err = snd_pcm_lib_readv(substream, bufs, data32.frames);
 		break;
 	}
-	
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	if (put_user(err, &srcptr->result))
-		err = -EFAULT;
- __err:
-	set_fs(oldseg);
-	return err < 0 ? err : 0;
+		return -EFAULT;
+	return 0;
 }
 
 
@@ -363,24 +345,22 @@ static int _snd_ioctl32_pcm_hw_params_old(unsigned int fd, unsigned int cmd, uns
 {
 	struct sndrv_pcm_hw_params_old32 data32;
 	struct sndrv_pcm_hw_params data;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 	int err;
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	snd_pcm_hw_convert_from_old_params(&data, &data32);
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	snd_pcm_hw_convert_to_old_params(&data32, &data);
-	if (copy_to_user((void*)arg, &data32, sizeof(data32))) {
-	  err = -EFAULT;
-	  goto __err;
-	}
- __err: set_fs(oldseg);
-	return err;
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return  -EFAULT;
+	return 0;
 }
 
 
@@ -451,9 +431,5 @@ struct ioctl32_mapper pcm_mappers[] = {
 	{ SNDRV_PCM_IOCTL_LINK, NULL },
 	{ SNDRV_PCM_IOCTL_UNLINK, NULL },
 
-	{ SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL },
-	{ SNDRV_CTL_IOCTL_PCM_INFO, NULL },
-	{ SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL },
-
 	{ 0 },
 };
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 3d638439d648..a949ac79b020 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1169,10 +1169,14 @@ static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag)
 			return err;
 		}
 		mixer->card = card;
-		strcpy(mixer->name, name);
+		if (*card->mixername) {
+			strncpy(mixer->name, card->mixername, sizeof(mixer->name) - 1);
+			mixer->name[sizeof(mixer->name)-1] = 0;
+		} else
+			strcpy(mixer->name, name);
 		snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
 				      card->number,
-				      name);
+				      mixer->name);
 		for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
 			mixer->slots[idx].number = idx;
 		card->mixer_oss = mixer;
diff --git a/sound/core/pcm_sgbuf.c b/sound/core/pcm_sgbuf.c
index 95651a098296..d1eb0bdc38e8 100644
--- a/sound/core/pcm_sgbuf.c
+++ b/sound/core/pcm_sgbuf.c
@@ -80,6 +80,9 @@ int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream)
 {
 	struct snd_sg_buf *sgbuf;
 
+	/* return in case, when sgbuf is not initialized */
+	if (substream->dma_private == NULL)
+		return -EINVAL;
 	sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
 	sgbuf_shrink(sgbuf, 0);
 	if (sgbuf->table)
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 7c5f6f91586c..42328414ff7b 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -67,7 +67,7 @@ obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-mid
 obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
-obj-$(CONFIG_SND_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_VIA82XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index 2174b630df8d..edb37b268090 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -146,6 +146,8 @@ snd_seq_oss_synth_register(snd_seq_device_t *dev)
 	debug_printk(("synth %s registered %d\n", rec->name, i));
 	spin_unlock_irqrestore(&register_lock, flags);
 	dev->driver_data = rec;
+	if (i < SNDRV_CARDS)
+		snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);
 	return 0;
 }
 
@@ -176,6 +178,8 @@ snd_seq_oss_synth_unregister(snd_seq_device_t *dev)
 		max_synth_devs = index + 1;
 	}
 	spin_unlock_irqrestore(&register_lock, flags);
+	if (rec->seq_device < SNDRV_CARDS)
+		snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device, NULL);
 
 	snd_use_lock_sync(&rec->use_lock);
 	kfree(rec);
diff --git a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile
index d227400254f1..0e98764e844c 100644
--- a/sound/drivers/mpu401/Makefile
+++ b/sound/drivers/mpu401/Makefile
@@ -35,7 +35,7 @@ obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o
-obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_VIA82XX) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o
diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c
index 5b38d9ee1c6b..5015a8bc8c0d 100644
--- a/sound/i2c/i2c.c
+++ b/sound/i2c/i2c.c
@@ -260,11 +260,15 @@ static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes,
 	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
 		return -EIO;		/* not yet implemented */
 	snd_i2c_bit_start(bus);
-	if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0)
+	if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) {
+		snd_i2c_bit_hw_stop(bus);
 		return err;
+	}
 	while (count-- > 0) {
-		if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0)
+		if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) {
+			snd_i2c_bit_hw_stop(bus);
 			return err;
+		}
 		res++;
 	}
 	snd_i2c_bit_stop(bus);
@@ -279,11 +283,15 @@ static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes,
 	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
 		return -EIO;		/* not yet implemented */
 	snd_i2c_bit_start(bus);
-	if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0)
+	if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) {
+		snd_i2c_bit_hw_stop(bus);
 		return err;
+	}
 	while (count-- > 0) {
-		if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0)
+		if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) {
+			snd_i2c_bit_hw_stop(bus);
 			return err;
+		}
 		*bytes++ = (unsigned char)err;
 		res++;
 	}
@@ -300,10 +308,9 @@ static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr)
 	if (addr & 0x7f80)	/* invalid address */
 		return -EINVAL;
 	snd_i2c_bit_start(bus);
-	if ((err = snd_i2c_bit_sendbyte(bus, addr << 1)) < 0)
-		return err;
+	err = snd_i2c_bit_sendbyte(bus, addr << 1);
 	snd_i2c_bit_stop(bus);
-	return 1;		/* present */
+	return err;
 }
 
 EXPORT_SYMBOL(snd_i2c_bus_create);
diff --git a/sound/pci/Config.help b/sound/pci/Config.help
index 6241d287669f..6047d766370d 100644
--- a/sound/pci/Config.help
+++ b/sound/pci/Config.help
@@ -82,8 +82,5 @@ CONFIG_SND_INTEL8X0
 CONFIG_SND_SONICVIBES
   Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards.
 
-CONFIG_SND_VIA686
-  Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge.
-
-CONFIG_SND_VIA8233
-  Say 'Y' or 'M' to include support for VIA VT8233 South Bridge.
+CONFIG_SND_VIA82XX
+  Say 'Y' or 'M' to include support for VIA VT82C686A/B, VT8233 South Bridge.
diff --git a/sound/pci/Config.in b/sound/pci/Config.in
index 1b43fdb7f8fe..4f7cb24ec97a 100644
--- a/sound/pci/Config.in
+++ b/sound/pci/Config.in
@@ -27,8 +27,7 @@ dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND
 dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND
 dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND
 dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND
-dep_tristate 'VIA 82C686A/B South Bridge' CONFIG_SND_VIA686 $CONFIG_SND
-dep_tristate 'VIA 8233 South Bridge' CONFIG_SND_VIA8233 $CONFIG_SND
+dep_tristate 'VIA 82C686A/B, 8233 South Bridge' CONFIG_SND_VIA82XX $CONFIG_SND
 
 # define gameport if necessary
 if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 6c9265602890..a2b80c9b6d9a 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -17,8 +17,7 @@ snd-maestro3-objs := maestro3.o
 snd-rme32-objs := rme32.o
 snd-rme96-objs := rme96.o
 snd-sonicvibes-objs := sonicvibes.o
-snd-via686-objs := via686.o
-snd-via8233-objs := via8233.o
+snd-via82xx-objs := via82xx.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ALS4000) += snd-als4000.o
@@ -35,8 +34,7 @@ obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
 obj-$(CONFIG_SND_RME32) += snd-rme32.o
 obj-$(CONFIG_SND_RME96) += snd-rme96.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
-obj-$(CONFIG_SND_VIA686) += snd-via686.o
-obj-$(CONFIG_SND_VIA8233) += snd-via8233.o
+obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
 
 obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/
 
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index c4bdc870a10c..e1383064eda0 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/Makefile
@@ -17,8 +17,7 @@ obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o
 obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o
-obj-$(CONFIG_SND_VIA686) += snd-ac97-codec.o
-obj-$(CONFIG_SND_VIA8233) += snd-ac97-codec.o
+obj-$(CONFIG_SND_VIA82XX) += snd-ac97-codec.o
 obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o
 obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o
 obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 0d7dbcca22ec..93d847bbc8a9 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -65,6 +65,7 @@ static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = {
 { 0x41445300, 0xffffff00, "Analog Devices",	NULL },
 { 0x414c4300, 0xffffff00, "Realtek",		NULL },
 { 0x414c4700, 0xffffff00, "Avance Logic",	NULL },
+{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL },
 { 0x43525900, 0xffffff00, "Cirrus Logic",	NULL },
 { 0x43585400, 0xffffff00, "Conexant",           NULL },
 { 0x45838300, 0xffffff00, "ESS Technology",	NULL },
@@ -95,6 +96,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
 { 0x41445360, 0xffffffff, "AD1885",		patch_ad1885 },
 { 0x41445361, 0xffffffff, "AD1886",		patch_ad1886 },
 { 0x41445362, 0xffffffff, "AD1887",		patch_ad1881 },
+{ 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881 },
 { 0x41445372, 0xffffffff, "AD1981A",		patch_ad1881 },
 { 0x414c4300, 0xfffffff0, "RL5306",	 	NULL },
 { 0x414c4310, 0xfffffff0, "RL5382", 		NULL },
@@ -104,6 +106,8 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
 { 0x414c4730, 0xffffffff, "ALC101",		NULL },
 { 0x414c4740, 0xfffffff0, "ALC202",		NULL },
 { 0x414c4750, 0xfffffff0, "ALC250",		NULL },
+{ 0x434d4941, 0xffffffff, "CMI9738",		NULL },
+{ 0x434d4961, 0xffffffff, "CMI9739",		NULL },
 { 0x43525900, 0xfffffff8, "CS4297",		NULL },
 { 0x43525910, 0xfffffff8, "CS4297A",		patch_cirrus_spdif },
 { 0x43525920, 0xfffffff8, "CS4294/4298",	NULL },
@@ -122,6 +126,8 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
 { 0x4e534331, 0xffffffff, "LM4549",		NULL },
 { 0x53494c22, 0xffffffff, "Si3036",		NULL },
 { 0x53494c23, 0xffffffff, "Si3038",		NULL },
+{ 0x54524102, 0xffffffff, "TR28022",		NULL },
+{ 0x54524106, 0xffffffff, "TR28026",		NULL },
 { 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028 }, // added by xin jin [07/09/99]
 { 0x54524123, 0xffffffff, "TR28602",		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
 { 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL },
@@ -130,14 +136,19 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
 { 0x574d4c00, 0xffffffff, "WM9701A",		patch_wolfson00 },
 { 0x574d4c03, 0xffffffff, "WM9703/9707",	patch_wolfson03 },
 { 0x574d4c04, 0xffffffff, "WM9704 (quad)",	patch_wolfson04 },
+{ 0x574d4c05, 0xffffffff, "WM9705",		NULL },	// patch?
 { 0x594d4800, 0xffffffff, "YMF743",		NULL },
+{ 0x594d4802, 0xffffffff, "YMF752",		NULL },
+{ 0x594d4803, 0xffffffff, "YMF753",		NULL },
 { 0x83847600, 0xffffffff, "STAC9700/83/84",	NULL },
 { 0x83847604, 0xffffffff, "STAC9701/3/4/5",	NULL },
 { 0x83847605, 0xffffffff, "STAC9704",		NULL },
 { 0x83847608, 0xffffffff, "STAC9708/11",	patch_sigmatel_stac9708 },
 { 0x83847609, 0xffffffff, "STAC9721/23",	patch_sigmatel_stac9721 },
 { 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744 },
+{ 0x83847650, 0xffffffff, "STAC9750/51",	NULL },	// patch?
 { 0x83847656, 0xffffffff, "STAC9756/57",	patch_sigmatel_stac9756 },
+{ 0x83847666, 0xffffffff, "STAC9766/67",	NULL }, // patch?
 { 0, 	      0,	  NULL,			NULL }
 };
 
@@ -201,6 +212,7 @@ static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg)
 		return 1;
 	case AC97_ID_AD1885:	/* AD1885 */
 	case AC97_ID_AD1886:	/* AD1886 */
+	case AC97_ID_AD1886A:	/* AD1886A - !!verify!! --jk */
 	case AC97_ID_AD1887:	/* AD1887 - !!verify!! --jk */
 		if (reg == 0x5a)
 			return 1;
diff --git a/sound/pci/ac97/ac97_id.h b/sound/pci/ac97/ac97_id.h
index 03bd1ad299c0..b5b4dc1d9181 100644
--- a/sound/pci/ac97/ac97_id.h
+++ b/sound/pci/ac97/ac97_id.h
@@ -30,6 +30,7 @@
 #define AC97_ID_AD1885		0x41445360
 #define AC97_ID_AD1886		0x41445361
 #define AC97_ID_AD1887		0x41445362
+#define AC97_ID_AD1886A		0x41445363
 #define AC97_ID_TR28028		0x54524108
 #define AC97_ID_STAC9700	0x83847600
 #define AC97_ID_STAC9704	0x83847604
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index ec6e16ac2331..fea1ac014565 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -629,6 +629,9 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
 	cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO);
 	int count;
 	unsigned short result;
+	// FIXME: volatile is necessary in the following due to a bug of
+	// some gcc versions
+	volatile int ac97_num = ((volatile ac97_t *)ac97)->num;
 
 	/*
 	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
@@ -639,7 +642,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
 	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
 	 */
 
-	snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSDA2 : BA0_ACSDA);
+	snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
 
 	/*
 	 *  Setup the AC97 control registers on the CS461x to send the
@@ -658,7 +661,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
 	snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0);
 	snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW |
 					    BA0_ACCTL_VFRM | BA0_ACCTL_ESYN |
-			   (ac97->num ? BA0_ACCTL_TC : 0));
+			   (ac97_num ? BA0_ACCTL_TC : 0));
 
 
 	/*
@@ -691,7 +694,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
 		 *  ACSTS = Status Register = 464h
 		 *  VSTS - Valid Status
 		 */
-		if (snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS)
+		if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS)
 			goto __ok2;
 		udelay(10);
 	}
@@ -705,7 +708,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
 	 *  Read the data returned from the AC97 register.
 	 *  ACSDA = Status Data Register = 474h
 	 */
-	result = snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSDA2 : BA0_ACSDA);
+	result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
 
       __end:
 	return result;
@@ -2107,7 +2110,8 @@ static void cs4281_suspend(cs4281_t *chip)
 
 	/* remember the status registers */
 	for (i = 0; number_of(saved_regs); i++)
-		chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]);
+		if (saved_regs[i])
+			chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]);
 
 	/* Turn off the serial ports. */
 	snd_cs4281_pokeBA0(chip, BA0_SERMC, 0);
@@ -2150,7 +2154,8 @@ static void cs4281_resume(cs4281_t *chip)
 
 	/* restore the status registers */
 	for (i = 0; number_of(saved_regs); i++)
-		snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]);
+		if (saved_regs[i])
+			snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]);
 
 	if (chip->ac97)
 		snd_ac97_resume(chip->ac97);
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 34a69403ae6e..466af9f8673f 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -3239,6 +3239,9 @@ int __devinit snd_cs46xx_create(snd_card_t * card,
 	if (chip == NULL)
 		return -ENOMEM;
 	spin_lock_init(&chip->reg_lock);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	init_MUTEX(&chip->spos_mutex);
+#endif
 	chip->card = card;
 	chip->pci = pci;
 	chip->capt.hw_size = PAGE_SIZE;
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index d78f1013ac78..6d248bc04b71 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1298,6 +1298,7 @@ static struct {
 	{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
 	{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
 	{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_CT5880_A },
+	{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 },
 	{ .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
 };
 
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 2951e21beacc..575bc86440aa 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1,5 +1,5 @@
 /*
- *  Driver for ESS Solo-1 (ES1938, ES1946) soundcard
+ *  Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard
  *  Copyright (c) by Jaromir Koutek <miri@punknet.cz>,
  *                   Jaroslav Kysela <perex@suse.cz>,
  *                   Thomas Sailer <sailer@ife.ee.ethz.ch>,
@@ -70,6 +70,8 @@ MODULE_DESCRIPTION("ESS Solo-1");
 MODULE_LICENSE("GPL");
 MODULE_CLASSES("{sound}");
 MODULE_DEVICES("{{ESS,ES1938},"
+                "{ESS,ES1946},"
+                "{ESS,ES1969},"
 		"{TerraTec,128i PCI}}");
 
 #ifndef PCI_VENDOR_ID_ESS
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index aba395239124..75924fb9947f 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -2501,11 +2501,13 @@ static int snd_es1968_set_power_state(snd_card_t *card, unsigned int power_state
 
 static int snd_es1968_free(es1968_t *chip)
 {
+	if (chip->res_io_port)
+		snd_es1968_reset(chip);
+
 	snd_es1968_set_acpi(chip, ACPI_D3);
 	chip->master_switch = NULL;
 	chip->master_volume = NULL;
 	if (chip->res_io_port) {
-		snd_es1968_reset(chip);
 		release_resource(chip->res_io_port);
 		kfree_nocheck(chip->res_io_port);
 	}
diff --git a/sound/pci/ice1712.c b/sound/pci/ice1712.c
index cbd11ca6616c..b7499a05889f 100644
--- a/sound/pci/ice1712.c
+++ b/sound/pci/ice1712.c
@@ -414,7 +414,8 @@ MODULE_PARM_SYNTAX(snd_omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
 #define ICE1712_6FIRE_TX2		0x40	/* MIDI2 */
 #define ICE1712_6FIRE_RX2		0x80	/* MIDI2 */
 
-#define ICE1712_6FIRE_CS8427_ADDR	(0x22>>1) /* ?? */
+#define ICE1712_6FIRE_PCF9554_ADDR	(0x40>>1)
+#define ICE1712_6FIRE_CS8427_ADDR	(0x22>>1)
 
 /*
  * DMA mode values
@@ -509,7 +510,7 @@ struct _snd_ice1712 {
 	snd_i2c_device_t *cs8404;	/* CS8404A I2C device */
 	snd_i2c_device_t *cs8427;	/* CS8427 I2C device */
 	snd_i2c_device_t *pcf8574[2];	/* PCF8574 Output/Input (EWS88MT) */
-	snd_i2c_device_t *pcf8575;	/* PCF8575 (EWS88D) */
+	snd_i2c_device_t *pcf8575;	/* PCF8575 (EWS88D) / PCF9554 (6Fire) */
 	
 	unsigned char cs8403_spdif_bits;
 	unsigned char cs8403_spdif_stream_bits;
@@ -2411,14 +2412,13 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
 		ac97.read = snd_ice1712_ac97_read;
 		ac97.private_data = ice;
 		ac97.private_free = snd_ice1712_mixer_free_ac97;
-		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0)
 			printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
-			// return err;
-		} else {
+		else {
 			if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
 				return err;
+			return 0;
 		}
-		return 0;
 	}
 	/* hmm.. can we have both consumer and pro ac97 mixers? */
 	if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) {
@@ -2428,11 +2428,10 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
 		ac97.read = snd_ice1712_pro_ac97_read;
 		ac97.private_data = ice;
 		ac97.private_free = snd_ice1712_mixer_free_ac97;
-		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0)
 			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
-			// return err;
-		}
-		return 0;
+		else
+			return 0;
 	}
 	/* I2S mixer only */
 	strcat(ice->card->mixername, "ICE1712 - multitrack");
@@ -3111,7 +3110,7 @@ static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = {
 
 static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){
 
-	static char *texts[4] = {
+	static char *texts[2] = {
 		"+4dBu", "-10dBV",
 	};
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -3354,26 +3353,32 @@ static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = {
  * DMX 6Fire controls
  */
 
-#if 0 // XXX not working yet
-static int snd_ice1712_6fire_read_pca(ice1712_t *ice)
+#define PCF9554_REG_INPUT	0
+#define PCF9554_REG_OUTPUT	1
+#define PCF9554_REG_POLARITY	2
+#define PCF9554_REG_CONFIG	3
+
+static int snd_ice1712_6fire_read_pca(ice1712_t *ice, unsigned char reg)
 {
 	unsigned char byte;
 	snd_i2c_lock(ice->i2c);
-	byte = 0; /* read port */
+	byte = reg;
 	snd_i2c_sendbytes(ice->pcf8575, &byte, 1);
+	byte = 0;
 	if (snd_i2c_readbytes(ice->pcf8575, &byte, 1) != 1) {
 		snd_i2c_unlock(ice->i2c);
+		printk("cannot read pca\n");
 		return -EIO;
 	}
 	snd_i2c_unlock(ice->i2c);
 	return byte;
 }
 
-static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char data)
+static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char reg, unsigned char data)
 {
 	unsigned char bytes[2];
 	snd_i2c_lock(ice->i2c);
-	bytes[0] = 1; /* write port */
+	bytes[0] = reg;
 	bytes[1] = data;
 	if (snd_i2c_sendbytes(ice->pcf8575, bytes, 2) != 2) {
 		snd_i2c_unlock(ice->i2c);
@@ -3399,7 +3404,7 @@ static int snd_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_
 	int invert = (kcontrol->private_value >> 8) & 1;
 	int data;
 	
-	if ((data = snd_ice1712_6fire_read_pca(ice)) < 0)
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
 		return data;
 	data = (data >> shift) & 1;
 	if (invert)
@@ -3415,7 +3420,7 @@ static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_
 	int invert = (kcontrol->private_value >> 8) & 1;
 	int data, ndata;
 	
-	if ((data = snd_ice1712_6fire_read_pca(ice)) < 0)
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
 		return data;
 	ndata = data & ~(1 << shift);
 	if (ucontrol->value.integer.value[0])
@@ -3423,27 +3428,77 @@ static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_
 	if (invert)
 		ndata ^= (1 << shift);
 	if (data != ndata) {
-		snd_ice1712_6fire_write_pca(ice, (unsigned char)ndata);
+		snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata);
 		return 1;
 	}
 	return 0;
 }
 
-#define DMX6FIRE_CONTROL(xiface, xname, xshift, xinvert, xaccess) \
-{ .iface = xiface,\
+static int snd_ice1712_6fire_select_input_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[4] = {
+		"Internal", "Front Input", "Rear Input", "Wave Table"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item >= 4)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+     
+static int snd_ice1712_6fire_select_input_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int data;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+		return data;
+	ucontrol->value.integer.value[0] = data & 3;
+	return 0;
+}
+
+static int snd_ice1712_6fire_select_input_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int data, ndata;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+		return data;
+	ndata = data & ~3;
+	ndata |= (ucontrol->value.integer.value[0] & 3);
+	if (data != ndata) {
+		snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata);
+		return 1;
+	}
+	return 0;
+}
+
+
+#define DMX6FIRE_CONTROL(xname, xshift, xinvert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
   .name = xname,\
-  .access = xaccess,\
   .info = snd_ice1712_6fire_control_info,\
   .get = snd_ice1712_6fire_control_get,\
   .put = snd_ice1712_6fire_control_put,\
   .private_value = xshift | (xinvert << 8),\
 }
 
-static snd_kcontrol_new_t snd_ice1712_6fire_led __devinitdata =
-DMX6FIRE_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Breakbox LED", 6, 0, 0);
-
-#endif // XXX not working yet
-
+static snd_kcontrol_new_t snd_ice1712_6fire_controls[] __devinitdata = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Analog Input Select",
+		.info = snd_ice1712_6fire_select_input_info,
+		.get = snd_ice1712_6fire_select_input_get,
+		.put = snd_ice1712_6fire_select_input_put,
+	},
+	DMX6FIRE_CONTROL("Front Digital Input Switch", 2, 0),
+	// DMX6FIRE_CONTROL("Master Clock Select", 3, 0),
+	DMX6FIRE_CONTROL("Optical Digital Input Switch", 4, 0),
+	DMX6FIRE_CONTROL("Phono Analog Input Switch", 5, 0),
+	DMX6FIRE_CONTROL("Breakbox LED", 6, 0),
+};
 
 /*
  *
@@ -3808,14 +3863,16 @@ static int __devinit snd_ice1712_chip_init(ice1712_t *ice)
 			}
 			break;
 		case ICE1712_SUBDEVICE_DMX6FIRE:
-#if 0 // XXX not working yet
-			if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", 0x40>>1, &ice->pcf8575)) < 0)
+			if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->pcf8575)) < 0) {
+				snd_printk("PCF9554 initialization failed\n");
 				return err;
-			if ((err = snd_cs8427_create(ice->i2c, 0x11, &ice->cs8427)) < 0) {
+			}
+#if 0 // XXX not working...
+			if ((err = snd_cs8427_create(ice->i2c, ICE1712_6FIRE_CS8427_ADDR, &ice->cs8427)) < 0) {
 				snd_printk("CS8427 initialization failed\n");
 				return err;
 			}
-#endif // XXX not working yet
+#endif
 			break;
 		case ICE1712_SUBDEVICE_EWS88MT:
 			if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0)
@@ -4052,11 +4109,11 @@ static int __init snd_ice1712_build_controls(ice1712_t *ice)
 		}
 		break;
 	case ICE1712_SUBDEVICE_DMX6FIRE:
-#if 0 // XXX not working yet
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_led, ice));
-		if (err < 0)
-			return err;
-#endif
+		for (idx = 0; idx < sizeof(snd_ice1712_6fire_controls)/sizeof(snd_ice1712_6fire_controls[0]); idx++) {
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_controls[idx], ice));
+			if (err < 0)
+				return err;
+		}
 		break;
 	}
 
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 54c4ce5021d2..25592dba6bc2 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -326,10 +326,6 @@ struct _snd_intel8x0 {
 	char ac97_name[32];
 	char ctrl_name[32];
 
-	unsigned long dma_playback_size;
-	unsigned long dma_capture_size;
-	unsigned long dma_mic_size;
-
 	int irq;
 
 	unsigned int mmio;
@@ -428,7 +424,7 @@ static void iputbyte(intel8x0_t *chip, u32 offset, u8 val)
 	if (chip->bm_mmio)
 		writeb(val, chip->remap_bmaddr + offset);
 	else
-		return outb(val, chip->bmaddr + offset);
+		outb(val, chip->bmaddr + offset);
 }
 
 static void iputword(intel8x0_t *chip, u32 offset, u16 val)
@@ -436,7 +432,7 @@ static void iputword(intel8x0_t *chip, u32 offset, u16 val)
 	if (chip->bm_mmio)
 		writew(val, chip->remap_bmaddr + offset);
 	else
-		return outw(val, chip->bmaddr + offset);
+		outw(val, chip->bmaddr + offset);
 }
 
 static void iputdword(intel8x0_t *chip, u32 offset, u32 val)
@@ -444,7 +440,7 @@ static void iputdword(intel8x0_t *chip, u32 offset, u32 val)
 	if (chip->bm_mmio)
 		writel(val, chip->remap_bmaddr + offset);
 	else
-		return outl(val, chip->bmaddr + offset);
+		outl(val, chip->bmaddr + offset);
 }
 
 /*
@@ -464,7 +460,7 @@ static void iaputword(intel8x0_t *chip, u32 offset, u16 val)
 	if (chip->mmio)
 		writew(val, chip->remap_addr + offset);
 	else
-		return outw(val, chip->addr + offset);
+		outw(val, chip->addr + offset);
 }
 
 /*
@@ -1583,6 +1579,7 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
 	if (codecs < 2)
 		goto __skip_secondary;
 	for (i = 1; i < codecs; i++) {
+		ac97.num = i;
 		if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0)
 			return err;
 		chip->ac97[i] = x97;
@@ -1995,7 +1992,13 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
 	snd_intel8x0_setup_periods(chip, ichdev);
 	port = ichdev->reg_offset;
 	spin_lock_irqsave(&chip->reg_lock, flags);
-	iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM); /* trigger */
+	/* trigger */
+	if (chip->device_type != DEVICE_ALI)
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
+	else {
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+	}
 	do_gettimeofday(&start_time);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 #if 0
@@ -2014,7 +2017,10 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
 		pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
 	pos += ichdev->position;
 	do_gettimeofday(&stop_time);
-	iputbyte(chip, port + ICH_REG_OFF_CR, 0); /* stop */
+	/* stop */
+	if (chip->device_type == DEVICE_ALI)
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8));
+	iputbyte(chip, port + ICH_REG_OFF_CR, 0);
 	/* reset whole DMA things */
 	while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
 		;
@@ -2279,6 +2285,7 @@ static struct shortname_table {
 	{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" },
 	{ 0x746d, "AMD AMD8111" },
 	{ 0x7445, "AMD AMD768" },
+	{ 0x5455, "ALi M5455" },
 	{ 0, 0 },
 };
 
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 7b2d860a992b..5a0c9443e361 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1517,10 +1517,8 @@ static int
 snd_rme32_info_inputtype_control(snd_kcontrol_t * kcontrol,
 				 snd_ctl_elem_info_t * uinfo)
 {
-	static char *_texts[5] =
-	    { "Optical", "Coaxial", "Internal", "XLR" };
 	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
-	char *texts[4] = { _texts[0], _texts[1], _texts[2], _texts[3] };
+	static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" };
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
@@ -1614,8 +1612,8 @@ snd_rme32_info_clockmode_control(snd_kcontrol_t * kcontrol,
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
 	uinfo->value.enumerated.items = 4;
-	if (uinfo->value.enumerated.item > 4) {
-		uinfo->value.enumerated.item = 4;
+	if (uinfo->value.enumerated.item > 3) {
+		uinfo->value.enumerated.item = 3;
 	}
 	strcpy(uinfo->value.enumerated.name,
 	       texts[uinfo->value.enumerated.item]);
diff --git a/sound/pci/via686.c b/sound/pci/via686.c
deleted file mode 100644
index 19599db855e8..000000000000
--- a/sound/pci/via686.c
+++ /dev/null
@@ -1,1232 +0,0 @@
-/*
- *   ALSA driver for VIA VT82C686A (South Bridge)
- *
- *	Copyright (c) 2000 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 <asm/io.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_sgbuf.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/ac97_codec.h>
-#include <sound/mpu401.h>
-#define SNDRV_GET_ID
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("VIA VT82C686A");
-MODULE_LICENSE("GPL");
-MODULE_CLASSES("{sound}");
-MODULE_DEVICES("{{VIA,VT82C686A,pci},{VIA,VT82C686B}}");
-
-static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
-static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
-static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
-static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
-static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
-
-MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_index, "Index value for VIA 82C686A bridge.");
-MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
-MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
-MODULE_PARM_DESC(snd_id, "ID string for VIA 82C686A bridge.");
-MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
-MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82C686A bridge.");
-MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
-MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
-MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port.");
-MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC);
-MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
-MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
-MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
-
-/*
- *  Direct registers
- */
-
-#ifndef PCI_DEVICE_ID_VIA_82C686_5
-#define PCI_DEVICE_ID_VIA_82C686_5	0x3058
-#endif
-
-#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
-
-/* offsets */
-#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
-#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
-#define   VIA_REG_STAT_PAUSED		0x40	/* RO */
-#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
-#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
-#define   VIA_REG_STAT_EOL		0x02	/* RWC */
-#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
-#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
-#define   VIA_REG_CTRL_START		0x80	/* WO */
-#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
-#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
-#define   VIA_REG_CTRL_RESET		0x01	/* RW - probably reset? undocumented */
-#define VIA_REG_OFFSET_TYPE		0x02	/* byte - channel type */
-#define   VIA_REG_TYPE_AUTOSTART	0x80	/* RW - autostart at EOL */
-#define   VIA_REG_TYPE_16BIT		0x20	/* RW */
-#define   VIA_REG_TYPE_STEREO		0x10	/* RW */
-#define   VIA_REG_TYPE_INT_LLINE	0x00
-#define   VIA_REG_TYPE_INT_LSAMPLE	0x04
-#define   VIA_REG_TYPE_INT_LESSONE	0x08
-#define   VIA_REG_TYPE_INT_MASK		0x0c
-#define   VIA_REG_TYPE_INT_EOL		0x02
-#define   VIA_REG_TYPE_INT_FLAG		0x01
-#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
-#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count */
-/* playback block */
-#define VIA_REG_PLAYBACK_STATUS		0x00	/* byte - channel status */
-#define VIA_REG_PLAYBACK_CONTROL	0x01	/* byte - channel control */
-#define VIA_REG_PLAYBACK_TYPE		0x02	/* byte - channel type */
-#define VIA_REG_PLAYBACK_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_PLAYBACK_CURR_PTR	0x04	/* dword - channel current pointer */
-#define VIA_REG_PLAYBACK_CURR_COUNT	0x0c	/* dword - channel current count */
-/* capture block */
-#define VIA_REG_CAPTURE_STATUS		0x10	/* byte - channel status */
-#define VIA_REG_CAPTURE_CONTROL		0x11	/* byte - channel control */
-#define VIA_REG_CAPTURE_TYPE		0x12	/* byte - channel type */
-#define VIA_REG_CAPTURE_TABLE_PTR	0x14	/* dword - channel table pointer */
-#define VIA_REG_CAPTURE_CURR_PTR	0x14	/* dword - channel current pointer */
-#define VIA_REG_CAPTURE_CURR_COUNT	0x1c	/* dword - channel current count */
-/* FM block */
-#define VIA_REG_FM_STATUS		0x20	/* byte - channel status */
-#define VIA_REG_FM_CONTROL		0x21	/* byte - channel control */
-#define VIA_REG_FM_TYPE			0x22	/* byte - channel type */
-#define VIA_REG_FM_TABLE_PTR		0x24	/* dword - channel table pointer */
-#define VIA_REG_FM_CURR_PTR		0x24	/* dword - channel current pointer */
-#define VIA_REG_FM_CURR_COUNT		0x2c	/* dword - channel current count */
-/* AC'97 */
-#define VIA_REG_AC97			0x80	/* dword */
-#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
-#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
-#define   VIA_REG_AC97_CODEC_ID_PRIMARY	0x00
-#define   VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
-#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
-#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
-#define   VIA_REG_AC97_BUSY		(1<<24)
-#define   VIA_REG_AC97_READ		(1<<23)
-#define   VIA_REG_AC97_CMD_SHIFT	16
-#define   VIA_REG_AC97_CMD_MASK		0x7e
-#define   VIA_REG_AC97_DATA_SHIFT	0
-#define   VIA_REG_AC97_DATA_MASK	0xffff
-#define VIA_REG_SGD_SHADOW		0x84	/* dword */
-
-#define VIA_TBL_BIT_FLAG	0x40000000
-#define VIA_TBL_BIT_EOL		0x80000000
-
-/*
- * pcm stream
- */
-
-typedef struct {
-	unsigned long reg_offset;
-        snd_pcm_substream_t *substream;
-	int running;
-        unsigned int size;
-        unsigned int fragsize;
-	unsigned int frags;
-	unsigned int lastptr;
-	unsigned int lastcount;
-	unsigned int page_per_frag;
-	unsigned int curidx;
-	unsigned int tbl_entries;	/* number of descriptor table entries */
-	unsigned int tbl_size;		/* size of a table entry */
-	u32 *table; /* physical address + flag */
-	dma_addr_t table_addr;
-} viadev_t;
-
-
-static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			   struct pci_dev *pci)
-{
-	int i, size;
-	struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
-
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-
-	/* allocate buffer descriptor lists */
-	if (dev->fragsize < PAGE_SIZE) {
-		dev->tbl_size = dev->fragsize;
-		dev->tbl_entries = dev->frags;
-		dev->page_per_frag = 1;
-	} else {
-		dev->tbl_size = PAGE_SIZE;
-		dev->tbl_entries = sgbuf->pages;
-		dev->page_per_frag = dev->fragsize >> PAGE_SHIFT;
-	}
-	/* the start of each lists must be aligned to 8 bytes,
-	 * but the kernel pages are much bigger, so we don't care
-	 */
-	dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr);
-	if (! dev->table)
-		return -ENOMEM;
-
-	if (dev->tbl_size < PAGE_SIZE) {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i);
-	} else {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr);
-	}
-	size = dev->size;
-	for (i = 0; i < dev->tbl_entries - 1; i++) {
-		dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size);
-		size -= dev->tbl_size;
-	}
-	dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size);
-
-	return 0;
-}
-
-
-static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			    struct pci_dev *pci)
-{
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-}
-
-
-/*
- */
-
-typedef struct _snd_via686a via686a_t;
-#define chip_t via686a_t
-
-struct _snd_via686a {
-	int irq;
-
-	unsigned long port;
-	struct resource *res_port;
-	unsigned char revision;
-
-	unsigned char old_legacy;
-	unsigned char old_legacy_cfg;
-
-	struct pci_dev *pci;
-	snd_card_t *card;
-
-	snd_pcm_t *pcm;
-	/*snd_pcm_t *pcm_fm;*/
-	viadev_t playback;
-	viadev_t capture;
-	/*viadev_t playback_fm;*/
-
-	snd_rawmidi_t *rmidi;
-
-	ac97_t *ac97;
-	unsigned int ac97_clock;
-	unsigned int ac97_secondary;	/* secondary AC'97 codec is present */
-
-	spinlock_t reg_lock;
-	snd_info_entry_t *proc_entry;
-};
-
-static struct pci_device_id snd_via686a_ids[] __devinitdata = {
-	{ 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* 686A */
-	{ 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, snd_via686a_ids);
-
-/*
- *  Basic I/O
- */
-
-static inline unsigned int snd_via686a_codec_xread(via686a_t *chip)
-{
-	return inl(VIAREG(chip, AC97));
-}
- 
-static inline void snd_via686a_codec_xwrite(via686a_t *chip, unsigned int val)
-{
-	outl(val, VIAREG(chip, AC97));
-}
- 
-static int snd_via686a_codec_ready(via686a_t *chip, int secondary)
-{
-	unsigned int timeout = 1000;	/* 1ms */
-	unsigned int val;
-	
-	while (timeout-- > 0) {
-		udelay(1);
-		if (!((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY))
-			return val & 0xffff;
-	}
-	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via686a_codec_xread(chip));
-	return -EIO;
-}
- 
-static int snd_via686a_codec_valid(via686a_t *chip, int secondary)
-{
-	unsigned int timeout = 1000;	/* 1ms */
-	unsigned int val;
-	unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
-					 VIA_REG_AC97_SECONDARY_VALID;
-	
-	while (timeout-- > 0) {
-		udelay(1);
-		if ((val = snd_via686a_codec_xread(chip)) & stat)
-			return val & 0xffff;
-	}
-	snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via686a_codec_xread(chip));
-	return -EIO;
-}
- 
-static void snd_via686a_codec_wait(ac97_t *ac97)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-	int err;
-	err = snd_via686a_codec_ready(chip, ac97->num);
-	/* here we need to wait fairly for long time.. */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ/2);
-}
-
-static void snd_via686a_codec_write(ac97_t *ac97,
-				    unsigned short reg,
-				    unsigned short val)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-	unsigned int xval;
-	
-	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
-	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
-	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
-	xval |= val << VIA_REG_AC97_DATA_SHIFT;
-	spin_lock(&chip->reg_lock);
-	snd_via686a_codec_xwrite(chip, xval);
-	snd_via686a_codec_ready(chip, ac97->num);
-	spin_unlock(&chip->reg_lock);
-}
-
-static unsigned short snd_via686a_codec_read(ac97_t *ac97, unsigned short reg)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return ~0);
-	unsigned int xval, val = 0xffff;
-	int again = 0;
-
-	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
-	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
-	xval = (!ac97->num ? VIA_REG_AC97_PRIMARY_VALID : VIA_REG_AC97_SECONDARY_VALID);
-	xval |= VIA_REG_AC97_READ;
-	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
-	spin_lock(&chip->reg_lock);
-      	while (1) {
-      		if (again++ > 3) {
-		        spin_unlock(&chip->reg_lock);
-		      	return 0xffff;
-		}
-		snd_via686a_codec_xwrite(chip, xval);
-		if (snd_via686a_codec_ready(chip, ac97->num) < 0)
-			continue;
-		if (snd_via686a_codec_valid(chip, ac97->num) >= 0) {
-			udelay(25);
-			val = snd_via686a_codec_xread(chip);
-			break;
-		}
-	}
-	spin_unlock(&chip->reg_lock);
-	return val & 0xffff;
-}
-
-#if 0
-static void snd_via686a_channel_print(via686a_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n",
-			port,
-			inb(port + VIA_REG_OFFSET_STATUS),
-			inb(port + VIA_REG_OFFSET_CONTROL),
-			inb(port + VIA_REG_OFFSET_TYPE),
-			inl(port + VIA_REG_OFFSET_CURR_PTR),
-			inl(port + VIA_REG_OFFSET_CURR_COUNT));
-}
-#endif
-
-static void snd_via686a_channel_reset(via686a_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL);
-	udelay(50);
-	outb(0x00, port + VIA_REG_OFFSET_CONTROL);
-	outb(0xff, port + VIA_REG_OFFSET_STATUS);
-	outb(0x00, port + VIA_REG_OFFSET_TYPE);
-	outl(0, port + VIA_REG_OFFSET_CURR_PTR);
-}
-
-static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd)
-{
-	unsigned char val = 0;
-	unsigned long port = chip->port + viadev->reg_offset;
-	
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		val = VIA_REG_CTRL_START;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-		val = VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET;
-		viadev->running = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		val = VIA_REG_CTRL_PAUSE;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		val = 0;
-		viadev->running = 0;
-		break;
-	default:
-		return -EINVAL;
-	}
-	outb(val, port + VIA_REG_OFFSET_CONTROL);
-	if (cmd == SNDRV_PCM_TRIGGER_STOP)
-		snd_via686a_channel_reset(chip, viadev);
-	return 0;
-}
-
-
-static int snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev,
-				      snd_pcm_substream_t *substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	unsigned long port = chip->port + viadev->reg_offset;
-	int v, err;
-
-	viadev->size = snd_pcm_lib_buffer_bytes(substream);
-	viadev->fragsize = snd_pcm_lib_period_bytes(substream);
-	viadev->frags = runtime->periods;
-	viadev->lastptr = ~0;
-	viadev->lastcount = ~0;
-	viadev->curidx = 0;
-
-	/* the period size must be in power of 2 */
-	v = ld2(viadev->fragsize);
-	if (viadev->fragsize != (1 << v)) {
-		snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize);
-		return -EINVAL;
-	}
-
-	snd_via686a_channel_reset(chip, viadev);
-
-	err = build_via_table(viadev, substream, chip->pci);
-	if (err < 0)
-		return err;
-
-	runtime->dma_bytes = viadev->size;
-	outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
-	outb(VIA_REG_TYPE_AUTOSTART |
-	     (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
-	     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
-	     ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) |
-	     VIA_REG_TYPE_INT_EOL |
-	     VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE);
-	return 0;
-}
-
-/*
- *  Interrupt handler
- */
-
-static inline void snd_via686a_update(via686a_t *chip, viadev_t *viadev)
-{
-	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
-	if (viadev->substream && viadev->running) {
-		viadev->curidx++;
-		if (viadev->curidx >= viadev->page_per_frag) {
-			viadev->curidx = 0;
-			spin_unlock(&chip->reg_lock);
-			snd_pcm_period_elapsed(viadev->substream);
-			spin_lock(&chip->reg_lock);
-		}
-	}
-}
-
-static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, dev_id, return);
-	unsigned int status;
-
-	spin_lock(&chip->reg_lock);
-	status = inl(VIAREG(chip, SGD_SHADOW));
-	if ((status & 0x00000077) == 0) {
-		spin_unlock(&chip->reg_lock);
-		if (chip->rmidi != NULL) {
-			snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
-		}
-		return;
-	}
-	if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via686a_update(chip, &chip->playback);
-	if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via686a_update(chip, &chip->capture);
-	/*if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-	  snd_via686a_update(chip, &chip->playback_fm);*/
-	spin_unlock(&chip->reg_lock);
-}
-
-/*
- *  PCM part
- */
-
-static int snd_via686a_playback_trigger(snd_pcm_substream_t * substream,
-					int cmd)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	return snd_via686a_trigger(chip, &chip->playback, cmd);
-}
-
-static int snd_via686a_capture_trigger(snd_pcm_substream_t * substream,
-				       int cmd)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	return snd_via686a_trigger(chip, &chip->capture, cmd);
-}
-
-static int snd_via686a_hw_params(snd_pcm_substream_t * substream,
-				 snd_pcm_hw_params_t * hw_params)
-{
-	return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_via686a_hw_free(snd_pcm_substream_t * substream)
-{
-	return 0;
-}
-
-static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-	return snd_via686a_setup_periods(chip, &chip->playback, substream);
-}
-
-static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-	return snd_via686a_setup_periods(chip, &chip->capture, substream);
-}
-
-static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev)
-{
-	unsigned int val, ptr, count;
-	
-	ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset);
-	count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset);
-	if (ptr == viadev->lastptr && count > viadev->lastcount)
-		ptr += 8;
-	if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE))
-		return 0;
-	snd_assert(viadev->tbl_entries, return 0);
-	/* get index */
-	if (ptr <= (unsigned int)viadev->table_addr)
-		val = 0;
-	else
-		val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries;
-	if (val < viadev->tbl_entries - 1) {
-		val *= viadev->tbl_size;
-		val += viadev->tbl_size - count;
-	} else {
-		val *= viadev->tbl_size;
-		val += (viadev->size % viadev->tbl_size) + 1 - count;
-	}
-	viadev->lastptr = ptr;
-	viadev->lastcount = count;
-	// printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, count, val);
-	return val;
-}
-
-static snd_pcm_uframes_t snd_via686a_playback_pointer(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback));
-}
-
-static snd_pcm_uframes_t snd_via686a_capture_pointer(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->capture));
-}
-
-static snd_pcm_hardware_t snd_via686a_playback =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	.rates =		0,
-	.rate_min =		8000,
-	.rate_max =		48000,
-	.channels_min =		1,
-	.channels_max =		2,
-	.buffer_bytes_max =	128 * 1024,
-	.period_bytes_min =	32,
-	.period_bytes_max =	128 * 1024,
-	.periods_min =		2,
-	.periods_max =		128,
-	.fifo_size =		0,
-};
-
-static snd_pcm_hardware_t snd_via686a_capture =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID),
-	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	.rates =		0,
-	.rate_min =		8000,
-	.rate_max =		48000,
-	.channels_min =		1,
-	.channels_max =		2,
-	.buffer_bytes_max =	128 * 1024,
-	.period_bytes_min =	32,
-	.period_bytes_max =	128 * 1024,
-	.periods_min =		2,
-	.periods_max =		128,
-	.fifo_size =		0,
-};
-
-static int snd_via686a_playback_open(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->playback.substream = substream;
-	runtime->hw = snd_via686a_playback;
-	runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-	/* we may remove following constaint when we modify table entries
-	   in interrupt */
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-	return 0;
-}
-
-static int snd_via686a_capture_open(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->capture.substream = substream;
-	runtime->hw = snd_via686a_capture;
-	runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-	return 0;
-}
-
-static int snd_via686a_playback_close(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	clean_via_table(&chip->playback, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->playback.substream = NULL;
-	return 0;
-}
-
-static int snd_via686a_capture_close(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	clean_via_table(&chip->capture, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->capture.substream = NULL;
-	return 0;
-}
-
-static snd_pcm_ops_t snd_via686a_playback_ops = {
-	.open =		snd_via686a_playback_open,
-	.close =	snd_via686a_playback_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_via686a_hw_params,
-	.hw_free =	snd_via686a_hw_free,
-	.prepare =	snd_via686a_playback_prepare,
-	.trigger =	snd_via686a_playback_trigger,
-	.pointer =	snd_via686a_playback_pointer,
-	.copy =		snd_pcm_sgbuf_ops_copy_playback,
-	.silence =	snd_pcm_sgbuf_ops_silence,
-	.page =		snd_pcm_sgbuf_ops_page,
-};
-
-static snd_pcm_ops_t snd_via686a_capture_ops = {
-	.open =		snd_via686a_capture_open,
-	.close =	snd_via686a_capture_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_via686a_hw_params,
-	.hw_free =	snd_via686a_hw_free,
-	.prepare =	snd_via686a_capture_prepare,
-	.trigger =	snd_via686a_capture_trigger,
-	.pointer =	snd_via686a_capture_pointer,
-	.copy =		snd_pcm_sgbuf_ops_copy_capture,
-	.silence =	snd_pcm_sgbuf_ops_silence,
-	.page =		snd_pcm_sgbuf_ops_page,
-};
-
-static void snd_via686a_pcm_free(snd_pcm_t *pcm)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, pcm->private_data, return);
-	chip->pcm = NULL;
-}
-
-static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** rpcm)
-{
-	snd_pcm_t *pcm;
-	int err;
-
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(chip->card, "VIA 82C686A", device, 1, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686a_capture_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_via686a_pcm_free;
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "VIA 82C686A");
-	chip->pcm = pcm;
-
-	if (rpcm)
-		*rpcm = NULL;
-	return 0;
-}
-
-
-/*
- *  Mixer part
- */
-
-static void snd_via686a_mixer_free_ac97(ac97_t *ac97)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-	chip->ac97 = NULL;
-}
-
-static int __devinit snd_via686a_mixer(via686a_t *chip)
-{
-	ac97_t ac97;
-	int err;
-
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.write = snd_via686a_codec_write;
-	ac97.read = snd_via686a_codec_read;
-	ac97.wait = snd_via686a_codec_wait;
-	ac97.private_data = chip;
-	ac97.private_free = snd_via686a_mixer_free_ac97;
-	ac97.clock = chip->ac97_clock;
-	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
-		return err;
-	return 0;
-}
-
-/*
- * joystick
- */
-
-static int snd_via686a_joystick_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_via686a_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	via686a_t *chip = snd_kcontrol_chip(kcontrol);
-	u16 val;
-
-	pci_read_config_word(chip->pci, 0x42, &val);
-	ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0;
-	return 0;
-}
-
-static int snd_via686a_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	via686a_t *chip = snd_kcontrol_chip(kcontrol);
-	u16 val, oval;
-
-	pci_read_config_word(chip->pci, 0x42, &oval);
-	val = oval & ~0x08;
-	if (ucontrol->value.integer.value[0])
-		val |= 0x08;
-	if (val != oval) {
-		pci_write_config_word(chip->pci, 0x42, val);
-		return 1;
-	}
-	return 0;
-}
-
-static snd_kcontrol_new_t snd_via686a_joystick_control __devinitdata = {
-	.name = "Joystick",
-	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
-	.info = snd_via686a_joystick_info,
-	.get = snd_via686a_joystick_get,
-	.put = snd_via686a_joystick_put,
-};
-
-/*
- *
- */
-
-static int __devinit snd_via686a_chip_init(via686a_t *chip)
-{
-	ac97_t ac97;
-	unsigned int val;
-	int max_count;
-	unsigned char pval;
-
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.private_data = chip;
-
-#if 0 /* broken on K7M? */
-	/* disable all legacy ports */
-	pci_write_config_byte(chip->pci, 0x42, 0);
-#endif
-
-	/* cold reset only when link is down */
-	pci_read_config_byte(chip->pci, 0x40, &pval);
-	if ((pval & 0x01) == 0) {
-		/* deassert ACLink reset, force SYNC */
-		pci_write_config_byte(chip->pci, 0x41, 0xe0);
-		udelay(100);
-		pci_write_config_byte(chip->pci, 0x41, 0x00);
-		udelay(100);
-		/* ACLink on, deassert ACLink reset, VSR, SGD data out */
-		/* note - FM data out has trouble with non VRA codecs !! */
-		pci_write_config_byte(chip->pci, 0x41, 0xcc);
-		udelay(100);
-	}
-	
-	/* Make sure VRA is enabled, in case we didn't do a
-	 * complete codec reset, above */
-	pci_read_config_byte(chip->pci, 0x41, &pval);
-	if ((pval & 0xcc) != 0xcc) {
-		/* ACLink on, deassert ACLink reset, VSR, SGD data out */
-		/* note - FM data out has trouble with non VRA codecs !! */
-		pci_write_config_byte(chip->pci, 0x41, 0xcc);
-		udelay(100);
-	}
-
-	/* wait until codec ready */
-	max_count = ((3 * HZ) / 4) + 1;
-	do {
-		pci_read_config_byte(chip->pci, 0x40, &pval);
-		if (pval & 0x01) /* primary codec ready */
-			break;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (--max_count > 0);
-
-	if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY)
-		snd_printk("AC'97 codec is not ready [0x%x]\n", val);
-
-	/* and then reset codec.. */
-	snd_via686a_codec_write(&ac97, AC97_RESET, 0x0000);
-
-	/* check the primary codec */
-	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
-				 VIA_REG_AC97_PRIMARY_VALID |
-				 (VIA_REG_AC97_CODEC_ID_PRIMARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	max_count = ((3 * HZ) / 4) + 1;
-	do {
-		if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_PRIMARY_VALID)
-			goto __ac97_ok1;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (--max_count > 0);
-	snd_printk("Primary AC'97 codec is not valid [0x%x]\n", val);
-
-      __ac97_ok1:
-#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */
-	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
-				 VIA_REG_AC97_SECONDARY_VALID |
-				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	max_count = ((3 * HZ) / 4) + 1;
-	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
-				 VIA_REG_AC97_SECONDARY_VALID |
-				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	do {
-		if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
-			chip->ac97_secondary = 1;
-			goto __ac97_ok2;
-		}
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (--max_count > 0);
-	/* This is ok, the most of motherboards have only one codec */
-
-      __ac97_ok2:
-#endif
-
-#if 0
-	{
-	unsigned char cmdb;
-
-	pci_read_config_byte(chip->pci, 0x40, &cmdb);
-	printk("PCI[0x40] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x42, &cmdb);
-	printk("PCI[0x42] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x43, &cmdb);
-	printk("PCI[0x43] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x44, &cmdb);
-	printk("PCI[0x44] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x48, &cmdb);
-	printk("PCI[0x48] = 0x%x\n", cmdb);
-	}
-#endif
-
-	/* route FM trap to IRQ, disable FM trap */
-	pci_write_config_byte(chip->pci, 0x48, 0);
-	
-	/* disable all GPI interrupts */
-	outl(0, chip->port + 0x8c);
-
-	/* disable interrupts */
-	snd_via686a_channel_reset(chip, &chip->playback);
-	snd_via686a_channel_reset(chip, &chip->capture);
-	/*snd_via686a_channel_reset(chip, &chip->playback_fm);*/
-	return 0;
-}
-
-static int snd_via686a_free(via686a_t *chip)
-{
-	if (chip->irq < 0)
-		goto __end_hw;
-	/* disable interrupts */
-	snd_via686a_channel_reset(chip, &chip->playback);
-	snd_via686a_channel_reset(chip, &chip->capture);
-	/*snd_via686a_channel_reset(chip, &chip->playback_fm);*/
-	/* --- */
-	synchronize_irq(chip->irq);
-      __end_hw:
-	if (chip->res_port) {
-		release_resource(chip->res_port);
-		kfree_nocheck(chip->res_port);
-	}
-	if (chip->irq >= 0)
-		free_irq(chip->irq, (void *)chip);
-	pci_write_config_byte(chip->pci, 0x42, chip->old_legacy);
-	pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg);
-	snd_magic_kfree(chip);
-	return 0;
-}
-
-static int snd_via686a_dev_free(snd_device_t *device)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, device->device_data, return -ENXIO);
-	return snd_via686a_free(chip);
-}
-
-static int __devinit snd_via686a_create(snd_card_t * card,
-				     struct pci_dev *pci,
-				     unsigned int ac97_clock,
-				     unsigned char old_legacy,
-				     unsigned char old_legacy_cfg,
-				     via686a_t ** r_via)
-{
-	via686a_t *chip;
-	int err;
-        static snd_device_ops_t ops = {
-		.dev_free =	snd_via686a_dev_free,
-        };
-
-	if ((err = pci_enable_device(pci)) < 0)
-		return err;
-
-	if ((chip = snd_magic_kcalloc(via686a_t, 0, GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	chip->old_legacy = old_legacy;
-	chip->old_legacy_cfg = old_legacy_cfg;
-
-	spin_lock_init(&chip->reg_lock);
-	chip->card = card;
-	chip->pci = pci;
-	chip->irq = -1;
-	chip->port = pci_resource_start(pci, 0);
-	if ((chip->res_port = request_region(chip->port, 256, "VIA686A")) == NULL) {
-		snd_via686a_free(chip);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
-		return -EBUSY;
-	}
-	if (request_irq(pci->irq, snd_via686a_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA686A", (void *)chip)) {
-		snd_via686a_free(chip);
-		snd_printk("unable to grab IRQ %d\n", chip->irq);
-		return -EBUSY;
-	}
-	chip->irq = pci->irq;
-	if (ac97_clock >= 8000 && ac97_clock <= 48000)
-		chip->ac97_clock = ac97_clock;
-	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
-	synchronize_irq(chip->irq);
-
-	/* initialize offsets */
-	chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS;
-	chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
-	/*chip->playback_fm.reg_offset = VIA_REG_FM_STATUS;*/
-
-	if ((err = snd_via686a_chip_init(chip)) < 0) {
-		snd_via686a_free(chip);
-		return err;
-	}
-
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_via686a_free(chip);
-		return err;
-	}
-
-	*r_via = chip;
-	return 0;
-}
-
-static int __devinit snd_via686a_probe(struct pci_dev *pci,
-				       const struct pci_device_id *id)
-{
-	static int dev;
-	snd_card_t *card;
-	via686a_t *chip;
-	int pcm_dev = 0;
-	unsigned char legacy;
-	unsigned char legacy_cfg;
-	int rev_h = 0, err;
-
-	if (dev >= SNDRV_CARDS)
-		return -ENODEV;
-	if (!snd_enable[dev]) {
-		dev++;
-		return -ENOENT;
-	}
-
-	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
-	if (card == NULL)
-		return -ENOMEM;
-
-	pci_read_config_byte(pci, 0x42, &legacy);
-	pci_read_config_byte(pci, 0x43, &legacy_cfg);
-
-	if ((err = snd_via686a_create(card,
-				      pci,
-				      snd_ac97_clock[dev],
-				      legacy,
-				      legacy_cfg,
-				      &chip)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-
-	if (snd_via686a_mixer(chip) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	if (snd_via686a_pcm(chip, pcm_dev++, NULL) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-#if 0
-	if (snd_via686a_pcm_fm(chip, pcm_dev++, NULL) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-#endif
-
-	legacy |= 0x40;		/* disable MIDI */
-	legacy &= ~0x08;	/* disable joystick */
-	if (chip->revision >= 0x20) {
-		if (check_region(pci_resource_start(pci, 2), 4)) {
-			rev_h = 0;
-			legacy &= ~0x80;	/* disable PCI I/O 2 */
-		} else {
-			rev_h = 1;
-			legacy |= 0x80;		/* enable PCI I/O 2 */
-		}
-	}
-	pci_write_config_byte(pci, 0x42, legacy);
-	pci_write_config_byte(pci, 0x43, legacy_cfg);
-	if (rev_h && snd_mpu_port[dev] >= 0x200) {	/* force MIDI */
-		legacy |= 0x02;	/* enable MPU */
-		pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01);
-	} else {
-		if (rev_h && (legacy & 0x02)) {
-			snd_mpu_port[dev] = pci_resource_start(pci, 2);
-			if (snd_mpu_port[dev] < 0x200)	/* bad value */
-				legacy &= ~0x02;	/* disable MIDI */
-		} else {
-			switch (snd_mpu_port[dev]) {	/* force MIDI */
-			case 0x300:
-			case 0x310:
-			case 0x320:
-			case 0x330:
-				legacy_cfg &= ~(3 << 2);
-				legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2;
-				legacy |= 0x02;
-				break;
-			default:			/* no, use BIOS settings */
-				if (legacy & 0x02)
-					snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2);
-			}
-		}
-	}
-	pci_write_config_byte(pci, 0x42, legacy);
-	pci_write_config_byte(pci, 0x43, legacy_cfg);
-	if (legacy & 0x02) {
-		if (check_region(snd_mpu_port[dev], 2)) {
-			printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]);
-			legacy &= ~0x02;
-			pci_write_config_byte(pci, 0x42, legacy);
-			goto __skip_mpu;
-		}
-		if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A,
-					snd_mpu_port[dev], 0,
-					pci->irq, 0,
-					&chip->rmidi) < 0) {
-			printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]);
-			legacy &= ~0x02;
-			pci_write_config_byte(pci, 0x42, legacy);
-			goto __skip_mpu;
-		}
-		legacy &= ~0x40;	/* enable MIDI interrupt */
-		pci_write_config_byte(pci, 0x42, legacy);
-	      __skip_mpu:
-		;
-	}
-	
-	/* card switches */
-	err = snd_ctl_add(card, snd_ctl_new1(&snd_via686a_joystick_control, chip));
-	if (err < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	strcpy(card->driver, "VIA686A");
-	strcpy(card->shortname, "VIA 82C686A/B");
-	
-	sprintf(card->longname, "%s at 0x%lx, irq %d",
-		card->shortname, chip->port, chip->irq);
-
-	if ((err = snd_card_register(card)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	pci_set_drvdata(pci, card);
-	dev++;
-	return 0;
-}
-
-static void __devexit snd_via686a_remove(struct pci_dev *pci)
-{
-	snd_card_free(pci_get_drvdata(pci));
-	pci_set_drvdata(pci, NULL);
-}
-
-static struct pci_driver driver = {
-	.name = "VIA 82C686A/B",
-	.id_table = snd_via686a_ids,
-	.probe = snd_via686a_probe,
-	.remove = __devexit_p(snd_via686a_remove),
-};
-
-static int __init alsa_card_via686a_init(void)
-{
-	int err;
-
-	if ((err = pci_module_init(&driver)) < 0) {
-#ifdef MODULE
-		printk(KERN_ERR "VIA 82C686A soundcard not found or device busy\n");
-#endif
-		return err;
-	}
-	return 0;
-}
-
-static void __exit alsa_card_via686a_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via686a_init)
-module_exit(alsa_card_via686a_exit)
-
-#ifndef MODULE
-
-/* format is: snd-via686a=snd_enable,snd_index,snd_id,
-			  snd_mpu_port,snd_ac97_clock */
-
-static int __init alsa_card_via686a_setup(char *str)
-{
-	static unsigned __initdata nr_dev = 0;
-
-	if (nr_dev >= SNDRV_CARDS)
-		return 0;
-	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
-	       get_option(&str,&snd_index[nr_dev]) == 2 &&
-	       get_id(&str,&snd_id[nr_dev]) == 2 &&
-	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
-	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
-	nr_dev++;
-	return 1;
-}
-
-__setup("snd-via686a=", alsa_card_via686a_setup);
-
-#endif /* ifndef MODULE */
diff --git a/sound/pci/via8233.c b/sound/pci/via8233.c
deleted file mode 100644
index 4f17239c2e5b..000000000000
--- a/sound/pci/via8233.c
+++ /dev/null
@@ -1,1022 +0,0 @@
-/*
- *   ALSA driver for VIA VT8233 (South Bridge)
- *
- *	Copyright (c) 2000 Tjeerd.Mulder@fujitsu-siemens.com
- *	This driver is based on VIA686 code 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 <asm/io.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_sgbuf.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/ac97_codec.h>
-#include <sound/mpu401.h>
-#define SNDRV_GET_ID
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Tjeerd.Mulder@fujitsu-siemens.com");
-MODULE_DESCRIPTION("VIA VT8233");
-MODULE_LICENSE("GPL");
-MODULE_CLASSES("{sound}");
-MODULE_DEVICES("{{VIA,VT8233,pci}}");
-
-static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
-static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
-static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
-static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
-
-MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_index, "Index value for VIA 8233 bridge.");
-MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
-MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
-MODULE_PARM_DESC(snd_id, "ID string for VIA 8233 bridge.");
-MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
-MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 8233 bridge.");
-MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
-MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
-MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
-MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
-
-
-/*
- *  Direct registers
- */
-
-#ifndef PCI_DEVICE_ID_VIA_8233_5
-#define PCI_DEVICE_ID_VIA_8233_5	0x3059
-#endif
-
-/* revision numbers */
-#define VIA_REV_PRE_8233	0x10	/* not in market */
-#define VIA_REV_8233C		0x20	/* 2 rec, 4 pb, 1 multi-pb */
-#define VIA_REV_8233		0x30	/* 2 rec, 4 pb, 1 multi-pb, spdif */
-#define VIA_REV_8233A		0x40	/* 1 rec, 1 multi-pb, spdf */
-#define VIA_REV_8235		0x50	/* 2 rec, 4 pb, 1 multi-pb, spdif */
-
-#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
-
-/* offsets */
-#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
-#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
-#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
-#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
-#define   VIA_REG_STAT_EOL		0x02	/* RWC */
-#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
-#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
-#define   VIA_REG_CTRL_START		0x80	/* WO */
-#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
-#define   VIA_REG_CTRL_AUTOSTART	0x20
-#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
-#define   VIA_REG_CTRL_INT_STOP		0x04		
-#define   VIA_REG_CTRL_INT_EOL		0x02
-#define   VIA_REG_CTRL_INT_FLAG		0x01
-#define   VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
-#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
-#define VIA_REG_OFFSET_STOP_IDX		0x08	/* dword - stop index, channel type, sample rate */
-#define   VIA_REG_TYPE_16BIT		0x00200000	/* RW */
-#define   VIA_REG_TYPE_STEREO		0x00100000	/* RW */
-#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
-#define VIA_REG_OFFSET_CURR_INDEX	0x0f	/* byte - channel current index */
-
-#define VIA_NUM_OF_DMA_CHANNELS	2
-
-/* playback block (VT8233/8233C) - channels 0-3 (0-0x3f) */
-#define VIA_REG_PLAYBACK_STATUS		0x00	/* byte - channel status */
-#define VIA_REG_PLAYBACK_CONTROL	0x01	/* byte - channel control */
-#define VIA_REG_PLAYBACK_VOLUME_L	0x02	/* byte */
-#define VIA_REG_PLAYBACK_VOLUME_R	0x03	/* byte */
-#define VIA_REG_PLAYBACK_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_PLAYBACK_CURR_PTR	0x04	/* dword - channel current pointer */
-#define VIA_REG_PLAYBACK_STOP_IDX	0x08    /* dword - stop index, channel type, sample rate */
-#define VIA_REG_PLAYBACK_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
-#define VIA_REG_PLAYBACK_CURR_INDEX	0x0f	/* byte - channel current index */
-
-/* multi-channel playback */
-#define VIA_REG_MULTPLAY_STATUS		0x40	/* byte - channel status */
-#define VIA_REG_MULTPLAY_CONTROL	0x41	/* byte - channel control */
-#define VIA_REG_MULTPLAY_FORMAT		0x42	/* byte - format and channels */
-#define   VIA_REG_MULTPLAY_FMT_8BIT	0x00
-#define   VIA_REG_MULTPLAY_FMT_16BIT	0x80
-#define   VIA_REG_MULTPLAY_FMT_CH_MASK	0x70	/* # channels << 4 (valid = 1,2,4,6) */
-#define VIA_REG_MULTPLAY_SCRATCH	0x43	/* byte - nop */
-#define VIA_REG_MULTPLAY_TABLE_PTR	0x44	/* dword - channel table pointer */
-#define VIA_REG_MULTPLAY_CURR_PTR	0x44	/* dword - channel current pointer */
-#define VIA_REG_MULTPLAY_STOP_IDX	0x48    /* dword - stop index, slots */
-#define VIA_REG_MULTPLAY_CURR_COUNT	0x4c	/* dword - channel current count (24 bit) */
-#define VIA_REG_MULTIPLAY_CURR_INDEX	0x4f	/* byte - channel current index */
-
-/* capture block */
-#define VIA_REG_CAPTURE_STATUS		0x60	/* byte - channel status */
-#define VIA_REG_CAPTURE_CONTROL		0x61	/* byte - channel control */
-#define VIA_REG_CAPTURE_FIFO		0x62	/* byte - bit 6 = fifo  enable */
-#define   VIA_REG_CAPTURE_FIFO_ENABLE	0x40
-#define VIA_REG_CAPTURE_CHANNEL		0x63	/* byte - input select */
-#define   VIA_REG_CAPTURE_CHANNEL_MIC	0x4
-#define   VIA_REG_CAPTURE_CHANNEL_LINE	0
-#define   VIA_REG_CAPTURE_SELECT_CODEC	0x03	/* recording source codec (0 = primary) */
-#define VIA_REG_CAPTURE_TABLE_PTR	0x64	/* dword - channel table pointer */
-#define VIA_REG_CAPTURE_CURR_PTR	0x64	/* dword - channel current pointer */
-#define VIA_REG_CAPTURE_STOP_IDX	0x68	/* dword - stop index */
-#define VIA_REG_CAPTURE_CURR_COUNT	0x6c	/* dword - channel current count (24 bit) */
-#define VIA_REG_CAPTURE_CURR_INDEX	0x6f	/* byte - channel current index */
-/* AC'97 */
-#define VIA_REG_AC97			0x80	/* dword */
-#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
-#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
-#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
-#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
-#define   VIA_REG_AC97_ANY_VALID	(VIA_REG_AC97_PRIMARY_VALID | VIA_REG_AC97_SECONDARY_VALID | (1<<28)| (1<<29))
-#define   VIA_REG_AC97_BUSY		(1<<24)
-#define   VIA_REG_AC97_READ		(1<<23)
-#define   VIA_REG_AC97_CMD_SHIFT	16
-#define   VIA_REG_AC97_CMD_MASK		0x7e
-#define   VIA_REG_AC97_DATA_SHIFT	0
-#define   VIA_REG_AC97_DATA_MASK	0xffff
-#define VIA_REG_SGD_SHADOW		0x84	/* dword */
-
-#define VIA_TBL_BIT_FLAG	0x40000000
-#define VIA_TBL_BIT_EOL		0x80000000
-
-/*
- *  
- */
-
-typedef struct {
-	unsigned long reg_offset;
-        snd_pcm_substream_t *substream;
-	int running;
-        unsigned int size;
-        unsigned int fragsize;
-	unsigned int frags;
-	unsigned int page_per_frag;
-	unsigned int curidx;
-	unsigned int tbl_entries;	/* number of descriptor table entries */
-	unsigned int tbl_size;		/* size of a table entry */
-	u32 *table; /* physical address + flag */
-	dma_addr_t table_addr;
-} viadev_t;
-
-static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			   struct pci_dev *pci)
-{
-	int i, size;
-	struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
-
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-
-	/* allocate buffer descriptor lists */
-	if (dev->fragsize < PAGE_SIZE) {
-		dev->tbl_size = dev->fragsize;
-		dev->tbl_entries = dev->frags;
-		dev->page_per_frag = 1;
-	} else {
-		dev->tbl_size = PAGE_SIZE;
-		dev->tbl_entries = sgbuf->pages;
-		dev->page_per_frag = dev->fragsize >> PAGE_SHIFT;
-	}
-	/* the start of each lists must be aligned to 8 bytes,
-	 * but the kernel pages are much bigger, so we don't care
-	 */
-	dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr);
-	if (! dev->table)
-		return -ENOMEM;
-
-	if (dev->tbl_size < PAGE_SIZE) {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i);
-	} else {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr);
-	}
-	size = dev->size;
-	for (i = 0; i < dev->tbl_entries - 1; i++) {
-		dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size);
-		size -= dev->tbl_size;
-	}
-	dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size);
-
-	return 0;
-}
-
-
-static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			    struct pci_dev *pci)
-{
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-}
-
-
-/*
- */
-
-typedef struct _snd_via8233 via8233_t;
-#define chip_t via8233_t
-
-struct _snd_via8233 {
-	int irq;
-
-	unsigned long port;
-	struct resource *res_port;
-	unsigned char revision;
-
-	struct pci_dev *pci;
-	snd_card_t *card;
-
-	snd_pcm_t *pcm;
-	viadev_t playback;
-	viadev_t capture;
-
-	ac97_t *ac97;
-	unsigned int ac97_clock;
-
-	spinlock_t reg_lock;
-	snd_info_entry_t *proc_entry;
-};
-
-static struct pci_device_id snd_via8233_ids[] __devinitdata = {
-	{ 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* VT8233 */
-	{ 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, snd_via8233_ids);
-
-/*
- *  Basic I/O
- */
-
-static inline unsigned int snd_via8233_codec_xread(via8233_t *chip)
-{
-	/* this acces should be atomic */
-	return inl(VIAREG(chip, AC97));
-}
- 
-static inline void snd_via8233_codec_xwrite(via8233_t *chip, unsigned int val)
-{
-	/* this acces should be atomic */
-	outl(val, VIAREG(chip, AC97));
-}
- 
-static int snd_via8233_codec_ready(via8233_t *chip, int secondary)
-{
-	int time;
-
-	time = 1000;
-	do {
-		udelay(1);
-		if ((snd_via8233_codec_xread(chip) & VIA_REG_AC97_BUSY) == 0)
-			return 0;
-	} while (time--);
-	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via8233_codec_xread(chip));
-	return -EIO;
-}
- 
-static void snd_via8233_codec_write(ac97_t *ac97,
-				    unsigned short reg,
-				    unsigned short val)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
-	unsigned int xval;
-	
-	xval = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT;
-	xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
-	xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
-	xval |= val << VIA_REG_AC97_DATA_SHIFT;
-	spin_lock(&chip->reg_lock);
-	snd_via8233_codec_ready(chip, ac97->num);
-	snd_via8233_codec_xwrite(chip, xval);
-	snd_via8233_codec_ready(chip, ac97->num);
-	spin_unlock(&chip->reg_lock);
-}
-
-static unsigned short snd_via8233_codec_read(ac97_t *ac97, unsigned short reg)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return ~0);
-	unsigned int val;
-	int valid = ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
-	int i;
-	
-	val = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT;
-	val |= valid;
-	val |= VIA_REG_AC97_READ;
-	val |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
-	spin_lock(&chip->reg_lock);
-	snd_via8233_codec_ready(chip, ac97->num);
-	snd_via8233_codec_xwrite(chip, val);
-	snd_via8233_codec_ready(chip, ac97->num);
-	for (i=1000; i--;) {
-		val = snd_via8233_codec_xread(chip);
-		if (val & valid) {
-			spin_unlock(&chip->reg_lock);
-			return (unsigned short)val;
-		}
-	}
-	spin_unlock(&chip->reg_lock);
-	snd_printk("codec_read: codec %i is not valid [0x%x]\n", ac97->num, val);
-	/* have to return some value, this is better then 0 */
-	return ~0;
-}
-
-#if 0
-static void snd_via8233_channel_print(via8233_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n",
-			port,
-			inb(port + VIA_REG_OFFSET_STATUS),
-			inb(port + VIA_REG_OFFSET_CONTROL),
-			inl(port + VIA_REG_OFFSET_STOP_IDX),
-			inl(port + VIA_REG_OFFSET_CURR_PTR),
-			inl(port + VIA_REG_OFFSET_CURR_COUNT));
-}
-#endif
-
-static void snd_via8233_channel_reset(via8233_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	outb(VIA_REG_CTRL_TERMINATE, port + VIA_REG_OFFSET_CONTROL);
-	udelay(50);
-	/* disable interrupts */
-	outb(0, port + VIA_REG_OFFSET_CONTROL);
-	/* clear interrupts */
-	outb(0x3, port + VIA_REG_OFFSET_STATUS);
-}
-
-static int snd_via8233_trigger(via8233_t *chip, viadev_t *viadev, int cmd)
-{
-	unsigned char val;
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		val = VIA_REG_CTRL_INT | VIA_REG_CTRL_START;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-		val = VIA_REG_CTRL_TERMINATE;
-		viadev->running = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		val = VIA_REG_CTRL_INT | VIA_REG_CTRL_PAUSE;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		val = VIA_REG_CTRL_INT;
-		viadev->running = 0;
-		break;
-	default:
-		return -EINVAL;
-	}
-	outb(val, port + VIA_REG_OFFSET_CONTROL);
-	if (cmd == SNDRV_PCM_TRIGGER_STOP)
-		snd_via8233_channel_reset(chip, viadev);
-	return 0;
-}
-
-static int snd_via8233_setup_periods(via8233_t *chip, viadev_t *viadev,
-				     snd_pcm_substream_t *substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	unsigned long port = chip->port + viadev->reg_offset;
-	int v, err;
-
-	viadev->size = snd_pcm_lib_buffer_bytes(substream);
-	viadev->fragsize = snd_pcm_lib_period_bytes(substream);
-	viadev->frags = runtime->periods;
-	viadev->curidx = 0;
-
-	/* the period size must be in power of 2 */
-	v = ld2(viadev->fragsize);
-	if (viadev->fragsize != (1 << v)) {
-		snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize);
-		return -EINVAL;
-	}
-
-	snd_via8233_channel_reset(chip, viadev);
-
-	err = build_via_table(viadev, substream, chip->pci);
-	if (err < 0)
-		return err;
-
-	runtime->dma_bytes = viadev->size;
-	outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
-	if (viadev->reg_offset == VIA_REG_MULTPLAY_STATUS) {
-		unsigned int slots;
-		int fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT;
-		fmt |= runtime->channels << 4;
-		outb(fmt, chip->port + VIA_REG_MULTPLAY_FORMAT);
-		/* set sample number to slot 3, 4, 7, 8, 6, 9 */
-		switch (runtime->channels) {
-		case 1: slots = (1<<0) | (1<<4); break;
-		case 2: slots = (1<<0) | (2<<4); break;
-		case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break;
-		case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break;
-		default: slots = 0; break;
-		}
-		/* STOP index is never reached */
-		outl(0xff000000 | slots, chip->port + VIA_REG_MULTPLAY_STOP_IDX);
-	} else {
-		outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
-		     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
-		     0xff000000,    /* STOP index is never reached */
-		     port + VIA_REG_OFFSET_STOP_IDX);
-	}
-	return 0;
-}
-
-/*
- *  Interrupt handler
- */
-
-static inline void snd_via8233_update(via8233_t *chip, viadev_t *viadev)
-{
-	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
-	if (viadev->substream && viadev->running) {
-		viadev->curidx++;
-		if (viadev->curidx >= viadev->page_per_frag) {
-			viadev->curidx = 0;
-			spin_unlock(&chip->reg_lock);
-			snd_pcm_period_elapsed(viadev->substream);
-			spin_lock(&chip->reg_lock);
-		}
-	}
-}
-
-static void snd_via8233_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, dev_id, return);
-
-	spin_lock(&chip->reg_lock);
-	if (inb(chip->port + chip->playback.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via8233_update(chip, &chip->playback);
-	if (inb(chip->port + chip->capture.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via8233_update(chip, &chip->capture);
-	spin_unlock(&chip->reg_lock);
-}
-
-/*
- *  PCM part
- */
-
-static int snd_via8233_playback_trigger(snd_pcm_substream_t * substream,
-					int cmd)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-
-	return snd_via8233_trigger(chip, &chip->playback, cmd);
-}
-
-static int snd_via8233_capture_trigger(snd_pcm_substream_t * substream,
-				       int cmd)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	return snd_via8233_trigger(chip, &chip->capture, cmd);
-}
-
-static int snd_via8233_hw_params(snd_pcm_substream_t * substream,
-					  snd_pcm_hw_params_t * hw_params)
-{
-	return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_via8233_hw_free(snd_pcm_substream_t * substream)
-{
-	return 0;
-}
-
-static int snd_via8233_playback_prepare(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-	if (chip->playback.reg_offset != VIA_REG_MULTPLAY_STATUS) {
-		unsigned int tmp;
-		/* I don't understand this stuff but its from the documentation and this way it works */
-		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L));
-		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R));
-		tmp = inl(VIAREG(chip, PLAYBACK_STOP_IDX)) & ~0xfffff;
-		outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_STOP_IDX));
-	}
-	return snd_via8233_setup_periods(chip, &chip->playback, substream);
-}
-
-static int snd_via8233_capture_prepare(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-	outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL));
-	outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO));
-	return snd_via8233_setup_periods(chip, &chip->capture, substream);
-}
-
-static inline unsigned int snd_via8233_cur_ptr(via8233_t *chip, viadev_t *viadev)
-{
-	unsigned int val, count;
-
-	count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff;
-	/* The via686a does not have this current index register,
-	 * this register makes life easier for us here. */
-	val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->tbl_entries;
-	if (val < viadev->tbl_entries - 1) {
-		val *= viadev->tbl_size;
-		val += viadev->tbl_size - count;
-	} else {
-		val *= viadev->tbl_size;
-		val += (viadev->size % viadev->tbl_size) + 1 - count;
-	}
-	// printk("pointer: ptr = 0x%x, count = 0x%x, val = 0x%x\n", ptr, count, val);
-	return val;
-}
-
-static snd_pcm_uframes_t snd_via8233_playback_pointer(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->playback));
-}
-
-static snd_pcm_uframes_t snd_via8233_capture_pointer(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->capture));
-}
-
-static snd_pcm_hardware_t snd_via8233_playback =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	.rates =		0,
-	.rate_min =		8000,
-	.rate_max =		48000,
-	.channels_min =		1,
-	.channels_max =		6,
-	.buffer_bytes_max =	128 * 1024,
-	.period_bytes_min =	32,
-	.period_bytes_max =	128 * 1024,
-	.periods_min =		2,
-	.periods_max =		128,
-	.fifo_size =		0,
-};
-
-static snd_pcm_hardware_t snd_via8233_capture =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID),
-	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	.rates =		0,
-	.rate_min =		8000,
-	.rate_max =		48000,
-	.channels_min =		1,
-	.channels_max =		2,
-	.buffer_bytes_max =	128 * 1024,
-	.period_bytes_min =	32,
-	.period_bytes_max =	128 * 1024,
-	.periods_min =		2,
-	.periods_max =		128,
-	.fifo_size =		0,
-};
-
-static unsigned int channels[] = {
-	1, 2, 4, 6
-};
-
-#define CHANNELS sizeof(channels) / sizeof(channels[0])
-
-static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
-	.count = CHANNELS,
-	.list = channels,
-	.mask = 0,
-};
-
-static int snd_via8233_playback_open(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->playback.substream = substream;
-	runtime->hw = snd_via8233_playback;
-	runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
-	return 0;
-}
-
-static int snd_via8233_capture_open(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->capture.substream = substream;
-	runtime->hw = snd_via8233_capture;
-	runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-	return 0;
-}
-
-static int snd_via8233_playback_close(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-
-	snd_via8233_channel_reset(chip, &chip->playback);
-	clean_via_table(&chip->playback, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->playback.substream = NULL;
-	return 0;
-}
-
-static int snd_via8233_capture_close(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-
-	snd_via8233_channel_reset(chip, &chip->capture);
-	clean_via_table(&chip->capture, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->capture.substream = NULL;
-	return 0;
-}
-
-static snd_pcm_ops_t snd_via8233_playback_ops = {
-	.open =		snd_via8233_playback_open,
-	.close =	snd_via8233_playback_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_via8233_hw_params,
-	.hw_free =	snd_via8233_hw_free,
-	.prepare =	snd_via8233_playback_prepare,
-	.trigger =	snd_via8233_playback_trigger,
-	.pointer =	snd_via8233_playback_pointer,
-	.copy =		snd_pcm_sgbuf_ops_copy_playback,
-	.silence =	snd_pcm_sgbuf_ops_silence,
-	.page =		snd_pcm_sgbuf_ops_page,
-};
-
-static snd_pcm_ops_t snd_via8233_capture_ops = {
-	.open =		snd_via8233_capture_open,
-	.close =	snd_via8233_capture_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_via8233_hw_params,
-	.hw_free =	snd_via8233_hw_free,
-	.prepare =	snd_via8233_capture_prepare,
-	.trigger =	snd_via8233_capture_trigger,
-	.pointer =	snd_via8233_capture_pointer,
-	.copy =		snd_pcm_sgbuf_ops_copy_capture,
-	.silence =	snd_pcm_sgbuf_ops_silence,
-	.page =		snd_pcm_sgbuf_ops_page,
-};
-
-static void snd_via8233_pcm_free(snd_pcm_t *pcm)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, pcm->private_data, return);
-	chip->pcm = NULL;
-}
-
-static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** rpcm)
-{
-	snd_pcm_t *pcm;
-	int err;
-
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(chip->card, "VIA 8233", device, 1, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_via8233_pcm_free;
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "VIA 8233");
-	chip->pcm = pcm;
-
-	if (rpcm)
-		*rpcm = NULL;
-	return 0;
-}
-
-/*
- *  Mixer part
- */
-
-static void snd_via8233_mixer_free_ac97(ac97_t *ac97)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
-	chip->ac97 = NULL;
-}
-
-static int __devinit snd_via8233_mixer(via8233_t *chip)
-{
-	ac97_t ac97;
-	int err;
-
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.write = snd_via8233_codec_write;
-	ac97.read = snd_via8233_codec_read;
-	ac97.private_data = chip;
-	ac97.private_free = snd_via8233_mixer_free_ac97;
-	ac97.clock = chip->ac97_clock;
-	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
-		return err;
-	return 0;
-}
-
-/*
- *
- */
-
-static int __devinit snd_via8233_chip_init(via8233_t *chip)
-{
-	ac97_t ac97;
-	unsigned char stat;
-	int i;
-	
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.private_data = chip;
-
-	/* deassert ACLink reset */
-	pci_write_config_byte(chip->pci, 0x41, 0x40);
-	udelay(100);
-	/* deassert ACLink reset, force SYNC (warm AC'97 reset) */
-	pci_write_config_byte(chip->pci, 0x41, 0x60);
-	udelay(2);
-	/* ACLink on, deassert ACLink reset, VSR, SGD data out */
-	pci_write_config_byte(chip->pci, 0x41, 0xcc);
-
-	/* Wait for codec ready to be accessed. */
-	for (i=HZ; i--; ) {
-		pci_read_config_byte(chip->pci, 0x40, &stat);
-		if (stat & 1)
-			break;
-		if (!i) {
-			snd_printk("chip_init: failed to access primary codec.\n");
-			return ~0;
-		}
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	}
-	snd_via8233_codec_ready(chip, 0);
-	snd_via8233_codec_write(&ac97, AC97_RESET, 0x0000);
-	snd_via8233_codec_read(&ac97, 0);
-
-	/* disable interrupts */
-	snd_via8233_channel_reset(chip, &chip->playback);
-	snd_via8233_channel_reset(chip, &chip->capture);
-	return 0;
-}
-
-static int snd_via8233_free(via8233_t *chip)
-{
-	if (chip->irq < 0)
-		goto __end_hw;
-	/* disable interrupts */
-	snd_via8233_channel_reset(chip, &chip->playback);
-	snd_via8233_channel_reset(chip, &chip->capture);
-	/* --- */
-	synchronize_irq(chip->irq);
-      __end_hw:
-	if (chip->res_port) {
-		release_resource(chip->res_port);
-		kfree_nocheck(chip->res_port);
-	}
-	if (chip->irq >= 0)
-		free_irq(chip->irq, (void *)chip);
-	snd_magic_kfree(chip);
-	return 0;
-}
-
-static int snd_via8233_dev_free(snd_device_t *device)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, device->device_data, return -ENXIO);
-	return snd_via8233_free(chip);
-}
-
-static int __devinit snd_via8233_create(snd_card_t * card,
-				     struct pci_dev *pci,
-				     unsigned int ac97_clock,
-				     via8233_t ** r_via)
-{
-	via8233_t *chip;
-	int err;
-        static snd_device_ops_t ops = {
-		.dev_free =	snd_via8233_dev_free,
-        };
-
-	if ((err = pci_enable_device(pci)) < 0)
-		return err;
-
-	if ((chip = snd_magic_kcalloc(via8233_t, 0, GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	spin_lock_init(&chip->reg_lock);
-	chip->card = card;
-	chip->pci = pci;
-	chip->irq = -1;
-	chip->port = pci_resource_start(pci, 0);
-	if ((chip->res_port = request_region(chip->port, 256, "VIA8233")) == NULL) {
-		snd_via8233_free(chip);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
-		return -EBUSY;
-	}
-	if (request_irq(pci->irq, snd_via8233_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA8233", (void *)chip)) {
-		snd_via8233_free(chip);
-		snd_printk("unable to grab IRQ %d\n", chip->irq);
-		return -EBUSY;
-	}
-	chip->irq = pci->irq;
-	if (ac97_clock >= 8000 && ac97_clock <= 48000)
-		chip->ac97_clock = ac97_clock;
-	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
-	synchronize_irq(chip->irq);
-
-	/* initialize offsets */
-#if 0
-	chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; /* this doesn't work on VIA8233A */
-#endif
-	/* we use multi-channel playback mode, since this mode is supported
-	 * by all VIA8233 models (and obviously suitable for our purpose).
-	 */
-	chip->playback.reg_offset = VIA_REG_MULTPLAY_STATUS;
-
-	chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
-
-	if ((err = snd_via8233_chip_init(chip)) < 0) {
-		snd_via8233_free(chip);
-		return err;
-	}
-
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_via8233_free(chip);
-		return err;
-	}
-
-	/* The 8233 ac97 controller does not implement the master bit
-	 * in the pci command register. IMHO this is a violation of the PCI spec.
-	 * We call pci_set_master here because it does not hurt. */
-	pci_set_master(pci);
-
-	*r_via = chip;
-	return 0;
-}
-
-static int __devinit snd_via8233_probe(struct pci_dev *pci,
-				       const struct pci_device_id *id)
-{
-	static int dev;
-	snd_card_t *card;
-	via8233_t *chip;
-	int pcm_dev = 0;
-	int err;
-
-	if (dev >= SNDRV_CARDS)
-		return -ENODEV;
-	if (!snd_enable[dev]) {
-		dev++;
-		return -ENOENT;
-	}
-
-	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
-	if (card == NULL)
-		return -ENOMEM;
-
-	if ((err = snd_via8233_create(card,
-				      pci,
-				      snd_ac97_clock[dev],
-				      &chip)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-
-	if (snd_via8233_mixer(chip) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	if (snd_via8233_pcm(chip, pcm_dev++, NULL) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	strcpy(card->driver, "VIA8233");
-	strcpy(card->shortname, "VIA 8233");
-	
-	sprintf(card->longname, "%s at 0x%lx, irq %d",
-		card->shortname, chip->port, chip->irq);
-
-	if ((err = snd_card_register(card)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	pci_set_drvdata(pci, card);
-	dev++;
-	return 0;
-}
-
-static void __devexit snd_via8233_remove(struct pci_dev *pci)
-{
-	snd_card_free(pci_get_drvdata(pci));
-	pci_set_drvdata(pci, NULL);
-}
-
-static struct pci_driver driver = {
-	.name = "VIA 8233",
-	.id_table = snd_via8233_ids,
-	.probe = snd_via8233_probe,
-	.remove = __devexit_p(snd_via8233_remove),
-};
-
-static int __init alsa_card_via8233_init(void)
-{
-	int err;
-
-	if ((err = pci_module_init(&driver)) < 0) {
-#ifdef MODULE
-		printk(KERN_ERR "VIA 8233 soundcard not found or device busy\n");
-#endif
-		return err;
-	}
-	return 0;
-}
-
-static void __exit alsa_card_via8233_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via8233_init)
-module_exit(alsa_card_via8233_exit)
-
-#ifndef MODULE
-
-/* format is: snd-via8233=snd_enable,snd_index,snd_id,snd_ac97_clock */
-
-static int __init alsa_card_via8233_setup(char *str)
-{
-	static unsigned __initdata nr_dev = 0;
-
-	if (nr_dev >= SNDRV_CARDS)
-		return 0;
-	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
-	       get_option(&str,&snd_index[nr_dev]) == 2 &&
-	       get_id(&str,&snd_id[nr_dev]) == 2 &&
-	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
-	nr_dev++;
-	return 1;
-}
-
-__setup("snd-via8233=", alsa_card_via8233_setup);
-
-#endif /* ifndef MODULE */
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
new file mode 100644
index 000000000000..70163c108eff
--- /dev/null
+++ b/sound/pci/via82xx.c
@@ -0,0 +1,1345 @@
+/*
+ *   ALSA driver for VIA VT82xx (South Bridge)
+ *
+ *   VT82C686A/B/C, VT8233A/C, VT8235
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *	                   Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
+ *                    2002 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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 <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_sgbuf.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA VT82xx audio");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/B/C}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for VIA 82xx bridge.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for VIA 82xx bridge.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82xx bridge.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
+MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
+
+
+/* pci ids */
+#ifndef PCI_DEVICE_ID_VIA_82C686_5
+#define PCI_DEVICE_ID_VIA_82C686_5	0x3058
+#endif
+#ifndef PCI_DEVICE_ID_VIA_8233_5
+#define PCI_DEVICE_ID_VIA_8233_5	0x3059
+#endif
+
+/* revision numbers for via8233 */
+#define VIA_REV_PRE_8233	0x10	/* not in market */
+#define VIA_REV_8233C		0x20	/* 2 rec, 4 pb, 1 multi-pb */
+#define VIA_REV_8233		0x30	/* 2 rec, 4 pb, 1 multi-pb, spdif */
+#define VIA_REV_8233A		0x40	/* 1 rec, 1 multi-pb, spdf */
+#define VIA_REV_8235		0x50	/* 2 rec, 4 pb, 1 multi-pb, spdif */
+
+/*
+ *  Direct registers
+ */
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+
+/* common offsets */
+#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
+#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
+#define   VIA_REG_STAT_PAUSED		0x40	/* RO */
+#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
+#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
+#define   VIA_REG_STAT_EOL		0x02	/* RWC */
+#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
+#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
+#define   VIA_REG_CTRL_START		0x80	/* WO */
+#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
+#define   VIA_REG_CTRL_AUTOSTART	0x20
+#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
+#define   VIA_REG_CTRL_INT_STOP		0x04		
+#define   VIA_REG_CTRL_INT_EOL		0x02
+#define   VIA_REG_CTRL_INT_FLAG		0x01
+#define   VIA_REG_CTRL_RESET		0x01	/* RW - probably reset? undocumented */
+#define   VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
+#define VIA_REG_OFFSET_TYPE		0x02	/* byte - channel type (686 only) */
+#define   VIA_REG_TYPE_AUTOSTART	0x80	/* RW - autostart at EOL */
+#define   VIA_REG_TYPE_16BIT		0x20	/* RW */
+#define   VIA_REG_TYPE_STEREO		0x10	/* RW */
+#define   VIA_REG_TYPE_INT_LLINE	0x00
+#define   VIA_REG_TYPE_INT_LSAMPLE	0x04
+#define   VIA_REG_TYPE_INT_LESSONE	0x08
+#define   VIA_REG_TYPE_INT_MASK		0x0c
+#define   VIA_REG_TYPE_INT_EOL		0x02
+#define   VIA_REG_TYPE_INT_FLAG		0x01
+#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
+#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
+#define VIA_REG_OFFSET_STOP_IDX		0x08	/* dword - stop index, channel type, sample rate */
+#define   VIA8233_REG_TYPE_16BIT	0x00200000	/* RW */
+#define   VIA8233_REG_TYPE_STEREO	0x00100000	/* RW */
+#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
+#define VIA_REG_OFFSET_CURR_INDEX	0x0f	/* byte - channel current index */
+
+#define DEFINE_VIA_REGSET(name,val) \
+enum {\
+	VIA_REG_##name##_STATUS		= (val),\
+	VIA_REG_##name##_CONTROL	= (val) + 0x01,\
+	VIA_REG_##name##_TYPE		= (val) + 0x02,\
+	VIA_REG_##name##_TABLE_PTR	= (val) + 0x04,\
+	VIA_REG_##name##_CURR_PTR	= (val) + 0x04,\
+	VIA_REG_##name##_STOP_IDX	= (val) + 0x08,\
+	VIA_REG_##name##_CURR_COUNT	= (val) + 0x0c,\
+}
+
+/* playback block */
+DEFINE_VIA_REGSET(PLAYBACK, 0x00);
+DEFINE_VIA_REGSET(CAPTURE, 0x10);
+DEFINE_VIA_REGSET(FM, 0x20);
+
+/* AC'97 */
+#define VIA_REG_AC97			0x80	/* dword */
+#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
+#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
+#define   VIA_REG_AC97_CODEC_ID_PRIMARY	0x00
+#define   VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
+#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
+#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
+#define   VIA_REG_AC97_BUSY		(1<<24)
+#define   VIA_REG_AC97_READ		(1<<23)
+#define   VIA_REG_AC97_CMD_SHIFT	16
+#define   VIA_REG_AC97_CMD_MASK		0x7e
+#define   VIA_REG_AC97_DATA_SHIFT	0
+#define   VIA_REG_AC97_DATA_MASK	0xffff
+#define VIA_REG_SGD_SHADOW		0x84	/* dword */
+
+/* multi-channel and capture registers for via8233 */
+DEFINE_VIA_REGSET(MULTPLAY, 0x20);
+DEFINE_VIA_REGSET(CAPTURE_8233, 0x10);
+
+/* via8233-specific registers */
+#define VIA_REG_PLAYBACK_VOLUME_L	0x02	/* byte */
+#define VIA_REG_PLAYBACK_VOLUME_R	0x03	/* byte */
+#define VIA_REG_MULTPLAY_FORMAT		0x42	/* byte - format and channels */
+#define   VIA_REG_MULTPLAY_FMT_8BIT	0x00
+#define   VIA_REG_MULTPLAY_FMT_16BIT	0x80
+#define   VIA_REG_MULTPLAY_FMT_CH_MASK	0x70	/* # channels << 4 (valid = 1,2,4,6) */
+#define VIA_REG_CAPTURE_FIFO		0x62	/* byte - bit 6 = fifo  enable */
+#define   VIA_REG_CAPTURE_FIFO_ENABLE	0x40
+#define VIA_REG_CAPTURE_CHANNEL		0x63	/* byte - input select */
+#define   VIA_REG_CAPTURE_CHANNEL_MIC	0x4
+#define   VIA_REG_CAPTURE_CHANNEL_LINE	0
+#define   VIA_REG_CAPTURE_SELECT_CODEC	0x03	/* recording source codec (0 = primary) */
+
+#define VIA_TBL_BIT_FLAG	0x40000000
+#define VIA_TBL_BIT_EOL		0x80000000
+
+/*
+ * pcm stream
+ */
+
+typedef struct {
+	unsigned long reg_offset;
+        snd_pcm_substream_t *substream;
+	int running;
+        unsigned int size;
+        unsigned int fragsize;
+	unsigned int frags;
+	unsigned int lastptr;
+	unsigned int lastcount;
+	unsigned int page_per_frag;
+	unsigned int curidx;
+	unsigned int tbl_entries;	/* number of descriptor table entries */
+	unsigned int tbl_size;		/* size of a table entry */
+	u32 *table; /* physical address + flag */
+	dma_addr_t table_addr;
+} viadev_t;
+
+
+static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+			   struct pci_dev *pci)
+{
+	int i, size;
+	struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
+
+	if (dev->table) {
+		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
+		dev->table = NULL;
+	}
+
+	/* allocate buffer descriptor lists */
+	if (dev->fragsize < PAGE_SIZE) {
+		dev->tbl_size = dev->fragsize;
+		dev->tbl_entries = dev->frags;
+		dev->page_per_frag = 1;
+	} else {
+		dev->tbl_size = PAGE_SIZE;
+		dev->tbl_entries = sgbuf->pages;
+		dev->page_per_frag = dev->fragsize >> PAGE_SHIFT;
+	}
+	/* the start of each lists must be aligned to 8 bytes,
+	 * but the kernel pages are much bigger, so we don't care
+	 */
+	dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr);
+	if (! dev->table)
+		return -ENOMEM;
+
+	if (dev->tbl_size < PAGE_SIZE) {
+		for (i = 0; i < dev->tbl_entries; i++)
+			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i);
+	} else {
+		for (i = 0; i < dev->tbl_entries; i++)
+			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr);
+	}
+	size = dev->size;
+	for (i = 0; i < dev->tbl_entries - 1; i++) {
+		dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size);
+		size -= dev->tbl_size;
+	}
+	dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size);
+
+	return 0;
+}
+
+
+static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+			    struct pci_dev *pci)
+{
+	if (dev->table) {
+		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
+		dev->table = NULL;
+	}
+}
+
+
+/*
+ */
+
+enum { TYPE_VIA686, TYPE_VIA8233 };
+
+typedef struct _snd_via82xx via82xx_t;
+#define chip_t via82xx_t
+
+struct _snd_via82xx {
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	int chip_type;
+	unsigned char revision;
+
+	unsigned char old_legacy;
+	unsigned char old_legacy_cfg;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+
+	snd_pcm_t *pcm;
+	viadev_t playback;
+	viadev_t capture;
+
+	snd_rawmidi_t *rmidi;
+
+	ac97_t *ac97;
+	unsigned int ac97_clock;
+	unsigned int ac97_secondary;	/* secondary AC'97 codec is present */
+
+	spinlock_t reg_lock;
+	spinlock_t ac97_lock;
+	snd_info_entry_t *proc_entry;
+};
+
+static struct pci_device_id snd_via82xx_ids[] __devinitdata = {
+	{ 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* 686A */
+	{ 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* VT8233 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via82xx_ids);
+
+/*
+ *  Basic I/O
+ */
+
+static inline unsigned int snd_via82xx_codec_xread(via82xx_t *chip)
+{
+	return inl(VIAREG(chip, AC97));
+}
+ 
+static inline void snd_via82xx_codec_xwrite(via82xx_t *chip, unsigned int val)
+{
+	outl(val, VIAREG(chip, AC97));
+}
+ 
+static int snd_via82xx_codec_ready(via82xx_t *chip, int secondary)
+{
+	unsigned int timeout = 1000;	/* 1ms */
+	unsigned int val;
+	
+	while (timeout-- > 0) {
+		udelay(1);
+		if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+			return val & 0xffff;
+	}
+	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip));
+	return -EIO;
+}
+ 
+static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary)
+{
+	unsigned int timeout = 1000;	/* 1ms */
+	unsigned int val;
+	unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
+					 VIA_REG_AC97_SECONDARY_VALID;
+	
+	while (timeout-- > 0) {
+		udelay(1);
+		if ((val = snd_via82xx_codec_xread(chip)) & stat)
+			return val & 0xffff;
+	}
+	snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via82xx_codec_xread(chip));
+	return -EIO;
+}
+ 
+static void snd_via82xx_codec_wait(ac97_t *ac97)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return);
+	int err;
+	err = snd_via82xx_codec_ready(chip, ac97->num);
+	/* here we need to wait fairly for long time.. */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ/2);
+}
+
+static void snd_via82xx_codec_write(ac97_t *ac97,
+				    unsigned short reg,
+				    unsigned short val)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return);
+	unsigned int xval;
+	
+	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
+	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
+	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
+	xval |= val << VIA_REG_AC97_DATA_SHIFT;
+	spin_lock(&chip->ac97_lock);
+	snd_via82xx_codec_xwrite(chip, xval);
+	snd_via82xx_codec_ready(chip, ac97->num);
+	spin_unlock(&chip->ac97_lock);
+}
+
+static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return ~0);
+	unsigned int xval, val = 0xffff;
+	int again = 0;
+
+	xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT;
+	xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
+	xval |= VIA_REG_AC97_READ;
+	xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
+	spin_lock(&chip->ac97_lock);
+      	while (1) {
+      		if (again++ > 3) {
+		        spin_unlock(&chip->ac97_lock);
+		      	return 0xffff;
+		}
+		snd_via82xx_codec_xwrite(chip, xval);
+		if (snd_via82xx_codec_ready(chip, ac97->num) < 0)
+			continue;
+		if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) {
+			udelay(25);
+			val = snd_via82xx_codec_xread(chip);
+			break;
+		}
+	}
+	spin_unlock(&chip->ac97_lock);
+	return val & 0xffff;
+}
+
+static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev)
+{
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL);
+	udelay(50);
+	/* disable interrupts */
+	outb(0x00, port + VIA_REG_OFFSET_CONTROL);
+	/* clear interrupts */
+	outb(0x03, port + VIA_REG_OFFSET_STATUS);
+	outb(0x00, port + VIA_REG_OFFSET_TYPE); /* for via686 */
+	outl(0, port + VIA_REG_OFFSET_CURR_PTR);
+}
+
+static int snd_via82xx_trigger(via82xx_t *chip, viadev_t *viadev, int cmd)
+{
+	unsigned char val;
+	unsigned long port = chip->port + viadev->reg_offset;
+	
+	if (chip->chip_type == TYPE_VIA8233)
+		val = VIA_REG_CTRL_INT;
+	else
+		val = 0;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val |= VIA_REG_CTRL_START;
+		viadev->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = VIA_REG_CTRL_TERMINATE;
+		viadev->running = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		val |= VIA_REG_CTRL_PAUSE;
+		viadev->running = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		viadev->running = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	outb(val, port + VIA_REG_OFFSET_CONTROL);
+	if (cmd == SNDRV_PCM_TRIGGER_STOP)
+		snd_via82xx_channel_reset(chip, viadev);
+	return 0;
+}
+
+
+static int snd_via82xx_setup_periods(via82xx_t *chip, viadev_t *viadev,
+				     snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long port = chip->port + viadev->reg_offset;
+	int v, err;
+
+	viadev->size = snd_pcm_lib_buffer_bytes(substream);
+	viadev->fragsize = snd_pcm_lib_period_bytes(substream);
+	viadev->frags = runtime->periods;
+	viadev->lastptr = ~0;
+	viadev->lastcount = ~0;
+	viadev->curidx = 0;
+
+	/* the period size must be in power of 2 */
+	v = ld2(viadev->fragsize);
+	if (viadev->fragsize != (1 << v)) {
+		snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize);
+		return -EINVAL;
+	}
+
+	snd_via82xx_channel_reset(chip, viadev);
+
+	err = build_via_table(viadev, substream, chip->pci);
+	if (err < 0)
+		return err;
+
+	runtime->dma_bytes = viadev->size;
+	outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
+	switch (chip->chip_type) {
+	case TYPE_VIA686:
+		outb(VIA_REG_TYPE_AUTOSTART |
+		     (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
+		     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
+		     ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) |
+		     VIA_REG_TYPE_INT_EOL |
+		     VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE);
+		break;
+	case TYPE_VIA8233:
+		if (viadev->reg_offset == VIA_REG_MULTPLAY_STATUS) {
+			unsigned int slots;
+			int fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT;
+			fmt |= runtime->channels << 4;
+			outb(fmt, port + VIA_REG_OFFSET_TYPE);
+			/* set sample number to slot 3, 4, 7, 8, 6, 9 */
+			switch (runtime->channels) {
+			case 1: slots = (1<<0) | (1<<4); break;
+			case 2: slots = (1<<0) | (2<<4); break;
+			case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break;
+			case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break;
+			default: slots = 0; break;
+			}
+			/* STOP index is never reached */
+			outl(0xff000000 | slots, port + VIA_REG_OFFSET_STOP_IDX);
+		} else {
+			outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) |
+			     (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) |
+			     0xff000000,    /* STOP index is never reached */
+			     port + VIA_REG_OFFSET_STOP_IDX);
+		}
+		break;
+	}
+	return 0;
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static inline void snd_via82xx_update(via82xx_t *chip, viadev_t *viadev)
+{
+	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
+	if (viadev->substream && viadev->running) {
+		viadev->curidx++;
+		if (viadev->curidx >= viadev->page_per_frag) {
+			viadev->curidx = 0;
+			spin_unlock(&chip->reg_lock);
+			snd_pcm_period_elapsed(viadev->substream);
+			spin_lock(&chip->reg_lock);
+		}
+	}
+}
+
+static void snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, dev_id, return);
+	unsigned int status;
+
+	spin_lock(&chip->reg_lock);
+	if (chip->chip_type == TYPE_VIA686) {
+		/* check mpu401 interrupt */
+		status = inl(VIAREG(chip, SGD_SHADOW));
+		if ((status & 0x00000077) == 0) {
+			spin_unlock(&chip->reg_lock);
+			if (chip->rmidi != NULL)
+				snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+			return;
+		}
+	}
+	/* check status for each stream */
+	if (inb(chip->port + chip->playback.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via82xx_update(chip, &chip->playback);
+	if (inb(chip->port + chip->capture.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via82xx_update(chip, &chip->capture);
+	spin_unlock(&chip->reg_lock);
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_via82xx_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via82xx_trigger(chip, &chip->playback, cmd);
+}
+
+static int snd_via82xx_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via82xx_trigger(chip, &chip->capture, cmd);
+}
+
+static int snd_via82xx_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_via82xx_hw_free(snd_pcm_substream_t * substream)
+{
+	return 0;
+}
+
+static int snd_via82xx_playback_prepare(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	if (chip->chip_type == TYPE_VIA8233 &&
+	    chip->playback.reg_offset != VIA_REG_MULTPLAY_STATUS) {
+		unsigned int tmp;
+		/* I don't understand this stuff but its from the documentation and this way it works */
+		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L));
+		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R));
+		tmp = inl(VIAREG(chip, PLAYBACK_STOP_IDX)) & ~0xfffff;
+		outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_STOP_IDX));
+	}
+	return snd_via82xx_setup_periods(chip, &chip->playback, substream);
+}
+
+static int snd_via82xx_capture_prepare(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	if (chip->chip_type == TYPE_VIA8233) {
+		outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL));
+		outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO));
+	}
+	return snd_via82xx_setup_periods(chip, &chip->capture, substream);
+}
+
+static inline unsigned int snd_via82xx_cur_ptr(via82xx_t *chip, viadev_t *viadev)
+{
+	unsigned int val, ptr, count;
+	
+	ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset)/* & 0xffffff*/;
+	switch (chip->chip_type) {
+	case TYPE_VIA686:
+		count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset);
+		if (ptr == viadev->lastptr && count > viadev->lastcount)
+			ptr += 8;
+		if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE))
+			return 0;
+		snd_assert(viadev->tbl_entries, return 0);
+		/* get index */
+		if (ptr <= (unsigned int)viadev->table_addr)
+			val = 0;
+		else
+			val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries;
+		viadev->lastptr = ptr;
+		viadev->lastcount = count;
+		break;
+
+	case TYPE_VIA8233:
+	default:
+		count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff;
+		/* The via686a does not have this current index register,
+		 * this register makes life easier for us here. */
+		val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->tbl_entries;
+		break;
+	}
+
+	/* convert to the linear position */
+	if (val < viadev->tbl_entries - 1) {
+		val *= viadev->tbl_size;
+		val += viadev->tbl_size - count;
+	} else {
+		val *= viadev->tbl_size;
+		val += (viadev->size % viadev->tbl_size) + 1 - count;
+	}
+	// printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, count, val);
+	return val;
+}
+
+static snd_pcm_uframes_t snd_via82xx_playback_pointer(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->playback));
+}
+
+static snd_pcm_uframes_t snd_via82xx_capture_pointer(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->capture));
+}
+
+static snd_pcm_hardware_t snd_via82xx_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		0,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	32,
+	.period_bytes_max =	128 * 1024,
+	.periods_min =		2,
+	.periods_max =		128,
+	.fifo_size =		0,
+};
+
+static snd_pcm_hardware_t snd_via82xx_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		0,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	32,
+	.period_bytes_max =	128 * 1024,
+	.periods_min =		2,
+	.periods_max =		128,
+	.fifo_size =		0,
+};
+
+static unsigned int channels[] = {
+	1, 2, 4, 6
+};
+
+#define CHANNELS sizeof(channels) / sizeof(channels[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
+	.count = CHANNELS,
+	.list = channels,
+	.mask = 0,
+};
+
+static int snd_via82xx_playback_open(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->playback.substream = substream;
+	runtime->hw = snd_via82xx_playback;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
+		return err;
+	/* we may remove following constaint when we modify table entries
+	   in interrupt */
+#if 0
+	/* applying the following constraint together with the power-of-2 rule
+	 * above may result in too narrow space.
+	 * this one is not strictly necessary, so let's disable it.
+	 */
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+#endif
+	if (chip->chip_type == TYPE_VIA8233) {
+		runtime->hw.channels_max = 6;
+		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
+	}
+	return 0;
+}
+
+static int snd_via82xx_capture_open(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->capture.substream = substream;
+	runtime->hw = snd_via82xx_capture;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
+		return err;
+#if 0
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+#endif
+	return 0;
+}
+
+static int snd_via82xx_playback_close(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	clean_via_table(&chip->playback, substream, chip->pci);
+	snd_pcm_sgbuf_delete(substream);
+	chip->playback.substream = NULL;
+	return 0;
+}
+
+static int snd_via82xx_capture_close(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	clean_via_table(&chip->capture, substream, chip->pci);
+	snd_pcm_sgbuf_delete(substream);
+	chip->capture.substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_via82xx_playback_ops = {
+	.open =		snd_via82xx_playback_open,
+	.close =	snd_via82xx_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via82xx_playback_prepare,
+	.trigger =	snd_via82xx_playback_trigger,
+	.pointer =	snd_via82xx_playback_pointer,
+	.copy =		snd_pcm_sgbuf_ops_copy_playback,
+	.silence =	snd_pcm_sgbuf_ops_silence,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+static snd_pcm_ops_t snd_via82xx_capture_ops = {
+	.open =		snd_via82xx_capture_open,
+	.close =	snd_via82xx_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via82xx_capture_prepare,
+	.trigger =	snd_via82xx_capture_trigger,
+	.pointer =	snd_via82xx_capture_pointer,
+	.copy =		snd_pcm_sgbuf_ops_copy_capture,
+	.silence =	snd_pcm_sgbuf_ops_silence,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+static void snd_via82xx_pcm_free(snd_pcm_t *pcm)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, pcm->private_data, return);
+	chip->pcm = NULL;
+}
+
+static int __devinit snd_via82xx_pcm(via82xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, chip->card->shortname, device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via82xx_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via82xx_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_via82xx_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcm = pcm;
+
+	if (rpcm)
+		*rpcm = NULL;
+	return 0;
+}
+
+
+/*
+ *  Mixer part
+ */
+
+static void snd_via82xx_mixer_free_ac97(ac97_t *ac97)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return);
+	chip->ac97 = NULL;
+}
+
+static int __devinit snd_via82xx_mixer(via82xx_t *chip)
+{
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_via82xx_codec_write;
+	ac97.read = snd_via82xx_codec_read;
+	ac97.wait = snd_via82xx_codec_wait;
+	ac97.private_data = chip;
+	ac97.private_free = snd_via82xx_mixer_free_ac97;
+	ac97.clock = chip->ac97_clock;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ * joystick
+ */
+
+static int snd_via82xx_joystick_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_via82xx_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val;
+
+	pci_read_config_word(chip->pci, 0x42, &val);
+	ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0;
+	return 0;
+}
+
+static int snd_via82xx_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val, oval;
+
+	pci_read_config_word(chip->pci, 0x42, &oval);
+	val = oval & ~0x08;
+	if (ucontrol->value.integer.value[0])
+		val |= 0x08;
+	if (val != oval) {
+		pci_write_config_word(chip->pci, 0x42, val);
+		return 1;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_via82xx_joystick_control __devinitdata = {
+	.name = "Joystick",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_via82xx_joystick_info,
+	.get = snd_via82xx_joystick_get,
+	.put = snd_via82xx_joystick_put,
+};
+
+/*
+ *
+ */
+
+static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
+{
+	ac97_t ac97;
+	unsigned int val;
+	int max_count;
+	unsigned char pval;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+
+#if 0 /* broken on K7M? */
+	if (chip->chip_type == TYPE_VIA686)
+		/* disable all legacy ports */
+		pci_write_config_byte(chip->pci, 0x42, 0);
+#endif
+
+	/* deassert ACLink reset, force SYNC */
+	pci_write_config_byte(chip->pci, 0x41, 0xe0);
+	udelay(100);
+	/* deassert ACLink reset, force SYNC (warm AC'97 reset) */
+	pci_write_config_byte(chip->pci, 0x41, 0x60);
+	udelay(2);
+	/* pci_write_config_byte(chip->pci, 0x41, 0x00);
+	   udelay(100);
+	*/
+	/* ACLink on, deassert ACLink reset, VSR, SGD data out */
+	/* note - FM data out has trouble with non VRA codecs !! */
+	pci_write_config_byte(chip->pci, 0x41, 0xcc);
+	udelay(100);
+	
+	/* Make sure VRA is enabled, in case we didn't do a
+	 * complete codec reset, above */
+	pci_read_config_byte(chip->pci, 0x41, &pval);
+	if ((pval & 0xcc) != 0xcc) {
+		/* ACLink on, deassert ACLink reset, VSR, SGD data out */
+		/* note - FM data out has trouble with non VRA codecs !! */
+		pci_write_config_byte(chip->pci, 0x41, 0xcc);
+		udelay(100);
+	}
+
+	/* wait until codec ready */
+	max_count = ((3 * HZ) / 4) + 1;
+	do {
+		pci_read_config_byte(chip->pci, 0x40, &pval);
+		if (pval & 0x01) /* primary codec ready */
+			break;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (--max_count > 0);
+
+	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+		snd_printk("AC'97 codec is not ready [0x%x]\n", val);
+
+	/* and then reset codec.. */
+	snd_via82xx_codec_ready(chip, 0);
+	snd_via82xx_codec_write(&ac97, AC97_RESET, 0x0000);
+	snd_via82xx_codec_read(&ac97, 0);
+
+#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */
+	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+				 VIA_REG_AC97_SECONDARY_VALID |
+				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+	max_count = ((3 * HZ) / 4) + 1;
+	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+				 VIA_REG_AC97_SECONDARY_VALID |
+				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+	do {
+		if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+			chip->ac97_secondary = 1;
+			goto __ac97_ok2;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (--max_count > 0);
+	/* This is ok, the most of motherboards have only one codec */
+
+      __ac97_ok2:
+#endif
+
+	if (chip->chip_type == TYPE_VIA686) {
+		/* route FM trap to IRQ, disable FM trap */
+		pci_write_config_byte(chip->pci, 0x48, 0);
+		/* disable all GPI interrupts */
+		outl(0, chip->port + 0x8c);
+	}
+
+	/* disable interrupts */
+	snd_via82xx_channel_reset(chip, &chip->playback);
+	snd_via82xx_channel_reset(chip, &chip->capture);
+	return 0;
+}
+
+static int snd_via82xx_free(via82xx_t *chip)
+{
+	if (chip->irq < 0)
+		goto __end_hw;
+	/* disable interrupts */
+	snd_via82xx_channel_reset(chip, &chip->playback);
+	snd_via82xx_channel_reset(chip, &chip->capture);
+	/* --- */
+	synchronize_irq(chip->irq);
+      __end_hw:
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	if (chip->chip_type == TYPE_VIA686) {
+		pci_write_config_byte(chip->pci, 0x42, chip->old_legacy);
+		pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_via82xx_dev_free(snd_device_t *device)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, device->device_data, return -ENXIO);
+	return snd_via82xx_free(chip);
+}
+
+static int __devinit snd_via82xx_create(snd_card_t * card,
+					struct pci_dev *pci,
+					int chip_type,
+					unsigned int ac97_clock,
+					via82xx_t ** r_via)
+{
+	via82xx_t *chip;
+	int err;
+        static snd_device_ops_t ops = {
+		.dev_free =	snd_via82xx_dev_free,
+        };
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	if ((chip = snd_magic_kcalloc(via82xx_t, 0, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	chip->chip_type = chip_type;
+
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->ac97_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	pci_read_config_byte(pci, 0x42, &chip->old_legacy);
+	pci_read_config_byte(pci, 0x43, &chip->old_legacy_cfg);
+
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 256, card->driver)) == NULL) {
+		snd_via82xx_free(chip);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			card->driver, (void *)chip)) {
+		snd_via82xx_free(chip);
+		snd_printk("unable to grab IRQ %d\n", chip->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	if (ac97_clock >= 8000 && ac97_clock <= 48000)
+		chip->ac97_clock = ac97_clock;
+	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
+	synchronize_irq(chip->irq);
+
+	/* initialize offsets */
+	switch (chip->chip_type) {
+	case TYPE_VIA686:
+		chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS;
+		chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
+		break;
+	case TYPE_VIA8233:
+		/* we use multi-channel playback mode, since this mode is supported
+		 * by all VIA8233 models (and obviously suitable for our purpose).
+		 */
+		chip->playback.reg_offset = VIA_REG_MULTPLAY_STATUS;
+		chip->capture.reg_offset = VIA_REG_CAPTURE_8233_STATUS;
+		break;
+	}
+
+	if ((err = snd_via82xx_chip_init(chip)) < 0) {
+		snd_via82xx_free(chip);
+		return err;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_via82xx_free(chip);
+		return err;
+	}
+
+	/* The 8233 ac97 controller does not implement the master bit
+	 * in the pci command register. IMHO this is a violation of the PCI spec.
+	 * We call pci_set_master here because it does not hurt. */
+	pci_set_master(pci);
+
+	*r_via = chip;
+	return 0;
+}
+
+static int __devinit snd_via82xx_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev;
+	snd_card_t *card;
+	via82xx_t *chip;
+	int pcm_dev = 0;
+	int chip_type;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	chip_type = id->driver_data;
+	switch (chip_type) {
+	case TYPE_VIA686:
+		strcpy(card->driver, "VIA686A");
+		strcpy(card->shortname, "VIA 82C686A/B");
+		break;
+	case TYPE_VIA8233:
+		strcpy(card->driver, "VIA8233");
+		strcpy(card->shortname, "VIA 8233A/C");
+		break;
+	default:
+		snd_printk(KERN_ERR "invalid chip type %d\n", chip_type);
+		snd_card_free(card);
+		return -EINVAL;
+	}
+		
+	if ((err = snd_via82xx_create(card, pci, chip_type, snd_ac97_clock[dev], &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+
+	if (snd_via82xx_mixer(chip) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_via82xx_pcm(chip, pcm_dev++, NULL) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#if 0
+	if (snd_via82xx_pcm_fm(chip, pcm_dev++, NULL) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	if (chip->chip_type == TYPE_VIA686) {
+		unsigned char legacy, legacy_cfg;
+		int rev_h = 0;
+		legacy = chip->old_legacy;
+		legacy_cfg = chip->old_legacy_cfg;
+		legacy |= 0x40;		/* disable MIDI */
+		legacy &= ~0x08;	/* disable joystick */
+		if (chip->revision >= 0x20) {
+			if (check_region(pci_resource_start(pci, 2), 4)) {
+				rev_h = 0;
+				legacy &= ~0x80;	/* disable PCI I/O 2 */
+			} else {
+				rev_h = 1;
+				legacy |= 0x80;		/* enable PCI I/O 2 */
+			}
+		}
+		pci_write_config_byte(pci, 0x42, legacy);
+		pci_write_config_byte(pci, 0x43, legacy_cfg);
+		if (rev_h && snd_mpu_port[dev] >= 0x200) {	/* force MIDI */
+			legacy |= 0x02;	/* enable MPU */
+			pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01);
+		} else {
+			if (rev_h && (legacy & 0x02)) {
+				snd_mpu_port[dev] = pci_resource_start(pci, 2);
+				if (snd_mpu_port[dev] < 0x200)	/* bad value */
+					legacy &= ~0x02;	/* disable MIDI */
+			} else {
+				switch (snd_mpu_port[dev]) {	/* force MIDI */
+				case 0x300:
+				case 0x310:
+				case 0x320:
+				case 0x330:
+					legacy_cfg &= ~(3 << 2);
+					legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2;
+					legacy |= 0x02;
+					break;
+				default:			/* no, use BIOS settings */
+					if (legacy & 0x02)
+						snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2);
+				}
+			}
+		}
+		pci_write_config_byte(pci, 0x42, legacy);
+		pci_write_config_byte(pci, 0x43, legacy_cfg);
+		if (legacy & 0x02) {
+			if (check_region(snd_mpu_port[dev], 2)) {
+				printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]);
+				legacy &= ~0x02;
+				pci_write_config_byte(pci, 0x42, legacy);
+				goto __skip_mpu;
+			}
+			if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A,
+						snd_mpu_port[dev], 0,
+						pci->irq, 0,
+						&chip->rmidi) < 0) {
+				printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]);
+				legacy &= ~0x02;
+				pci_write_config_byte(pci, 0x42, legacy);
+				goto __skip_mpu;
+			}
+			legacy &= ~0x40;	/* enable MIDI interrupt */
+			pci_write_config_byte(pci, 0x42, legacy);
+		__skip_mpu:
+			;
+		}
+	
+		/* card switches */
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_via82xx_joystick_control, chip));
+		if (err < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname, chip->port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_via82xx_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "VIA 82xx Audio",
+	.id_table = snd_via82xx_ids,
+	.probe = snd_via82xx_probe,
+	.remove = __devexit_p(snd_via82xx_remove),
+};
+
+static int __init alsa_card_via82xx_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "VIA 82xx soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_via82xx_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via82xx_init)
+module_exit(alsa_card_via82xx_exit)
+
+#ifndef MODULE
+
+/* format is: snd-via82xx=snd_enable,snd_index,snd_id,
+			  snd_mpu_port,snd_ac97_clock */
+
+static int __init alsa_card_via82xx_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-via82xx=", alsa_card_via82xx_setup);
+
+#endif /* ifndef MODULE */
+ 
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
index 782191b51a7f..b9f3e96741ce 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -74,11 +74,14 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
 	new_client->id = keywest_ctx->id++; /* Automatically unique */
 	keywest_ctx->client = new_client;
 
-	if ((err = keywest_ctx->init_client(keywest_ctx)) < 0)
+	if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) {
+		snd_printk(KERN_ERR "tumbler: cannot initialize the MCS\n");
 		goto __err;
+	}
 
 	/* Tell the i2c layer a new client has arrived */
 	if (i2c_attach_client(new_client)) {
+		snd_printk(KERN_ERR "tumbler: cannot attach i2c client\n");
 		err = -ENODEV;
 		goto __err;
 	}
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index 2adf58920862..190984c2e287 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -1170,8 +1170,7 @@ static int __init snd_pmac_detect(pmac_t *chip)
 		// chip->can_byte_swap = 0; /* FIXME: check this */
 		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
 	}
-	if (device_is_compatible(sound, "tumbler") ||
-	    device_is_compatible(sound, "snapper")) {
+	if (device_is_compatible(sound, "tumbler")) {
 		chip->model = PMAC_TUMBLER;
 		chip->can_capture = 0;  /* no capture */
 		chip->can_duplex = 0;
@@ -1180,6 +1179,15 @@ static int __init snd_pmac_detect(pmac_t *chip)
 		chip->freq_table = tumbler_freqs;
 		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
 	}
+	if (device_is_compatible(sound, "snapper")) {
+		chip->model = PMAC_SNAPPER;
+		chip->can_capture = 0;  /* no capture */
+		chip->can_duplex = 0;
+		// chip->can_byte_swap = 0; /* FIXME: check this */
+		chip->num_freqs = 2;
+		chip->freq_table = tumbler_freqs;
+		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
+	}
 	prop = (unsigned int *)get_property(sound, "device-id", 0);
 	if (prop)
 		chip->device_id = *prop;
diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h
index a0960636a71c..bb812a979804 100644
--- a/sound/ppc/pmac.h
+++ b/sound/ppc/pmac.h
@@ -40,6 +40,7 @@
 #endif
 #endif
 #include <linux/nvram.h>
+#include <linux/tty.h>
 #include <linux/vt_kern.h>
 #include <asm/dbdma.h>
 #include <asm/prom.h>
@@ -115,7 +116,7 @@ struct snd_pmac_beep {
  */
 
 enum snd_pmac_model {
-	PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER
+	PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER, PMAC_SNAPPER
 };
 
 struct snd_pmac {
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index 46727219547a..6132cde87ece 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -94,8 +94,10 @@ static int __init snd_pmac_probe(void)
 			goto __error;
 		break;
 	case PMAC_TUMBLER:
-		strcpy(card->driver, "PMac Tumbler");
-		strcpy(card->shortname, "PowerMac Tumbler");
+	case PMAC_SNAPPER:
+		name_ext = chip->model == PMAC_TUMBLER ? "Tumbler" : "Snapper";
+		sprintf(card->driver, "PMac %s", name_ext);
+		sprintf(card->shortname, "PowerMac %s", name_ext);
 		sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
 			card->shortname, chip->device_id, chip->subframe);
 		if ((err = snd_pmac_tumbler_init(chip)) < 0)
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index bf54d8d894d5..43d8da2ec672 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1,5 +1,5 @@
 /*
- * PMac Tumbler lowlevel functions
+ * PMac Tumbler/Snapper lowlevel functions
  *
  * Copyright (c) by Takashi Iwai <tiwai@suse.de>
  *
@@ -46,17 +46,27 @@
 #define TAS_REG_VOL	0x04
 #define TAS_REG_TREBLE	0x05
 #define TAS_REG_BASS	0x06
-#define TAS_REG_INPUT1	0x07	/* pcm */
-#define TAS_REG_INPUT2	0x08	/* ??? */
+#define TAS_REG_INPUT1	0x07
+#define TAS_REG_INPUT2	0x08
 
+/* tas3001c */
 #define TAS_REG_PCM	TAS_REG_INPUT1
+ 
+/* tas3004 */
+#define TAS_REG_LMIX	TAS_REG_INPUT1
+#define TAS_REG_RMIX	TAS_REG_INPUT2
 
-#define TAS_MIXER_VOL_MAX	200
+/* mono volumes for tas3001c/tas3004 */
+enum {
+	VOL_IDX_PCM_MONO, /* tas3001c only */
+	VOL_IDX_BASS, VOL_IDX_TREBLE,
+	VOL_IDX_LAST_MONO
+};
 
+/* stereo volumes for tas3004 */
 enum {
-	VOL_IDX_PCM, VOL_IDX_BASS, VOL_IDX_TREBLE,
-	//VOL_IDX_ALTPCM,
-	VOL_IDX_LAST
+	VOL_IDX_PCM, VOL_IDX_PCM2, VOL_IDX_ADC,
+	VOL_IDX_LAST_MIX
 };
 
 typedef struct pmac_gpio {
@@ -77,7 +87,8 @@ typedef struct pmac_tumber_t {
 	int headphone_irq;
 	unsigned int master_vol[2];
 	unsigned int master_switch[2];
-	unsigned int mono_vol[VOL_IDX_LAST];
+	unsigned int mono_vol[VOL_IDX_LAST_MONO];
+	unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */
 	int drc_range;
 	int drc_enable;
 } pmac_tumbler_t;
@@ -90,9 +101,16 @@ typedef struct pmac_tumber_t {
 
 static int tumbler_init_client(pmac_keywest_t *i2c)
 {
-       /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */
-       return snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS,
-                                          (1<<6)+(2<<4)+(2<<2)+0);
+	int err, count = 10;
+	do {
+		/* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */
+		err =  snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS,
+						   (1<<6)+(2<<4)+(2<<2)+0);
+		if (err >= 0)
+			return err;
+		mdelay(10);
+	} while (count--);
+	return -ENXIO;
 }
 
 
@@ -245,8 +263,11 @@ static int tumbler_put_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu
 
 
 /*
- * dynamic range compression
+ * TAS3001c dynamic range compression
  */
+
+#define TAS3001_DRC_MAX		0x5f
+
 static int tumbler_set_drc(pmac_tumbler_t *mix)
 {
 	unsigned char val[2];
@@ -256,7 +277,7 @@ static int tumbler_set_drc(pmac_tumbler_t *mix)
   
 	if (mix->drc_enable) {
 		val[0] = 0xc1; /* enable, 3:1 compression */
-		if (mix->drc_range > 0x5f)
+		if (mix->drc_range > TAS3001_DRC_MAX)
 			val[1] = 0xf0;
 		else if (mix->drc_range < 0)
 			val[1] = 0x91;
@@ -274,12 +295,49 @@ static int tumbler_set_drc(pmac_tumbler_t *mix)
 	return 0;
 }
 
+/*
+ * TAS3004
+ */
+
+#define TAS3004_DRC_MAX		0xef
+
+static int snapper_set_drc(pmac_tumbler_t *mix)
+{
+	unsigned char val[6];
+
+	if (! mix->i2c.client)
+		return -ENODEV;
+  
+	if (mix->drc_enable)
+		val[0] = 0x50; /* 3:1 above threshold */
+	else
+		val[0] = 0x51; /* disabled */
+	val[1] = 0x02; /* 1:1 below threshold */
+	if (mix->drc_range > 0xef)
+		val[2] = 0xef;
+	else if (mix->drc_range < 0)
+		val[2] = 0x00;
+	else
+		val[2] = mix->drc_range;
+	val[3] = 0xb0;
+	val[4] = 0x60;
+	val[5] = 0xa0;
+
+	if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_DRC, 6, val) < 0) {
+		snd_printk("failed to set DRC\n");  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
 static int tumbler_info_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 0x5f;
+	uinfo->value.integer.max =
+		chip->model == PMAC_TUMBLER ? TAS3001_DRC_MAX : TAS3004_DRC_MAX;
 	return 0;
 }
 
@@ -304,7 +362,10 @@ static int tumbler_put_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 	change = mix->drc_range != ucontrol->value.integer.value[0];
 	if (change) {
 		mix->drc_range = ucontrol->value.integer.value[0];
-		tumbler_set_drc(mix);
+		if (chip->model == PMAC_TUMBLER)
+			tumbler_set_drc(mix);
+		else
+			snapper_set_drc(mix);
 	}
 	return change;
 }
@@ -330,7 +391,10 @@ static int tumbler_put_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 	change = mix->drc_enable != ucontrol->value.integer.value[0];
 	if (change) {
 		mix->drc_enable = !!ucontrol->value.integer.value[0];
-		tumbler_set_drc(mix);
+		if (chip->model == PMAC_TUMBLER)
+			tumbler_set_drc(mix);
+		else
+			snapper_set_drc(mix);
 	}
 	return change;
 }
@@ -409,24 +473,15 @@ static int tumbler_put_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon
 	return change;
 }
 
+/* TAS3001c mono volumes */
 static struct tumbler_mono_vol tumbler_pcm_vol_info = {
-	.index = VOL_IDX_PCM,
+	.index = VOL_IDX_PCM_MONO,
 	.reg = TAS_REG_PCM,
 	.bytes = 3,
 	.max = number_of(mixer_volume_table),
 	.table = mixer_volume_table,
 };
 
-#if 0 // for what?
-static struct tumbler_mono_vol tumbler_altpcm_vol_info = {
-	.index = VOL_IDX_ALTPCM,
-	.reg = TAS_REG_INPUT2,
-	.bytes = 3,
-	.max = number_of(mixer_volume_table),
-	.table = mixer_volume_table,
-};
-#endif
-
 static struct tumbler_mono_vol tumbler_bass_vol_info = {
 	.index = VOL_IDX_BASS,
 	.reg = TAS_REG_BASS,
@@ -443,6 +498,24 @@ static struct tumbler_mono_vol tumbler_treble_vol_info = {
 	.table = treble_volume_table,
 };
 
+/* TAS3004 mono volumes */
+static struct tumbler_mono_vol snapper_bass_vol_info = {
+	.index = VOL_IDX_BASS,
+	.reg = TAS_REG_BASS,
+	.bytes = 1,
+	.max = number_of(snapper_bass_volume_table),
+	.table = snapper_bass_volume_table,
+};
+
+static struct tumbler_mono_vol snapper_treble_vol_info = {
+	.index = VOL_IDX_TREBLE,
+	.reg = TAS_REG_TREBLE,
+	.bytes = 1,
+	.max = number_of(snapper_treble_volume_table),
+	.table = snapper_treble_volume_table,
+};
+
+
 #define DEFINE_MONO(xname,type) { \
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
 	.name = xname, \
@@ -452,6 +525,95 @@ static struct tumbler_mono_vol tumbler_treble_vol_info = {
 	.private_value = (unsigned long)(&tumbler_##type##_vol_info), \
 }
 
+#define DEFINE_SNAPPER_MONO(xname,type) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+	.name = xname, \
+	.info = tumbler_info_mono, \
+	.get = tumbler_get_mono, \
+	.put = tumbler_put_mono, \
+	.private_value = (unsigned long)(&snapper_##type##_vol_info), \
+}
+
+
+/*
+ * snapper mixer volumes
+ */
+
+static int snapper_set_mix_vol1(pmac_tumbler_t *mix, int idx, int ch, int reg)
+{
+	int i, j, vol;
+	unsigned char block[9];
+
+	vol = mix->mix_vol[idx][ch];
+	if (vol >= number_of(mixer_volume_table)) {
+		vol = number_of(mixer_volume_table) - 1;
+		mix->mix_vol[idx][ch] = vol;
+	}
+
+	for (i = 0; i < 3; i++) {
+		vol = mix->mix_vol[i][ch];
+		vol = mixer_volume_table[vol];
+		for (j = 0; j < 3; j++)
+			block[i * 3 + j] = (vol >> ((2 - j) * 8)) & 0xff;
+	}
+	if (snd_pmac_keywest_write(&mix->i2c, reg, 9, block) < 0) {
+		snd_printk("failed to set mono volume %d\n", reg);  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
+static int snapper_set_mix_vol(pmac_tumbler_t *mix, int idx)
+{
+	if (! mix->i2c.client)
+		return -ENODEV;
+	if (snapper_set_mix_vol1(mix, idx, 0, TAS_REG_LMIX) < 0 ||
+	    snapper_set_mix_vol1(mix, idx, 1, TAS_REG_RMIX) < 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int snapper_info_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = number_of(mixer_volume_table) - 1;
+	return 0;
+}
+
+static int snapper_get_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	int idx = (int)kcontrol->private_value;
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->mix_vol[idx][0];
+	ucontrol->value.integer.value[1] = mix->mix_vol[idx][1];
+	return 0;
+}
+
+static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	int idx = (int)kcontrol->private_value;
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] ||
+		mix->mix_vol[idx][1] != ucontrol->value.integer.value[1];
+	if (change) {
+		mix->mix_vol[idx][0] = ucontrol->value.integer.value[0];
+		mix->mix_vol[idx][1] = ucontrol->value.integer.value[1];
+		snapper_set_mix_vol(mix, idx);
+	}
+	return change;
+}
+
+
 /*
  * mute switches
  */
@@ -487,6 +649,16 @@ static int tumbler_put_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_
 	return 0;
 }
 
+#define DEFINE_SNAPPER_MIX(xname,idx,ofs) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+	.name = xname, \
+	.info = snapper_info_mix, \
+	.get = snapper_get_mix, \
+	.put = snapper_put_mix, \
+	.index = idx,\
+	.private_value = ofs, \
+}
+
 
 /*
  */
@@ -506,7 +678,38 @@ static snd_kcontrol_new_t tumbler_mixers[] __initdata = {
 	DEFINE_MONO("Tone Control - Bass", bass),
 	DEFINE_MONO("Tone Control - Treble", treble),
 	DEFINE_MONO("PCM Playback Volume", pcm),
-	//  DEFINE_MONO("Mixer2 Playback Volume", altpcm),
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "DRC Switch",
+	  .info = snd_pmac_boolean_mono_info,
+	  .get = tumbler_get_drc_switch,
+	  .put = tumbler_put_drc_switch
+	},
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "DRC Range",
+	  .info = tumbler_info_drc_value,
+	  .get = tumbler_get_drc_value,
+	  .put = tumbler_put_drc_value
+	},
+};
+
+static snd_kcontrol_new_t snapper_mixers[] __initdata = {
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "Master Playback Volume",
+	  .info = tumbler_info_master_volume,
+	  .get = tumbler_get_master_volume,
+	  .put = tumbler_put_master_volume
+	},
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "Master Playback Switch",
+	  .info = snd_pmac_boolean_stereo_info,
+	  .get = tumbler_get_master_switch,
+	  .put = tumbler_put_master_switch
+	},
+	DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM),
+	DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2),
+	DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC),
+	DEFINE_SNAPPER_MONO("Tone Control - Bass", bass),
+	DEFINE_SNAPPER_MONO("Tone Control - Treble", treble),
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "DRC Switch",
 	  .info = snd_pmac_boolean_mono_info,
@@ -675,10 +878,17 @@ static void tumbler_resume(pmac_t *chip)
 	tumbler_reset_audio(chip);
 	if (mix->i2c.client)
 		tumbler_init_client(&mix->i2c);
-	tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info);
-	tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
-	tumbler_set_mono_volume(mix, &tumbler_treble_vol_info);
-	// tumbler_set_mono_volume(mix, &tumbler_altpcm_vol_info);
+	if (chip->model == PMAC_TUMBLER) {
+		tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info);
+		tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
+		tumbler_set_mono_volume(mix, &tumbler_treble_vol_info);
+	} else {
+		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_drc(mix);
 	tumbler_set_master_volume(mix);
 	if (chip->update_automute)
@@ -741,6 +951,7 @@ int __init snd_pmac_tumbler_init(pmac_t *chip)
 	pmac_tumbler_t *mix;
 	u32 *paddr;
 	struct device_node *tas_node;
+	char *chipname;
 
 #ifdef CONFIG_KMOD
 	request_module("i2c-keywest");
@@ -770,18 +981,32 @@ int __init snd_pmac_tumbler_init(pmac_t *chip)
 		mix->i2c.addr = TAS_I2C_ADDR;
 
 	mix->i2c.init_client = tumbler_init_client;
-	mix->i2c.name = "TAS3001c";
+	if (chip->model == PMAC_TUMBLER) {
+		mix->i2c.name = "TAS3001c";
+		chipname = "Tumbler";
+	} else {
+		mix->i2c.name = "TAS3004";
+		chipname = "Snapper";
+	}
+
 	if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0)
 		return err;
 
 	/*
 	 * build mixers
 	 */
-	strcpy(chip->card->mixername, "PowerMac Tumbler");
+	sprintf(chip->card->mixername, "PowerMac %s", chipname);
 
-	for (i = 0; i < number_of(tumbler_mixers); i++) {
-		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0)
-			return err;
+	if (chip->model == PMAC_TUMBLER) {
+		for (i = 0; i < number_of(tumbler_mixers); i++) {
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0)
+				return err;
+		}
+	} else {
+		for (i = 0; i < number_of(snapper_mixers); i++) {
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip))) < 0)
+				return err;
+		}
 	}
 	chip->master_sw_ctl = snd_ctl_new1(&tumbler_hp_sw, chip);
 	if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
diff --git a/sound/ppc/tumbler_volume.h b/sound/ppc/tumbler_volume.h
index a299a9edc07d..ef8d85d58b02 100644
--- a/sound/ppc/tumbler_volume.h
+++ b/sound/ppc/tumbler_volume.h
@@ -63,7 +63,7 @@ static unsigned int master_volume_table[] = {
 	0x00071457, 0x00077fbb,	0x0007f17b,
 };
 
-/* treble table */
+/* treble table for TAS3001c */
 /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
 static unsigned int treble_volume_table[] = {
 	0x96, 0x95, 0x94,
@@ -93,7 +93,7 @@ static unsigned int treble_volume_table[] = {
 	0x01,
 };
 
-/* bass table */
+/* bass table for TAS3001c */
 /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
 static unsigned int bass_volume_table[] = {
 	0x86, 0x82, 0x7f,
@@ -186,3 +186,65 @@ static unsigned int mixer_volume_table[] = {
 	0x5f4e52, 0x64f403, 0x6aef5d,
 	0x714575, 0x77fbaa, 0x7f17af,
 };
+
+
+/* treble table for TAS3004 */
+/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
+static unsigned int snapper_treble_volume_table[] = {
+	0x96, 0x95, 0x94,
+	0x93, 0x92, 0x91,
+	0x90, 0x8f, 0x8e,
+	0x8d, 0x8c, 0x8b,
+	0x8a, 0x89, 0x88,
+	0x87, 0x86, 0x85,
+	0x84, 0x83, 0x82,
+	0x81, 0x80, 0x7f,
+	0x7e, 0x7d, 0x7c,
+	0x7b, 0x7a, 0x79,
+	0x78, 0x77, 0x76,
+	0x75, 0x74, 0x73,
+	0x72, 0x71, 0x70,
+	0x6f, 0x6d, 0x6c,
+	0x6b, 0x69, 0x68,
+	0x67, 0x65, 0x63,
+	0x62, 0x60, 0x5d,
+	0x5b, 0x59, 0x56,
+	0x53, 0x51, 0x4d,
+	0x4a, 0x47, 0x43,
+	0x3f, 0x3b, 0x36,
+	0x31, 0x2c, 0x26,
+	0x20, 0x1a, 0x13,
+	0x08, 0x04, 0x01,
+	0x01,
+};
+
+/* bass table for TAS3004 */
+/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
+static unsigned int snapper_bass_volume_table[] = {
+	0x96, 0x95, 0x94,
+	0x93, 0x92, 0x91,
+	0x90, 0x8f, 0x8e,
+	0x8d, 0x8c, 0x8b,
+	0x8a, 0x89, 0x88,
+	0x87, 0x86, 0x85,
+	0x84, 0x83, 0x82,
+	0x81, 0x80, 0x7f,
+	0x7e, 0x7d, 0x7c,
+	0x7b, 0x7a, 0x79,
+	0x78, 0x77, 0x76,
+	0x75, 0x74, 0x73,
+	0x72, 0x71, 0x6f,
+	0x6e, 0x6d, 0x6b,
+	0x6a, 0x69, 0x67,
+	0x66, 0x65, 0x63,
+	0x62, 0x61, 0x5f,
+	0x5d, 0x5b, 0x58,
+	0x55, 0x52, 0x4f,
+	0x4c, 0x49, 0x46,
+	0x43, 0x3f, 0x3b,
+	0x37, 0x33, 0x2e,
+	0x29, 0x24, 0x1e,
+	0x18, 0x11, 0x0a,
+	0x01,
+};
+
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index f71a2fd08a6d..d175637dafb8 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -53,6 +53,8 @@ MODULE_DEVICES("{{Generic,USB Audio}}");
 static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */
+static int snd_pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */
 
 MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
 MODULE_PARM_DESC(snd_index, "Index value for the USB audio adapter.");
@@ -63,6 +65,27 @@ MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
 MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
 MODULE_PARM_DESC(snd_enable, "Enable USB audio adapter.");
 MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_vid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_vid, "Vendor ID for the USB audio device.");
+MODULE_PARM_SYNTAX(snd_vid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
+MODULE_PARM(snd_pid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pid, "Product ID for the USB audio device.");
+MODULE_PARM_SYNTAX(snd_pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
+
+
+/*
+ * for using ASYNC unlink mode, define the following.
+ * this will make the driver quicker response for request to STOP-trigger,
+ * but it may cause oops by some unknown reason (bug of usb driver?),
+ * so turning off might be sure.
+ */
+/* #define SND_USE_ASYNC_UNLINK */
+
+#ifdef SND_USB_ASYNC_UNLINK
+#define UNLINK_FLAGS	USB_ASYNC_UNLINK
+#else
+#define UNLINK_FLAGS	0
+#endif
 
 
 /*
@@ -528,6 +551,10 @@ static int deactivate_urbs(snd_usb_substream_t *subs)
 
 	subs->running = 0;
 
+#ifndef SND_USB_ASYNC_UNLINK
+	if (in_interrupt())
+		return 0;
+#endif
 	alive = 0;
 	for (i = 0; i < subs->nurbs; i++) {
 		if (test_bit(i, &subs->active_mask)) {
@@ -545,7 +572,11 @@ static int deactivate_urbs(snd_usb_substream_t *subs)
 			}
 		}
 	}
+#ifdef SND_USB_ASYNC_UNLINK
 	return alive;
+#else
+	return 0;
+#endif
 }
 
 
@@ -803,7 +834,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *run
 		}
 		u->urb->dev = subs->dev;
 		u->urb->pipe = subs->datapipe;
-		u->urb->transfer_flags = USB_ISO_ASAP | USB_ASYNC_UNLINK;
+		u->urb->transfer_flags = USB_ISO_ASAP | UNLINK_FLAGS;
 		u->urb->number_of_packets = u->packets;
 		u->urb->context = u;
 		u->urb->complete = snd_complete_urb;
@@ -825,7 +856,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *run
 			u->urb->transfer_buffer_length = NRPACKS * 3;
 			u->urb->dev = subs->dev;
 			u->urb->pipe = subs->syncpipe;
-			u->urb->transfer_flags = USB_ISO_ASAP | USB_ASYNC_UNLINK;
+			u->urb->transfer_flags = USB_ISO_ASAP | UNLINK_FLAGS;
 			u->urb->number_of_packets = u->packets;
 			u->urb->context = u;
 			u->urb->complete = snd_complete_sync_urb;
@@ -2043,7 +2074,9 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
 		 * now look for an empty slot and create a new card instance
 		 */
 		for (i = 0; i < SNDRV_CARDS; i++)
-			if (snd_enable[i] && ! usb_chip[i]) {
+			if (snd_enable[i] && ! usb_chip[i] &&
+			    (snd_vid[i] == -1 || snd_vid[i] == dev->descriptor.idVendor) &&
+			    (snd_pid[i] == -1 || snd_pid[i] == dev->descriptor.idProduct)) {
 				card = snd_card_new(snd_index[i], snd_id[i], THIS_MODULE, 0);
 				if (card == NULL) {
 					snd_printk(KERN_ERR "cannot create a card instance %d\n", i);
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 96b0d5f4ca0b..d527455593da 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -73,6 +73,7 @@ struct usb_mixer_elem_info {
 	int channels;
 	int val_type;
 	int min, max;
+	unsigned int initialized: 1;
 };
 
 
@@ -498,6 +499,23 @@ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
 		uinfo->value.integer.min = 0;
 		uinfo->value.integer.max = 1;
 	} else {
+		if (! cval->initialized) {
+			int minchn = 0;
+			if (cval->cmask) {
+				int i;
+				for (i = 0; i < MAX_CHANNELS; i++)
+					if (cval->cmask & (1 << i)) {
+						minchn = i + 1;
+						break;
+					}
+			}
+			if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 ||
+			    get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) {
+				snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, cval->control);
+				return -EINVAL;
+			}
+			cval->initialized = 1;
+		}
 		uinfo->value.integer.min = 0;
 		uinfo->value.integer.max = cval->max - cval->min;
 	}
@@ -515,8 +533,10 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 		for (c = 0; c < MAX_CHANNELS; c++) {
 			if (cval->cmask & (1 << c)) {
 				err = get_cur_mix_value(cval, c + 1, &val);
-				if (err < 0)
+				if (err < 0) {
+					printk("cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err);
 					return err;
+				}
 				val = get_relative_value(cval, val);
 				ucontrol->value.integer.value[cnt] = val;
 				cnt++;
@@ -525,8 +545,10 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 	} else {
 		/* master channel */
 		err = get_cur_mix_value(cval, 0, &val);
-		if (err < 0)
+		if (err < 0) {
+			printk("cannot get current value for control %d master ch: err = %d\n", cval->control, err);
 			return err;
+		}
 		val = get_relative_value(cval, val);
 		ucontrol->value.integer.value[0] = val;
 	}
@@ -592,6 +614,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
 	int nameid = desc[desc[0] - 1];
 	snd_kcontrol_t *kctl;
 	usb_mixer_elem_info_t *cval;
+	int minchn = 0;
 
 	if (control == USB_FEATURE_GEQ) {
 		/* FIXME: not supported yet */
@@ -614,22 +637,25 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
 	else {
 		int i, c = 0;
 		for (i = 0; i < 16; i++)
-			if (ctl_mask & (1 << i))
+			if (ctl_mask & (1 << i)) {
+				if (! minchn)
+					minchn = i + 1;
 				c++;
+			}
 		cval->channels = c;
 	}
 
 	/* get min/max values */
 	if (cval->val_type == USB_MIXER_BOOLEAN ||
-	    cval->val_type == USB_MIXER_INV_BOOLEAN)
+	    cval->val_type == USB_MIXER_INV_BOOLEAN) {
 		cval->max = 1;
-	else {
-		if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | (ctl_mask ? 1 : 0), &cval->max) < 0 ||
-		    get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | (ctl_mask ? 1 : 0), &cval->min) < 0) {
+		cval->initialized = 1;
+	} else {
+		if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 ||
+		    get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0)
 			snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, control);
-			snd_magic_kfree(cval);
-			return;
-		}
+		else
+			cval->initialized = 1;
 	}
 
 	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
@@ -759,6 +785,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
 	int i, len;
 	snd_kcontrol_t *kctl;
 	usb_audio_term_t iterm;
+	int minchn = 0;
 
 	cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
 	if (! cval)
@@ -776,16 +803,17 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
 		if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) {
 			cval->cmask |= (1 << i);
 			cval->channels++;
+			if (! minchn)
+				minchn = i + 1;
 		}
 	}
 
 	/* get min/max values */
-	if (get_ctl_value(cval, GET_MAX, ((in_ch+1) << 8) | 1, &cval->max) < 0 ||
-	    get_ctl_value(cval, GET_MIN, ((in_ch+1) << 8) | 1, &cval->min) < 0) {
+	if (get_ctl_value(cval, GET_MAX, ((in_ch+1) << 8) | minchn, &cval->max) < 0 ||
+	    get_ctl_value(cval, GET_MIN, ((in_ch+1) << 8) | minchn, &cval->min) < 0)
 		snd_printk(KERN_ERR "cannot get min/max values for mixer\n");
-		snd_magic_kfree(cval);
-		return;
-	}
+	else
+		cval->initialized = 1;
 
 	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
 	if (! kctl) {
@@ -994,11 +1022,10 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
 
 		/* get min/max values */
 		if (get_ctl_value(cval, GET_MAX, cval->control, &cval->max) < 0 ||
-		    get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0) {
+		    get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0)
 			snd_printk(KERN_ERR "cannot get min/max values for proc/ext unit\n");
-			snd_magic_kfree(cval);
-			continue;
-		}
+		else
+			cval->initialized = 1;
 
 		kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
 		if (! kctl) {
@@ -1158,6 +1185,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
 	cval->channels = 1;
 	cval->min = 1;
 	cval->max = num_ins;
+	cval->initialized = 1;
 
 	namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL);
 	if (! namelist) {
-- 
2.30.9